1 /* Support for embedding graphical components in a buffer.
3 Copyright (C) 2011-2016 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
25 #include "blockinput.h"
30 #include <webkit2/webkit2.h>
31 #include <JavaScriptCore/JavaScript.h>
33 static struct xwidget
*
34 allocate_xwidget (void)
36 return ALLOCATE_PSEUDOVECTOR (struct xwidget
, height
, PVEC_XWIDGET
);
39 static struct xwidget_view
*
40 allocate_xwidget_view (void)
42 return ALLOCATE_PSEUDOVECTOR (struct xwidget_view
, redisplayed
,
46 #define XSETXWIDGET(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET)
47 #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET_VIEW)
49 static struct xwidget_view
*xwidget_view_lookup (struct xwidget
*,
51 static void webkit_view_load_changed_cb (WebKitWebView
*,
54 static void webkit_javascript_finished_cb (GObject
*,
57 static gboolean
webkit_download_cb (WebKitWebContext
*, WebKitDownload
*, gpointer
);
60 webkit_decide_policy_cb (WebKitWebView
*,
61 WebKitPolicyDecision
*,
62 WebKitPolicyDecisionType
,
66 DEFUN ("make-xwidget",
67 Fmake_xwidget
, Smake_xwidget
,
69 doc
: /* Make an xwidget of TYPE.
70 If BUFFER is nil, use the current buffer.
71 If BUFFER is a string and no such buffer exists, create it.
72 TYPE is a symbol which can take one of the following values:
76 Returns the newly constructed xwidget, or nil if construction fails. */)
78 Lisp_Object title
, Lisp_Object width
, Lisp_Object height
,
79 Lisp_Object arguments
, Lisp_Object buffer
)
83 CHECK_NATNUM (height
);
85 struct xwidget
*xw
= allocate_xwidget ();
89 xw
->buffer
= NILP (buffer
) ? Fcurrent_buffer () : Fget_buffer_create (buffer
);
90 xw
->height
= XFASTINT (height
);
91 xw
->width
= XFASTINT (width
);
92 xw
->kill_without_query
= false;
93 XSETXWIDGET (val
, xw
);
94 Vxwidget_list
= Fcons (val
, Vxwidget_list
);
95 xw
->widgetwindow_osr
= NULL
;
96 xw
->widget_osr
= NULL
;
99 if (EQ (xw
->type
, Qwebkit
))
102 xw
->widgetwindow_osr
= gtk_offscreen_window_new ();
103 gtk_window_resize (GTK_WINDOW (xw
->widgetwindow_osr
), xw
->width
,
106 if (EQ (xw
->type
, Qwebkit
))
108 xw
->widget_osr
= webkit_web_view_new ();
111 gtk_widget_set_size_request (GTK_WIDGET (xw
->widget_osr
), xw
->width
,
114 if (EQ (xw
->type
, Qwebkit
))
116 gtk_container_add (GTK_CONTAINER (xw
->widgetwindow_osr
),
117 GTK_WIDGET (WEBKIT_WEB_VIEW (xw
->widget_osr
)));
121 gtk_container_add (GTK_CONTAINER (xw
->widgetwindow_osr
),
125 gtk_widget_show (xw
->widget_osr
);
126 gtk_widget_show (xw
->widgetwindow_osr
);
128 /* Store some xwidget data in the gtk widgets for convenient
129 retrieval in the event handlers. */
130 g_object_set_data (G_OBJECT (xw
->widget_osr
), XG_XWIDGET
, xw
);
131 g_object_set_data (G_OBJECT (xw
->widgetwindow_osr
), XG_XWIDGET
, xw
);
134 if (EQ (xw
->type
, Qwebkit
))
136 g_signal_connect (G_OBJECT (xw
->widget_osr
),
138 G_CALLBACK (webkit_view_load_changed_cb
), xw
);
140 g_signal_connect (G_OBJECT (webkit_web_context_get_default ()),
142 G_CALLBACK (webkit_download_cb
), xw
);
144 g_signal_connect (G_OBJECT (xw
->widget_osr
),
147 (webkit_decide_policy_cb
),
157 DEFUN ("get-buffer-xwidgets", Fget_buffer_xwidgets
, Sget_buffer_xwidgets
,
159 doc
: /* Return a list of xwidgets associated with BUFFER.
160 BUFFER may be a buffer or the name of one. */)
163 Lisp_Object xw
, tail
, xw_list
;
167 buffer
= Fget_buffer (buffer
);
173 for (tail
= Vxwidget_list
; CONSP (tail
); tail
= XCDR (tail
))
176 if (XWIDGETP (xw
) && EQ (Fxwidget_buffer (xw
), buffer
))
177 xw_list
= Fcons (xw
, xw_list
);
183 xwidget_hidden (struct xwidget_view
*xv
)
189 xwidget_show_view (struct xwidget_view
*xv
)
192 gtk_widget_show (xv
->widgetwindow
);
193 gtk_fixed_move (GTK_FIXED (xv
->emacswindow
),
195 xv
->x
+ xv
->clip_left
,
196 xv
->y
+ xv
->clip_top
);
199 /* Hide an xwidget view. */
201 xwidget_hide_view (struct xwidget_view
*xv
)
204 gtk_fixed_move (GTK_FIXED (xv
->emacswindow
), xv
->widgetwindow
,
208 /* When the off-screen webkit master view changes this signal is called.
209 It copies the bitmap from the off-screen instance. */
211 offscreen_damage_event (GtkWidget
*widget
, GdkEvent
*event
,
214 /* Queue a redraw of onscreen widget.
215 There is a guard against receiving an invalid widget,
216 which should only happen if we failed to remove the
217 specific signal handler for the damage event. */
218 if (GTK_IS_WIDGET (xv_widget
))
219 gtk_widget_queue_draw (GTK_WIDGET (xv_widget
));
221 printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
228 store_xwidget_event_string (struct xwidget
*xw
, const char *eventname
,
229 const char *eventstr
)
231 struct input_event event
;
233 XSETXWIDGET (xwl
, xw
);
235 event
.kind
= XWIDGET_EVENT
;
236 event
.frame_or_window
= Qnil
;
237 event
.arg
= list3 (intern (eventname
), xwl
, build_string (eventstr
));
238 kbd_buffer_store_event (&event
);
242 store_xwidget_js_callback_event (struct xwidget
*xw
,
244 Lisp_Object argument
)
246 struct input_event event
;
248 XSETXWIDGET (xwl
, xw
);
250 event
.kind
= XWIDGET_EVENT
;
251 event
.frame_or_window
= Qnil
;
252 event
.arg
= list4 (intern ("javascript-callback"), xwl
, proc
, argument
);
253 kbd_buffer_store_event (&event
);
258 webkit_view_load_changed_cb (WebKitWebView
*webkitwebview
,
259 WebKitLoadEvent load_event
,
262 switch (load_event
) {
263 case WEBKIT_LOAD_FINISHED
:
265 struct xwidget
*xw
= g_object_get_data (G_OBJECT (webkitwebview
),
267 store_xwidget_event_string (xw
, "load-changed", "");
275 /* Recursively convert a JavaScript value to a Lisp value. */
277 webkit_js_to_lisp (JSContextRef context
, JSValueRef value
)
279 switch (JSValueGetType (context
, value
))
283 JSStringRef js_str_value
;
287 js_str_value
= JSValueToStringCopy (context
, value
, NULL
);
288 str_length
= JSStringGetMaximumUTF8CStringSize (js_str_value
);
289 str_value
= (gchar
*)g_malloc (str_length
);
290 JSStringGetUTF8CString (js_str_value
, str_value
, str_length
);
291 JSStringRelease (js_str_value
);
292 return build_string (str_value
);
295 return (JSValueToBoolean (context
, value
)) ? Qt
: Qnil
;
297 return make_number (JSValueToNumber (context
, value
, NULL
));
300 if (JSValueIsArray (context
, value
))
302 JSStringRef pname
= JSStringCreateWithUTF8CString("length");
303 JSValueRef len
= JSObjectGetProperty (context
, (JSObjectRef
) value
, pname
, NULL
);
304 int n
= JSValueToNumber (context
, len
, NULL
);
305 JSStringRelease(pname
);
308 struct Lisp_Vector
*p
= allocate_vector (n
);
310 for (int i
= 0; i
< n
; ++i
)
313 webkit_js_to_lisp (context
,
314 JSObjectGetPropertyAtIndex (context
,
323 JSPropertyNameArrayRef properties
=
324 JSObjectCopyPropertyNames (context
, (JSObjectRef
) value
);
326 int n
= JSPropertyNameArrayGetCount (properties
);
329 /* TODO: can we use a regular list here? */
330 struct Lisp_Vector
*p
= allocate_vector (n
);
332 for (int i
= 0; i
< n
; ++i
)
334 JSStringRef name
= JSPropertyNameArrayGetNameAtIndex (properties
, i
);
335 JSValueRef property
= JSObjectGetProperty (context
,
340 str_length
= JSStringGetMaximumUTF8CStringSize (name
);
341 str_name
= (gchar
*)g_malloc (str_length
);
342 JSStringGetUTF8CString (name
, str_name
, str_length
);
343 JSStringRelease (name
);
346 Fcons (build_string (str_name
),
347 webkit_js_to_lisp (context
, property
));
350 JSPropertyNameArrayRelease (properties
);
355 case kJSTypeUndefined
:
363 webkit_javascript_finished_cb (GObject
*webview
,
364 GAsyncResult
*result
,
365 gpointer lisp_callback
)
367 WebKitJavascriptResult
*js_result
;
369 JSGlobalContextRef context
;
370 GError
*error
= NULL
;
371 struct xwidget
*xw
= g_object_get_data (G_OBJECT (webview
),
374 js_result
= webkit_web_view_run_javascript_finish
375 (WEBKIT_WEB_VIEW (webview
), result
, &error
);
379 g_warning ("Error running javascript: %s", error
->message
);
380 g_error_free (error
);
384 context
= webkit_javascript_result_get_global_context (js_result
);
385 value
= webkit_javascript_result_get_value (js_result
);
386 Lisp_Object lisp_value
= webkit_js_to_lisp (context
, value
);
387 webkit_javascript_result_unref (js_result
);
389 /* Register an xwidget event here, which then runs the callback.
390 This ensures that the callback runs in sync with the Emacs
392 store_xwidget_js_callback_event (xw
, (Lisp_Object
)lisp_callback
,
398 webkit_download_cb (WebKitWebContext
*webkitwebcontext
,
399 WebKitDownload
*arg1
,
402 WebKitWebView
*view
= webkit_download_get_web_view(arg1
);
403 WebKitURIRequest
*request
= webkit_download_get_request(arg1
);
404 struct xwidget
*xw
= g_object_get_data (G_OBJECT (view
),
407 store_xwidget_event_string (xw
, "download-started",
408 webkit_uri_request_get_uri(request
));
413 webkit_decide_policy_cb (WebKitWebView
*webView
,
414 WebKitPolicyDecision
*decision
,
415 WebKitPolicyDecisionType type
,
419 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE
:
420 /* This function makes webkit send a download signal for all unknown
421 mime types. TODO: Defer the decision to Lisp, so that it's
422 possible to make Emacs handle mime text for instance. */
424 WebKitResponsePolicyDecision
*response
=
425 WEBKIT_RESPONSE_POLICY_DECISION (decision
);
426 if (!webkit_response_policy_decision_is_mime_type_supported (response
))
428 webkit_policy_decision_download (decision
);
435 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION
:
436 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION
:
438 WebKitNavigationPolicyDecision
*navigation_decision
=
439 WEBKIT_NAVIGATION_POLICY_DECISION (decision
);
440 WebKitNavigationAction
*navigation_action
=
441 webkit_navigation_policy_decision_get_navigation_action (navigation_decision
);
442 WebKitURIRequest
*request
=
443 webkit_navigation_action_get_request (navigation_action
);
445 struct xwidget
*xw
= g_object_get_data (G_OBJECT (webView
), XG_XWIDGET
);
446 store_xwidget_event_string (xw
, "decide-policy",
447 webkit_uri_request_get_uri (request
));
457 /* For gtk3 offscreen rendered widgets. */
459 xwidget_osr_draw_cb (GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
461 struct xwidget
*xw
= g_object_get_data (G_OBJECT (widget
), XG_XWIDGET
);
462 struct xwidget_view
*xv
= g_object_get_data (G_OBJECT (widget
),
465 cairo_rectangle (cr
, 0, 0, xv
->clip_right
, xv
->clip_bottom
);
468 gtk_widget_draw (xw
->widget_osr
, cr
);
473 xwidget_osr_event_forward (GtkWidget
*widget
, GdkEvent
*event
,
476 /* Copy events that arrive at the outer widget to the offscreen widget. */
477 struct xwidget
*xw
= g_object_get_data (G_OBJECT (widget
), XG_XWIDGET
);
478 GdkEvent
*eventcopy
= gdk_event_copy (event
);
479 eventcopy
->any
.window
= gtk_widget_get_window (xw
->widget_osr
);
481 /* TODO: This might leak events. They should be deallocated later,
482 perhaps in xwgir_event_cb. */
483 gtk_main_do_event (eventcopy
);
485 /* Don't propagate this event further. */
490 xwidget_osr_event_set_embedder (GtkWidget
*widget
, GdkEvent
*event
,
493 struct xwidget_view
*xv
= data
;
494 struct xwidget
*xww
= XXWIDGET (xv
->model
);
495 gdk_offscreen_window_set_embedder (gtk_widget_get_window
496 (xww
->widgetwindow_osr
),
497 gtk_widget_get_window (xv
->widget
));
502 /* Initializes and does initial placement of an xwidget view on screen. */
503 static struct xwidget_view
*
504 xwidget_init_view (struct xwidget
*xww
,
505 struct glyph_string
*s
,
508 struct xwidget_view
*xv
= allocate_xwidget_view ();
511 XSETXWIDGET_VIEW (val
, xv
);
512 Vxwidget_view_list
= Fcons (val
, Vxwidget_view_list
);
514 XSETWINDOW (xv
->w
, s
->w
);
515 XSETXWIDGET (xv
->model
, xww
);
517 if (EQ (xww
->type
, Qwebkit
))
519 xv
->widget
= gtk_drawing_area_new ();
520 /* Expose event handling. */
521 gtk_widget_set_app_paintable (xv
->widget
, TRUE
);
522 gtk_widget_add_events (xv
->widget
, GDK_ALL_EVENTS_MASK
);
524 /* Draw the view on damage-event. */
525 g_signal_connect (G_OBJECT (xww
->widgetwindow_osr
), "damage-event",
526 G_CALLBACK (offscreen_damage_event
), xv
->widget
);
528 if (EQ (xww
->type
, Qwebkit
))
530 g_signal_connect (G_OBJECT (xv
->widget
), "button-press-event",
531 G_CALLBACK (xwidget_osr_event_forward
), NULL
);
532 g_signal_connect (G_OBJECT (xv
->widget
), "button-release-event",
533 G_CALLBACK (xwidget_osr_event_forward
), NULL
);
534 g_signal_connect (G_OBJECT (xv
->widget
), "motion-notify-event",
535 G_CALLBACK (xwidget_osr_event_forward
), NULL
);
539 /* xwgir debug, orthogonal to forwarding. */
540 g_signal_connect (G_OBJECT (xv
->widget
), "enter-notify-event",
541 G_CALLBACK (xwidget_osr_event_set_embedder
), xv
);
543 g_signal_connect (G_OBJECT (xv
->widget
), "draw",
544 G_CALLBACK (xwidget_osr_draw_cb
), NULL
);
547 /* Widget realization.
549 Make container widget first, and put the actual widget inside the
550 container later. Drawing should crop container window if necessary
551 to handle case where xwidget is partially obscured by other Emacs
552 windows. Other containers than gtk_fixed where explored, but
553 gtk_fixed had the most predictable behavior so far. */
555 xv
->emacswindow
= FRAME_GTK_WIDGET (s
->f
);
556 xv
->widgetwindow
= gtk_fixed_new ();
557 gtk_widget_set_has_window (xv
->widgetwindow
, TRUE
);
558 gtk_container_add (GTK_CONTAINER (xv
->widgetwindow
), xv
->widget
);
560 /* Store some xwidget data in the gtk widgets. */
561 g_object_set_data (G_OBJECT (xv
->widget
), XG_FRAME_DATA
, s
->f
);
562 g_object_set_data (G_OBJECT (xv
->widget
), XG_XWIDGET
, xww
);
563 g_object_set_data (G_OBJECT (xv
->widget
), XG_XWIDGET_VIEW
, xv
);
564 g_object_set_data (G_OBJECT (xv
->widgetwindow
), XG_XWIDGET
, xww
);
565 g_object_set_data (G_OBJECT (xv
->widgetwindow
), XG_XWIDGET_VIEW
, xv
);
567 gtk_widget_set_size_request (GTK_WIDGET (xv
->widget
), xww
->width
,
569 gtk_widget_set_size_request (xv
->widgetwindow
, xww
->width
, xww
->height
);
570 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s
->f
)), xv
->widgetwindow
, x
, y
);
573 gtk_widget_show_all (xv
->widgetwindow
);
579 x_draw_xwidget_glyph_string (struct glyph_string
*s
)
581 /* This method is called by the redisplay engine and places the
582 xwidget on screen. Moving and clipping is done here. Also view
584 struct xwidget
*xww
= s
->xwidget
;
585 struct xwidget_view
*xv
;
591 /* FIXME: The result of this call is discarded.
592 What if the lookup fails? */
593 xwidget_view_lookup (xww
, s
->w
);
596 int y
= s
->y
+ (s
->height
/ 2) - (xww
->height
/ 2);
598 /* Do initialization here in the display loop because there is no
599 other time to know things like window placement etc. */
600 xv
= xwidget_init_view (xww
, s
, x
, y
);
602 int text_area_x
, text_area_y
, text_area_width
, text_area_height
;
604 window_box (s
->w
, TEXT_AREA
, &text_area_x
, &text_area_y
,
605 &text_area_width
, &text_area_height
);
606 clip_left
= max (0, text_area_x
- x
);
607 clip_right
= max (clip_left
,
608 min (xww
->width
, text_area_x
+ text_area_width
- x
));
609 clip_top
= max (0, text_area_y
- y
);
610 clip_bottom
= max (clip_top
,
611 min (xww
->height
, text_area_y
+ text_area_height
- y
));
613 /* We are concerned with movement of the onscreen area. The area
614 might sit still when the widget actually moves. This happens
615 when an Emacs window border moves across a widget window. So, if
616 any corner of the outer widget clipping window moves, that counts
617 as movement here, even if it looks like no movement happens
618 because the widget sits still inside the clipping area. The
619 widget can also move inside the clipping area, which happens
621 bool moved
= (xv
->x
+ xv
->clip_left
!= x
+ clip_left
622 || xv
->y
+ xv
->clip_top
!= y
+ clip_top
);
628 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s
->f
)),
629 xv
->widgetwindow
, x
+ clip_left
, y
+ clip_top
);
631 /* Clip the widget window if some parts happen to be outside
632 drawable area. An Emacs window is not a gtk window. A gtk window
633 covers the entire frame. Clipping might have changed even if we
634 haven't actually moved; try to figure out when we need to reclip
636 if (xv
->clip_right
!= clip_right
637 || xv
->clip_bottom
!= clip_bottom
638 || xv
->clip_top
!= clip_top
|| xv
->clip_left
!= clip_left
)
640 gtk_widget_set_size_request (xv
->widgetwindow
, clip_right
- clip_left
,
641 clip_bottom
- clip_top
);
642 gtk_fixed_move (GTK_FIXED (xv
->widgetwindow
), xv
->widget
, -clip_left
,
645 xv
->clip_right
= clip_right
;
646 xv
->clip_bottom
= clip_bottom
;
647 xv
->clip_top
= clip_top
;
648 xv
->clip_left
= clip_left
;
651 /* If emacs wants to repaint the area where the widget lives, queue
652 a redraw. It seems its possible to get out of sync with emacs
653 redraws so emacs background sometimes shows up instead of the
654 xwidgets background. It's just a visual glitch though. */
655 if (!xwidget_hidden (xv
))
657 gtk_widget_queue_draw (xv
->widgetwindow
);
658 gtk_widget_queue_draw (xv
->widget
);
662 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
663 #define WEBKIT_FN_INIT() \
664 CHECK_XWIDGET (xwidget); \
665 struct xwidget *xw = XXWIDGET (xwidget); \
666 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
668 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
672 DEFUN ("xwidget-webkit-goto-uri",
673 Fxwidget_webkit_goto_uri
, Sxwidget_webkit_goto_uri
,
675 doc
: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
676 (Lisp_Object xwidget
, Lisp_Object uri
)
680 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw
->widget_osr
), SSDATA (uri
));
684 DEFUN ("xwidget-webkit-zoom",
685 Fxwidget_webkit_zoom
, Sxwidget_webkit_zoom
,
687 doc
: /* Change the zoom factor of the xwidget webkit instance
688 referenced by XWIDGET. */)
689 (Lisp_Object xwidget
, Lisp_Object factor
)
694 double zoom_change
= XFLOAT_DATA (factor
);
695 webkit_web_view_set_zoom_level
696 (WEBKIT_WEB_VIEW (xw
->widget_osr
),
697 webkit_web_view_get_zoom_level
698 (WEBKIT_WEB_VIEW (xw
->widget_osr
)) + zoom_change
);
704 DEFUN ("xwidget-webkit-execute-script",
705 Fxwidget_webkit_execute_script
, Sxwidget_webkit_execute_script
,
707 doc
: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. If
708 FUN is provided, feed the JavaScript return value to the single
709 argument procedure FUN.*/)
710 (Lisp_Object xwidget
, Lisp_Object script
, Lisp_Object fun
)
713 CHECK_STRING (script
);
714 if (!NILP (fun
) && (!FUNCTIONP (fun
)))
715 wrong_type_argument (Qinvalid_function
, fun
);
717 void *callback
= (FUNCTIONP (fun
)) ?
718 &webkit_javascript_finished_cb
: NULL
;
720 /* JavaScript execution happens asynchronously. If an elisp
721 callback function is provided we pass it to the C callback
722 procedure that retrieves the return value. */
723 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw
->widget_osr
),
725 NULL
, /* cancelable */
731 DEFUN ("xwidget-resize", Fxwidget_resize
, Sxwidget_resize
, 3, 3, 0,
732 doc
: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
733 (Lisp_Object xwidget
, Lisp_Object new_width
, Lisp_Object new_height
)
735 CHECK_XWIDGET (xwidget
);
736 CHECK_NATNUM (new_width
);
737 CHECK_NATNUM (new_height
);
738 struct xwidget
*xw
= XXWIDGET (xwidget
);
739 int w
= XFASTINT (new_width
);
740 int h
= XFASTINT (new_height
);
745 /* If there is an offscreen widget resize it first. */
748 gtk_window_resize (GTK_WINDOW (xw
->widgetwindow_osr
), xw
->width
,
750 gtk_container_resize_children (GTK_CONTAINER (xw
->widgetwindow_osr
));
751 gtk_widget_set_size_request (GTK_WIDGET (xw
->widget_osr
), xw
->width
,
755 for (Lisp_Object tail
= Vxwidget_view_list
; CONSP (tail
); tail
= XCDR (tail
))
757 if (XWIDGET_VIEW_P (XCAR (tail
)))
759 struct xwidget_view
*xv
= XXWIDGET_VIEW (XCAR (tail
));
760 if (XXWIDGET (xv
->model
) == xw
)
761 gtk_widget_set_size_request (GTK_WIDGET (xv
->widget
), xw
->width
,
772 DEFUN ("xwidget-size-request",
773 Fxwidget_size_request
, Sxwidget_size_request
,
775 doc
: /* Return the desired size of the XWIDGET.
776 This can be used to read the xwidget desired size, and resizes the
777 Emacs allocated area accordingly. */)
778 (Lisp_Object xwidget
)
780 CHECK_XWIDGET (xwidget
);
781 GtkRequisition requisition
;
782 gtk_widget_size_request (XXWIDGET (xwidget
)->widget_osr
, &requisition
);
783 return list2 (make_number (requisition
.width
),
784 make_number (requisition
.height
));
788 Fxwidgetp
, Sxwidgetp
,
790 doc
: /* Return t if OBJECT is an xwidget. */)
793 return XWIDGETP (object
) ? Qt
: Qnil
;
796 DEFUN ("xwidget-view-p",
797 Fxwidget_view_p
, Sxwidget_view_p
,
799 doc
: /* Return t if OBJECT is an xwidget-view. */)
802 return XWIDGET_VIEW_P (object
) ? Qt
: Qnil
;
805 DEFUN ("xwidget-info",
806 Fxwidget_info
, Sxwidget_info
,
808 doc
: /* Return XWIDGET properties in a vector.
809 Currently [TYPE TITLE WIDTH HEIGHT]. */)
810 (Lisp_Object xwidget
)
812 CHECK_XWIDGET (xwidget
);
813 struct xwidget
*xw
= XXWIDGET (xwidget
);
814 return CALLN (Fvector
, xw
->type
, xw
->title
,
815 make_natnum (xw
->width
), make_natnum (xw
->height
));
818 DEFUN ("xwidget-view-info",
819 Fxwidget_view_info
, Sxwidget_view_info
,
821 doc
: /* Return properties of XWIDGET-VIEW in a vector.
822 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
823 (Lisp_Object xwidget_view
)
825 CHECK_XWIDGET_VIEW (xwidget_view
);
826 struct xwidget_view
*xv
= XXWIDGET_VIEW (xwidget_view
);
827 return CALLN (Fvector
, make_number (xv
->x
), make_number (xv
->y
),
828 make_number (xv
->clip_right
), make_number (xv
->clip_bottom
),
829 make_number (xv
->clip_top
), make_number (xv
->clip_left
));
832 DEFUN ("xwidget-view-model",
833 Fxwidget_view_model
, Sxwidget_view_model
,
835 doc
: /* Return the model associated with XWIDGET-VIEW. */)
836 (Lisp_Object xwidget_view
)
838 CHECK_XWIDGET_VIEW (xwidget_view
);
839 return XXWIDGET_VIEW (xwidget_view
)->model
;
842 DEFUN ("xwidget-view-window",
843 Fxwidget_view_window
, Sxwidget_view_window
,
845 doc
: /* Return the window of XWIDGET-VIEW. */)
846 (Lisp_Object xwidget_view
)
848 CHECK_XWIDGET_VIEW (xwidget_view
);
849 return XXWIDGET_VIEW (xwidget_view
)->w
;
853 DEFUN ("delete-xwidget-view",
854 Fdelete_xwidget_view
, Sdelete_xwidget_view
,
856 doc
: /* Delete the XWIDGET-VIEW. */)
857 (Lisp_Object xwidget_view
)
859 CHECK_XWIDGET_VIEW (xwidget_view
);
860 struct xwidget_view
*xv
= XXWIDGET_VIEW (xwidget_view
);
861 gtk_widget_destroy (xv
->widgetwindow
);
862 Vxwidget_view_list
= Fdelq (xwidget_view
, Vxwidget_view_list
);
863 /* xv->model still has signals pointing to the view. There can be
864 several views. Find the matching signals and delete them all. */
865 g_signal_handlers_disconnect_matched (XXWIDGET (xv
->model
)->widgetwindow_osr
,
872 DEFUN ("xwidget-view-lookup",
873 Fxwidget_view_lookup
, Sxwidget_view_lookup
,
875 doc
: /* Return the xwidget-view associated with XWIDGET in WINDOW.
876 If WINDOW is unspecified or nil, use the selected window.
877 Return nil if no association is found. */)
878 (Lisp_Object xwidget
, Lisp_Object window
)
880 CHECK_XWIDGET (xwidget
);
883 window
= Fselected_window ();
884 CHECK_WINDOW (window
);
886 for (Lisp_Object tail
= Vxwidget_view_list
; CONSP (tail
);
889 Lisp_Object xwidget_view
= XCAR (tail
);
890 if (EQ (Fxwidget_view_model (xwidget_view
), xwidget
)
891 && EQ (Fxwidget_view_window (xwidget_view
), window
))
898 DEFUN ("xwidget-plist",
899 Fxwidget_plist
, Sxwidget_plist
,
901 doc
: /* Return the plist of XWIDGET. */)
902 (Lisp_Object xwidget
)
904 CHECK_XWIDGET (xwidget
);
905 return XXWIDGET (xwidget
)->plist
;
908 DEFUN ("xwidget-buffer",
909 Fxwidget_buffer
, Sxwidget_buffer
,
911 doc
: /* Return the buffer of XWIDGET. */)
912 (Lisp_Object xwidget
)
914 CHECK_XWIDGET (xwidget
);
915 return XXWIDGET (xwidget
)->buffer
;
918 DEFUN ("set-xwidget-plist",
919 Fset_xwidget_plist
, Sset_xwidget_plist
,
921 doc
: /* Replace the plist of XWIDGET with PLIST.
923 (Lisp_Object xwidget
, Lisp_Object plist
)
925 CHECK_XWIDGET (xwidget
);
928 XXWIDGET (xwidget
)->plist
= plist
;
932 DEFUN ("set-xwidget-query-on-exit-flag",
933 Fset_xwidget_query_on_exit_flag
, Sset_xwidget_query_on_exit_flag
,
935 doc
: /* Specify if query is needed for XWIDGET when Emacs is exited.
936 If the second argument FLAG is non-nil, Emacs will query the user before
937 exiting or killing a buffer if XWIDGET is running.
938 This function returns FLAG. */)
939 (Lisp_Object xwidget
, Lisp_Object flag
)
941 CHECK_XWIDGET (xwidget
);
942 XXWIDGET (xwidget
)->kill_without_query
= NILP (flag
);
946 DEFUN ("xwidget-query-on-exit-flag",
947 Fxwidget_query_on_exit_flag
, Sxwidget_query_on_exit_flag
,
949 doc
: /* Return the current value of the query-on-exit flag for XWIDGET. */)
950 (Lisp_Object xwidget
)
952 CHECK_XWIDGET (xwidget
);
953 return (XXWIDGET (xwidget
)->kill_without_query
? Qnil
: Qt
);
957 syms_of_xwidget (void)
959 defsubr (&Smake_xwidget
);
960 defsubr (&Sxwidgetp
);
961 DEFSYM (Qxwidgetp
, "xwidgetp");
962 defsubr (&Sxwidget_view_p
);
963 DEFSYM (Qxwidget_view_p
, "xwidget-view-p");
964 defsubr (&Sxwidget_info
);
965 defsubr (&Sxwidget_view_info
);
966 defsubr (&Sxwidget_resize
);
967 defsubr (&Sget_buffer_xwidgets
);
968 defsubr (&Sxwidget_view_model
);
969 defsubr (&Sxwidget_view_window
);
970 defsubr (&Sxwidget_view_lookup
);
971 defsubr (&Sxwidget_query_on_exit_flag
);
972 defsubr (&Sset_xwidget_query_on_exit_flag
);
974 defsubr (&Sxwidget_webkit_goto_uri
);
975 defsubr (&Sxwidget_webkit_zoom
);
976 defsubr (&Sxwidget_webkit_execute_script
);
977 DEFSYM (Qwebkit
, "webkit");
979 defsubr (&Sxwidget_size_request
);
980 defsubr (&Sdelete_xwidget_view
);
982 defsubr (&Sxwidget_plist
);
983 defsubr (&Sxwidget_buffer
);
984 defsubr (&Sset_xwidget_plist
);
986 DEFSYM (Qxwidget
, "xwidget");
988 DEFSYM (QCxwidget
, ":xwidget");
989 DEFSYM (QCtitle
, ":title");
991 /* Do not forget to update the docstring of make-xwidget if you add
994 DEFSYM (Qvertical
, "vertical");
995 DEFSYM (Qhorizontal
, "horizontal");
997 DEFSYM (QCplist
, ":plist");
999 DEFVAR_LISP ("xwidget-list", Vxwidget_list
,
1000 doc
: /* xwidgets list. */);
1001 Vxwidget_list
= Qnil
;
1003 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list
,
1004 doc
: /* xwidget views list. */);
1005 Vxwidget_view_list
= Qnil
;
1007 Fprovide (intern ("xwidget-internal"), Qnil
);
1011 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1012 valid xwidget specification is a list whose car is the symbol
1013 `xwidget', and whose rest is a property list. The property list must
1014 contain a value for key `:type'. That value must be the name of a
1015 supported xwidget type. The rest of the property list depends on the
1019 valid_xwidget_spec_p (Lisp_Object object
)
1021 return CONSP (object
) && EQ (XCAR (object
), Qxwidget
);
1025 /* Find a value associated with key in spec. */
1027 xwidget_spec_value (Lisp_Object spec
, Lisp_Object key
)
1031 eassert (valid_xwidget_spec_p (spec
));
1033 for (tail
= XCDR (spec
);
1034 CONSP (tail
) && CONSP (XCDR (tail
)); tail
= XCDR (XCDR (tail
)))
1036 if (EQ (XCAR (tail
), key
))
1037 return XCAR (XCDR (tail
));
1045 xwidget_view_delete_all_in_window (struct window
*w
)
1047 struct xwidget_view
*xv
= NULL
;
1048 for (Lisp_Object tail
= Vxwidget_view_list
; CONSP (tail
);
1051 if (XWIDGET_VIEW_P (XCAR (tail
)))
1053 xv
= XXWIDGET_VIEW (XCAR (tail
));
1054 if (XWINDOW (xv
->w
) == w
)
1056 Fdelete_xwidget_view (XCAR (tail
));
1062 static struct xwidget_view
*
1063 xwidget_view_lookup (struct xwidget
*xw
, struct window
*w
)
1065 Lisp_Object xwidget
, window
, ret
;
1066 XSETXWIDGET (xwidget
, xw
);
1067 XSETWINDOW (window
, w
);
1069 ret
= Fxwidget_view_lookup (xwidget
, window
);
1071 return EQ (ret
, Qnil
) ? NULL
: XXWIDGET_VIEW (ret
);
1075 lookup_xwidget (Lisp_Object spec
)
1077 /* When a xwidget lisp spec is found initialize the C struct that is
1078 used in the C code. This is done by redisplay so values change
1079 if the spec changes. So, take special care of one-shot events. */
1083 value
= xwidget_spec_value (spec
, QCxwidget
);
1084 xw
= XXWIDGET (value
);
1089 /* Set up detection of touched xwidget. */
1091 xwidget_start_redisplay (void)
1093 for (Lisp_Object tail
= Vxwidget_view_list
; CONSP (tail
);
1096 if (XWIDGET_VIEW_P (XCAR (tail
)))
1097 XXWIDGET_VIEW (XCAR (tail
))->redisplayed
= false;
1101 /* The xwidget was touched during redisplay, so it isn't a candidate
1104 xwidget_touch (struct xwidget_view
*xv
)
1106 xv
->redisplayed
= true;
1110 xwidget_touched (struct xwidget_view
*xv
)
1112 return xv
->redisplayed
;
1115 /* Redisplay has ended, now we should hide untouched xwidgets. */
1117 xwidget_end_redisplay (struct window
*w
, struct glyph_matrix
*matrix
)
1122 xwidget_start_redisplay ();
1123 /* Iterate desired glyph matrix of window here, hide gtk widgets
1124 not in the desired matrix.
1126 This only takes care of xwidgets in active windows. If a window
1127 goes away from the screen, xwidget views must be deleted.
1129 dump_glyph_matrix (matrix, 2); */
1130 for (i
= 0; i
< matrix
->nrows
; ++i
)
1132 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1133 struct glyph_row
*row
;
1134 row
= MATRIX_ROW (matrix
, i
);
1136 for (area
= LEFT_MARGIN_AREA
; area
< LAST_AREA
; ++area
)
1138 struct glyph
*glyph
= row
->glyphs
[area
];
1139 struct glyph
*glyph_end
= glyph
+ row
->used
[area
];
1140 for (; glyph
< glyph_end
; ++glyph
)
1141 if (glyph
->type
== XWIDGET_GLYPH
)
1143 /* The only call to xwidget_end_redisplay is in dispnew.
1144 xwidget_end_redisplay (w->current_matrix); */
1145 struct xwidget_view
*xv
1146 = xwidget_view_lookup (glyph
->u
.xwidget
, w
);
1147 /* FIXME: Is it safe to assume xwidget_view_lookup
1148 always succeeds here? If so, this comment can be removed.
1149 If not, the code probably needs fixing. */
1156 for (Lisp_Object tail
= Vxwidget_view_list
; CONSP (tail
);
1159 if (XWIDGET_VIEW_P (XCAR (tail
)))
1161 struct xwidget_view
*xv
= XXWIDGET_VIEW (XCAR (tail
));
1163 /* "touched" is only meaningful for the current window, so
1164 disregard other views. */
1165 if (XWINDOW (xv
->w
) == w
)
1167 if (xwidget_touched (xv
))
1168 xwidget_show_view (xv
);
1170 xwidget_hide_view (xv
);
1176 /* Kill all xwidget in BUFFER. */
1178 kill_buffer_xwidgets (Lisp_Object buffer
)
1180 Lisp_Object tail
, xwidget
;
1181 for (tail
= Fget_buffer_xwidgets (buffer
); CONSP (tail
); tail
= XCDR (tail
))
1183 xwidget
= XCAR (tail
);
1184 Vxwidget_list
= Fdelq (xwidget
, Vxwidget_list
);
1185 /* TODO free the GTK things in xw. */
1187 CHECK_XWIDGET (xwidget
);
1188 struct xwidget
*xw
= XXWIDGET (xwidget
);
1189 if (xw
->widget_osr
&& xw
->widgetwindow_osr
)
1191 gtk_widget_destroy (xw
->widget_osr
);
1192 gtk_widget_destroy (xw
->widgetwindow_osr
);