* lisp/emacs-lisp/benchmark.el (benchmark-run): Allow variable.
[emacs.git] / src / xwidget.c
blob95fa5f19c408941f6e40ec9ef557f7222a673522
1 /* Support for embedding graphical components in a buffer.
3 Copyright (C) 2011-2018 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 <https://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #include "xwidget.h"
24 #include "lisp.h"
25 #include "blockinput.h"
26 #include "frame.h"
27 #include "keyboard.h"
28 #include "gtkutil.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,
43 PVEC_XWIDGET_VIEW);
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 *,
50 struct window *);
51 static void webkit_view_load_changed_cb (WebKitWebView *,
52 WebKitLoadEvent,
53 gpointer);
54 static void webkit_javascript_finished_cb (GObject *,
55 GAsyncResult *,
56 gpointer);
57 static gboolean webkit_download_cb (WebKitWebContext *, WebKitDownload *, gpointer);
59 static gboolean
60 webkit_decide_policy_cb (WebKitWebView *,
61 WebKitPolicyDecision *,
62 WebKitPolicyDecisionType,
63 gpointer);
66 DEFUN ("make-xwidget",
67 Fmake_xwidget, Smake_xwidget,
68 5, 6, 0,
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:
74 - webkit
76 Returns the newly constructed xwidget, or nil if construction fails. */)
77 (Lisp_Object type,
78 Lisp_Object title, Lisp_Object width, Lisp_Object height,
79 Lisp_Object arguments, Lisp_Object buffer)
81 CHECK_SYMBOL (type);
82 CHECK_NATNUM (width);
83 CHECK_NATNUM (height);
85 struct xwidget *xw = allocate_xwidget ();
86 Lisp_Object val;
87 xw->type = type;
88 xw->title = title;
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;
97 xw->plist = Qnil;
99 if (EQ (xw->type, Qwebkit))
101 block_input ();
102 xw->widgetwindow_osr = gtk_offscreen_window_new ();
103 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
104 xw->height);
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,
112 xw->height);
114 if (EQ (xw->type, Qwebkit))
116 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
117 GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr)));
119 else
121 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
122 xw->widget_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);
133 /* signals */
134 if (EQ (xw->type, Qwebkit))
136 g_signal_connect (G_OBJECT (xw->widget_osr),
137 "load-changed",
138 G_CALLBACK (webkit_view_load_changed_cb), xw);
140 g_signal_connect (G_OBJECT (webkit_web_context_get_default ()),
141 "download-started",
142 G_CALLBACK (webkit_download_cb), xw);
144 g_signal_connect (G_OBJECT (xw->widget_osr),
145 "decide-policy",
146 G_CALLBACK
147 (webkit_decide_policy_cb),
148 xw);
151 unblock_input ();
154 return val;
157 DEFUN ("get-buffer-xwidgets", Fget_buffer_xwidgets, Sget_buffer_xwidgets,
158 1, 1, 0,
159 doc: /* Return a list of xwidgets associated with BUFFER.
160 BUFFER may be a buffer or the name of one. */)
161 (Lisp_Object buffer)
163 Lisp_Object xw, tail, xw_list;
165 if (NILP (buffer))
166 return Qnil;
167 buffer = Fget_buffer (buffer);
168 if (NILP (buffer))
169 return Qnil;
171 xw_list = Qnil;
173 for (tail = Vxwidget_list; CONSP (tail); tail = XCDR (tail))
175 xw = XCAR (tail);
176 if (XWIDGETP (xw) && EQ (Fxwidget_buffer (xw), buffer))
177 xw_list = Fcons (xw, xw_list);
179 return xw_list;
182 static bool
183 xwidget_hidden (struct xwidget_view *xv)
185 return xv->hidden;
188 static void
189 xwidget_show_view (struct xwidget_view *xv)
191 xv->hidden = false;
192 gtk_widget_show (xv->widgetwindow);
193 gtk_fixed_move (GTK_FIXED (xv->emacswindow),
194 xv->widgetwindow,
195 xv->x + xv->clip_left,
196 xv->y + xv->clip_top);
199 /* Hide an xwidget view. */
200 static void
201 xwidget_hide_view (struct xwidget_view *xv)
203 xv->hidden = true;
204 gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow,
205 10000, 10000);
208 /* When the off-screen webkit master view changes this signal is called.
209 It copies the bitmap from the off-screen instance. */
210 static gboolean
211 offscreen_damage_event (GtkWidget *widget, GdkEvent *event,
212 gpointer xv_widget)
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));
220 else
221 printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
222 xv_widget);
224 return FALSE;
227 static void
228 store_xwidget_event_string (struct xwidget *xw, const char *eventname,
229 const char *eventstr)
231 struct input_event event;
232 Lisp_Object xwl;
233 XSETXWIDGET (xwl, xw);
234 EVENT_INIT (event);
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);
241 static void
242 store_xwidget_js_callback_event (struct xwidget *xw,
243 Lisp_Object proc,
244 Lisp_Object argument)
246 struct input_event event;
247 Lisp_Object xwl;
248 XSETXWIDGET (xwl, xw);
249 EVENT_INIT (event);
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);
257 void
258 webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
259 WebKitLoadEvent load_event,
260 gpointer data)
262 switch (load_event) {
263 case WEBKIT_LOAD_FINISHED:
265 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
266 XG_XWIDGET);
267 store_xwidget_event_string (xw, "load-changed", "");
268 break;
270 default:
271 break;
275 /* Recursively convert a JavaScript value to a Lisp value. */
276 static Lisp_Object
277 webkit_js_to_lisp (JSContextRef context, JSValueRef value)
279 switch (JSValueGetType (context, value))
281 case kJSTypeString:
283 JSStringRef js_str_value;
284 gchar *str_value;
285 gsize str_length;
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);
294 case kJSTypeBoolean:
295 return (JSValueToBoolean (context, value)) ? Qt : Qnil;
296 case kJSTypeNumber:
297 return make_number (JSValueToNumber (context, value, NULL));
298 case kJSTypeObject:
300 if (JSValueIsArray (context, value))
302 JSStringRef pname = JSStringCreateWithUTF8CString("length");
303 JSValueRef len = JSObjectGetProperty (context, (JSObjectRef) value, pname, NULL);
304 EMACS_INT n = JSValueToNumber (context, len, NULL);
305 JSStringRelease(pname);
307 Lisp_Object obj;
308 struct Lisp_Vector *p = allocate_vector (n);
310 for (ptrdiff_t i = 0; i < n; ++i)
312 p->contents[i] =
313 webkit_js_to_lisp (context,
314 JSObjectGetPropertyAtIndex (context,
315 (JSObjectRef) value,
316 i, NULL));
318 XSETVECTOR (obj, p);
319 return obj;
321 else
323 JSPropertyNameArrayRef properties =
324 JSObjectCopyPropertyNames (context, (JSObjectRef) value);
326 ptrdiff_t n = JSPropertyNameArrayGetCount (properties);
327 Lisp_Object obj;
329 /* TODO: can we use a regular list here? */
330 struct Lisp_Vector *p = allocate_vector (n);
332 for (ptrdiff_t i = 0; i < n; ++i)
334 JSStringRef name = JSPropertyNameArrayGetNameAtIndex (properties, i);
335 JSValueRef property = JSObjectGetProperty (context,
336 (JSObjectRef) value,
337 name, NULL);
338 gchar *str_name;
339 gsize str_length;
340 str_length = JSStringGetMaximumUTF8CStringSize (name);
341 str_name = (gchar *)g_malloc (str_length);
342 JSStringGetUTF8CString (name, str_name, str_length);
343 JSStringRelease (name);
345 p->contents[i] =
346 Fcons (build_string (str_name),
347 webkit_js_to_lisp (context, property));
350 JSPropertyNameArrayRelease (properties);
351 XSETVECTOR (obj, p);
352 return obj;
355 case kJSTypeUndefined:
356 case kJSTypeNull:
357 default:
358 return Qnil;
362 static void
363 webkit_javascript_finished_cb (GObject *webview,
364 GAsyncResult *result,
365 gpointer lisp_callback)
367 WebKitJavascriptResult *js_result;
368 JSValueRef value;
369 JSGlobalContextRef context;
370 GError *error = NULL;
371 struct xwidget *xw = g_object_get_data (G_OBJECT (webview),
372 XG_XWIDGET);
374 js_result = webkit_web_view_run_javascript_finish
375 (WEBKIT_WEB_VIEW (webview), result, &error);
377 if (!js_result)
379 g_warning ("Error running javascript: %s", error->message);
380 g_error_free (error);
381 return;
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
391 event loop. */
392 /* FIXME: This might lead to disaster if LISP_CALLBACK's object
393 was garbage collected before now. See the FIXME in
394 Fxwidget_webkit_execute_script. */
395 store_xwidget_js_callback_event (xw, XPL (lisp_callback), lisp_value);
399 gboolean
400 webkit_download_cb (WebKitWebContext *webkitwebcontext,
401 WebKitDownload *arg1,
402 gpointer data)
404 WebKitWebView *view = webkit_download_get_web_view(arg1);
405 WebKitURIRequest *request = webkit_download_get_request(arg1);
406 struct xwidget *xw = g_object_get_data (G_OBJECT (view),
407 XG_XWIDGET);
409 store_xwidget_event_string (xw, "download-started",
410 webkit_uri_request_get_uri(request));
411 return FALSE;
414 static gboolean
415 webkit_decide_policy_cb (WebKitWebView *webView,
416 WebKitPolicyDecision *decision,
417 WebKitPolicyDecisionType type,
418 gpointer user_data)
420 switch (type) {
421 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
422 /* This function makes webkit send a download signal for all unknown
423 mime types. TODO: Defer the decision to Lisp, so that it's
424 possible to make Emacs handle mime text for instance. */
426 WebKitResponsePolicyDecision *response =
427 WEBKIT_RESPONSE_POLICY_DECISION (decision);
428 if (!webkit_response_policy_decision_is_mime_type_supported (response))
430 webkit_policy_decision_download (decision);
431 return TRUE;
433 else
434 return FALSE;
435 break;
437 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
438 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
440 WebKitNavigationPolicyDecision *navigation_decision =
441 WEBKIT_NAVIGATION_POLICY_DECISION (decision);
442 WebKitNavigationAction *navigation_action =
443 webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
444 WebKitURIRequest *request =
445 webkit_navigation_action_get_request (navigation_action);
447 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
448 store_xwidget_event_string (xw, "decide-policy",
449 webkit_uri_request_get_uri (request));
450 return FALSE;
451 break;
453 default:
454 return FALSE;
459 /* For gtk3 offscreen rendered widgets. */
460 static gboolean
461 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
463 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
464 struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget),
465 XG_XWIDGET_VIEW);
467 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
468 cairo_clip (cr);
470 gtk_widget_draw (xw->widget_osr, cr);
471 return FALSE;
474 static gboolean
475 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
476 gpointer user_data)
478 /* Copy events that arrive at the outer widget to the offscreen widget. */
479 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
480 GdkEvent *eventcopy = gdk_event_copy (event);
481 eventcopy->any.window = gtk_widget_get_window (xw->widget_osr);
483 /* TODO: This might leak events. They should be deallocated later,
484 perhaps in xwgir_event_cb. */
485 gtk_main_do_event (eventcopy);
487 /* Don't propagate this event further. */
488 return TRUE;
491 static gboolean
492 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
493 gpointer data)
495 struct xwidget_view *xv = data;
496 struct xwidget *xww = XXWIDGET (xv->model);
497 gdk_offscreen_window_set_embedder (gtk_widget_get_window
498 (xww->widgetwindow_osr),
499 gtk_widget_get_window (xv->widget));
500 return FALSE;
504 /* Initializes and does initial placement of an xwidget view on screen. */
505 static struct xwidget_view *
506 xwidget_init_view (struct xwidget *xww,
507 struct glyph_string *s,
508 int x, int y)
510 struct xwidget_view *xv = allocate_xwidget_view ();
511 Lisp_Object val;
513 XSETXWIDGET_VIEW (val, xv);
514 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
516 XSETWINDOW (xv->w, s->w);
517 XSETXWIDGET (xv->model, xww);
519 if (EQ (xww->type, Qwebkit))
521 xv->widget = gtk_drawing_area_new ();
522 /* Expose event handling. */
523 gtk_widget_set_app_paintable (xv->widget, TRUE);
524 gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK);
526 /* Draw the view on damage-event. */
527 g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event",
528 G_CALLBACK (offscreen_damage_event), xv->widget);
530 if (EQ (xww->type, Qwebkit))
532 g_signal_connect (G_OBJECT (xv->widget), "button-press-event",
533 G_CALLBACK (xwidget_osr_event_forward), NULL);
534 g_signal_connect (G_OBJECT (xv->widget), "button-release-event",
535 G_CALLBACK (xwidget_osr_event_forward), NULL);
536 g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event",
537 G_CALLBACK (xwidget_osr_event_forward), NULL);
539 else
541 /* xwgir debug, orthogonal to forwarding. */
542 g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event",
543 G_CALLBACK (xwidget_osr_event_set_embedder), xv);
545 g_signal_connect (G_OBJECT (xv->widget), "draw",
546 G_CALLBACK (xwidget_osr_draw_cb), NULL);
549 /* Widget realization.
551 Make container widget first, and put the actual widget inside the
552 container later. Drawing should crop container window if necessary
553 to handle case where xwidget is partially obscured by other Emacs
554 windows. Other containers than gtk_fixed where explored, but
555 gtk_fixed had the most predictable behavior so far. */
557 xv->emacswindow = FRAME_GTK_WIDGET (s->f);
558 xv->widgetwindow = gtk_fixed_new ();
559 gtk_widget_set_has_window (xv->widgetwindow, TRUE);
560 gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget);
562 /* Store some xwidget data in the gtk widgets. */
563 g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f);
564 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww);
565 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv);
566 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww);
567 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv);
569 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width,
570 xww->height);
571 gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height);
572 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y);
573 xv->x = x;
574 xv->y = y;
575 gtk_widget_show_all (xv->widgetwindow);
577 return xv;
580 void
581 x_draw_xwidget_glyph_string (struct glyph_string *s)
583 /* This method is called by the redisplay engine and places the
584 xwidget on screen. Moving and clipping is done here. Also view
585 initialization. */
586 struct xwidget *xww = s->xwidget;
587 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
588 int clip_right;
589 int clip_bottom;
590 int clip_top;
591 int clip_left;
593 int x = s->x;
594 int y = s->y + (s->height / 2) - (xww->height / 2);
596 /* Do initialization here in the display loop because there is no
597 other time to know things like window placement etc. Do not
598 create a new view if we have found one that is usable. */
599 if (!xv)
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
620 later. */
621 bool moved = (xv->x + xv->clip_left != x + clip_left
622 || xv->y + xv->clip_top != y + clip_top);
623 xv->x = x;
624 xv->y = y;
626 /* Has it moved? */
627 if (moved)
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
635 for real. */
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,
643 -clip_top);
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"); \
669 return Qnil; \
672 DEFUN ("xwidget-webkit-goto-uri",
673 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
674 2, 2, 0,
675 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
676 (Lisp_Object xwidget, Lisp_Object uri)
678 WEBKIT_FN_INIT ();
679 CHECK_STRING (uri);
680 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
681 return Qnil;
684 DEFUN ("xwidget-webkit-zoom",
685 Fxwidget_webkit_zoom, Sxwidget_webkit_zoom,
686 2, 2, 0,
687 doc: /* Change the zoom factor of the xwidget webkit instance
688 referenced by XWIDGET. */)
689 (Lisp_Object xwidget, Lisp_Object factor)
691 WEBKIT_FN_INIT ();
692 if (FLOATP (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);
700 return Qnil;
704 DEFUN ("xwidget-webkit-execute-script",
705 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
706 2, 3, 0,
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)
712 WEBKIT_FN_INIT ();
713 CHECK_STRING (script);
714 if (!NILP (fun) && !FUNCTIONP (fun))
715 wrong_type_argument (Qinvalid_function, fun);
717 GAsyncReadyCallback callback
718 = FUNCTIONP (fun) ? webkit_javascript_finished_cb : NULL;
720 /* FIXME: The following hack assumes USE_LSB_TAG. */
721 verify (USE_LSB_TAG);
722 /* FIXME: This hack might lead to disaster if FUN is garbage
723 collected before store_xwidget_js_callback_event makes it visible
724 to Lisp again. See the FIXME in webkit_javascript_finished_cb. */
725 gpointer callback_arg = XLP (fun);
727 /* JavaScript execution happens asynchronously. If an elisp
728 callback function is provided we pass it to the C callback
729 procedure that retrieves the return value. */
730 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw->widget_osr),
731 SSDATA (script),
732 NULL, /* cancelable */
733 callback, callback_arg);
734 return Qnil;
737 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
738 doc: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
739 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
741 CHECK_XWIDGET (xwidget);
742 CHECK_RANGED_INTEGER (new_width, 0, INT_MAX);
743 CHECK_RANGED_INTEGER (new_height, 0, INT_MAX);
744 struct xwidget *xw = XXWIDGET (xwidget);
745 int w = XFASTINT (new_width);
746 int h = XFASTINT (new_height);
748 xw->width = w;
749 xw->height = h;
751 /* If there is an offscreen widget resize it first. */
752 if (xw->widget_osr)
754 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
755 xw->height);
756 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
757 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
758 xw->height);
761 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
763 if (XWIDGET_VIEW_P (XCAR (tail)))
765 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
766 if (XXWIDGET (xv->model) == xw)
767 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
768 xw->height);
772 return Qnil;
778 DEFUN ("xwidget-size-request",
779 Fxwidget_size_request, Sxwidget_size_request,
780 1, 1, 0,
781 doc: /* Return the desired size of the XWIDGET.
782 This can be used to read the xwidget desired size, and resizes the
783 Emacs allocated area accordingly. */)
784 (Lisp_Object xwidget)
786 CHECK_XWIDGET (xwidget);
787 GtkRequisition requisition;
788 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
789 return list2 (make_number (requisition.width),
790 make_number (requisition.height));
793 DEFUN ("xwidgetp",
794 Fxwidgetp, Sxwidgetp,
795 1, 1, 0,
796 doc: /* Return t if OBJECT is an xwidget. */)
797 (Lisp_Object object)
799 return XWIDGETP (object) ? Qt : Qnil;
802 DEFUN ("xwidget-view-p",
803 Fxwidget_view_p, Sxwidget_view_p,
804 1, 1, 0,
805 doc: /* Return t if OBJECT is an xwidget-view. */)
806 (Lisp_Object object)
808 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
811 DEFUN ("xwidget-info",
812 Fxwidget_info, Sxwidget_info,
813 1, 1, 0,
814 doc: /* Return XWIDGET properties in a vector.
815 Currently [TYPE TITLE WIDTH HEIGHT]. */)
816 (Lisp_Object xwidget)
818 CHECK_XWIDGET (xwidget);
819 struct xwidget *xw = XXWIDGET (xwidget);
820 return CALLN (Fvector, xw->type, xw->title,
821 make_natnum (xw->width), make_natnum (xw->height));
824 DEFUN ("xwidget-view-info",
825 Fxwidget_view_info, Sxwidget_view_info,
826 1, 1, 0,
827 doc: /* Return properties of XWIDGET-VIEW in a vector.
828 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
829 (Lisp_Object xwidget_view)
831 CHECK_XWIDGET_VIEW (xwidget_view);
832 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
833 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
834 make_number (xv->clip_right), make_number (xv->clip_bottom),
835 make_number (xv->clip_top), make_number (xv->clip_left));
838 DEFUN ("xwidget-view-model",
839 Fxwidget_view_model, Sxwidget_view_model,
840 1, 1, 0,
841 doc: /* Return the model associated with XWIDGET-VIEW. */)
842 (Lisp_Object xwidget_view)
844 CHECK_XWIDGET_VIEW (xwidget_view);
845 return XXWIDGET_VIEW (xwidget_view)->model;
848 DEFUN ("xwidget-view-window",
849 Fxwidget_view_window, Sxwidget_view_window,
850 1, 1, 0,
851 doc: /* Return the window of XWIDGET-VIEW. */)
852 (Lisp_Object xwidget_view)
854 CHECK_XWIDGET_VIEW (xwidget_view);
855 return XXWIDGET_VIEW (xwidget_view)->w;
859 DEFUN ("delete-xwidget-view",
860 Fdelete_xwidget_view, Sdelete_xwidget_view,
861 1, 1, 0,
862 doc: /* Delete the XWIDGET-VIEW. */)
863 (Lisp_Object xwidget_view)
865 CHECK_XWIDGET_VIEW (xwidget_view);
866 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
867 gtk_widget_destroy (xv->widgetwindow);
868 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
869 /* xv->model still has signals pointing to the view. There can be
870 several views. Find the matching signals and delete them all. */
871 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
872 G_SIGNAL_MATCH_DATA,
873 0, 0, 0, 0,
874 xv->widget);
875 return Qnil;
878 DEFUN ("xwidget-view-lookup",
879 Fxwidget_view_lookup, Sxwidget_view_lookup,
880 1, 2, 0,
881 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
882 If WINDOW is unspecified or nil, use the selected window.
883 Return nil if no association is found. */)
884 (Lisp_Object xwidget, Lisp_Object window)
886 CHECK_XWIDGET (xwidget);
888 if (NILP (window))
889 window = Fselected_window ();
890 CHECK_WINDOW (window);
892 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
893 tail = XCDR (tail))
895 Lisp_Object xwidget_view = XCAR (tail);
896 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
897 && EQ (Fxwidget_view_window (xwidget_view), window))
898 return xwidget_view;
901 return Qnil;
904 DEFUN ("xwidget-plist",
905 Fxwidget_plist, Sxwidget_plist,
906 1, 1, 0,
907 doc: /* Return the plist of XWIDGET. */)
908 (Lisp_Object xwidget)
910 CHECK_XWIDGET (xwidget);
911 return XXWIDGET (xwidget)->plist;
914 DEFUN ("xwidget-buffer",
915 Fxwidget_buffer, Sxwidget_buffer,
916 1, 1, 0,
917 doc: /* Return the buffer of XWIDGET. */)
918 (Lisp_Object xwidget)
920 CHECK_XWIDGET (xwidget);
921 return XXWIDGET (xwidget)->buffer;
924 DEFUN ("set-xwidget-plist",
925 Fset_xwidget_plist, Sset_xwidget_plist,
926 2, 2, 0,
927 doc: /* Replace the plist of XWIDGET with PLIST.
928 Returns PLIST. */)
929 (Lisp_Object xwidget, Lisp_Object plist)
931 CHECK_XWIDGET (xwidget);
932 CHECK_LIST (plist);
934 XXWIDGET (xwidget)->plist = plist;
935 return plist;
938 DEFUN ("set-xwidget-query-on-exit-flag",
939 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
940 2, 2, 0,
941 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
942 If the second argument FLAG is non-nil, Emacs will query the user before
943 exiting or killing a buffer if XWIDGET is running.
944 This function returns FLAG. */)
945 (Lisp_Object xwidget, Lisp_Object flag)
947 CHECK_XWIDGET (xwidget);
948 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
949 return flag;
952 DEFUN ("xwidget-query-on-exit-flag",
953 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
954 1, 1, 0,
955 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
956 (Lisp_Object xwidget)
958 CHECK_XWIDGET (xwidget);
959 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
962 void
963 syms_of_xwidget (void)
965 defsubr (&Smake_xwidget);
966 defsubr (&Sxwidgetp);
967 DEFSYM (Qxwidgetp, "xwidgetp");
968 defsubr (&Sxwidget_view_p);
969 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
970 defsubr (&Sxwidget_info);
971 defsubr (&Sxwidget_view_info);
972 defsubr (&Sxwidget_resize);
973 defsubr (&Sget_buffer_xwidgets);
974 defsubr (&Sxwidget_view_model);
975 defsubr (&Sxwidget_view_window);
976 defsubr (&Sxwidget_view_lookup);
977 defsubr (&Sxwidget_query_on_exit_flag);
978 defsubr (&Sset_xwidget_query_on_exit_flag);
980 defsubr (&Sxwidget_webkit_goto_uri);
981 defsubr (&Sxwidget_webkit_zoom);
982 defsubr (&Sxwidget_webkit_execute_script);
983 DEFSYM (Qwebkit, "webkit");
985 defsubr (&Sxwidget_size_request);
986 defsubr (&Sdelete_xwidget_view);
988 defsubr (&Sxwidget_plist);
989 defsubr (&Sxwidget_buffer);
990 defsubr (&Sset_xwidget_plist);
992 DEFSYM (Qxwidget, "xwidget");
994 DEFSYM (QCxwidget, ":xwidget");
995 DEFSYM (QCtitle, ":title");
997 /* Do not forget to update the docstring of make-xwidget if you add
998 new types. */
1000 DEFSYM (Qvertical, "vertical");
1001 DEFSYM (Qhorizontal, "horizontal");
1003 DEFSYM (QCplist, ":plist");
1005 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1006 doc: /* xwidgets list. */);
1007 Vxwidget_list = Qnil;
1009 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1010 doc: /* xwidget views list. */);
1011 Vxwidget_view_list = Qnil;
1013 Fprovide (intern ("xwidget-internal"), Qnil);
1017 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1018 valid xwidget specification is a list whose car is the symbol
1019 `xwidget', and whose rest is a property list. The property list must
1020 contain a value for key `:type'. That value must be the name of a
1021 supported xwidget type. The rest of the property list depends on the
1022 xwidget type. */
1024 bool
1025 valid_xwidget_spec_p (Lisp_Object object)
1027 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1031 /* Find a value associated with key in spec. */
1032 static Lisp_Object
1033 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1035 Lisp_Object tail;
1037 eassert (valid_xwidget_spec_p (spec));
1039 for (tail = XCDR (spec);
1040 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1042 if (EQ (XCAR (tail), key))
1043 return XCAR (XCDR (tail));
1046 return Qnil;
1050 void
1051 xwidget_view_delete_all_in_window (struct window *w)
1053 struct xwidget_view *xv = NULL;
1054 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1055 tail = XCDR (tail))
1057 if (XWIDGET_VIEW_P (XCAR (tail)))
1059 xv = XXWIDGET_VIEW (XCAR (tail));
1060 if (XWINDOW (xv->w) == w)
1062 Fdelete_xwidget_view (XCAR (tail));
1068 static struct xwidget_view *
1069 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1071 Lisp_Object xwidget, window, ret;
1072 XSETXWIDGET (xwidget, xw);
1073 XSETWINDOW (window, w);
1075 ret = Fxwidget_view_lookup (xwidget, window);
1077 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1080 struct xwidget *
1081 lookup_xwidget (Lisp_Object spec)
1083 /* When a xwidget lisp spec is found initialize the C struct that is
1084 used in the C code. This is done by redisplay so values change
1085 if the spec changes. So, take special care of one-shot events. */
1086 Lisp_Object value;
1087 struct xwidget *xw;
1089 value = xwidget_spec_value (spec, QCxwidget);
1090 xw = XXWIDGET (value);
1092 return xw;
1095 /* Set up detection of touched xwidget. */
1096 static void
1097 xwidget_start_redisplay (void)
1099 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1100 tail = XCDR (tail))
1102 if (XWIDGET_VIEW_P (XCAR (tail)))
1103 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1107 /* The xwidget was touched during redisplay, so it isn't a candidate
1108 for hiding. */
1109 static void
1110 xwidget_touch (struct xwidget_view *xv)
1112 xv->redisplayed = true;
1115 static bool
1116 xwidget_touched (struct xwidget_view *xv)
1118 return xv->redisplayed;
1121 /* Redisplay has ended, now we should hide untouched xwidgets. */
1122 void
1123 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1125 int i;
1126 int area;
1128 xwidget_start_redisplay ();
1129 /* Iterate desired glyph matrix of window here, hide gtk widgets
1130 not in the desired matrix.
1132 This only takes care of xwidgets in active windows. If a window
1133 goes away from the screen, xwidget views must be deleted.
1135 dump_glyph_matrix (matrix, 2); */
1136 for (i = 0; i < matrix->nrows; ++i)
1138 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1139 struct glyph_row *row;
1140 row = MATRIX_ROW (matrix, i);
1141 if (row->enabled_p)
1142 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1144 struct glyph *glyph = row->glyphs[area];
1145 struct glyph *glyph_end = glyph + row->used[area];
1146 for (; glyph < glyph_end; ++glyph)
1147 if (glyph->type == XWIDGET_GLYPH)
1149 /* The only call to xwidget_end_redisplay is in dispnew.
1150 xwidget_end_redisplay (w->current_matrix); */
1151 struct xwidget_view *xv
1152 = xwidget_view_lookup (glyph->u.xwidget, w);
1153 /* FIXME: Is it safe to assume xwidget_view_lookup
1154 always succeeds here? If so, this comment can be removed.
1155 If not, the code probably needs fixing. */
1156 eassume (xv);
1157 xwidget_touch (xv);
1162 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1163 tail = XCDR (tail))
1165 if (XWIDGET_VIEW_P (XCAR (tail)))
1167 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1169 /* "touched" is only meaningful for the current window, so
1170 disregard other views. */
1171 if (XWINDOW (xv->w) == w)
1173 if (xwidget_touched (xv))
1174 xwidget_show_view (xv);
1175 else
1176 xwidget_hide_view (xv);
1182 /* Kill all xwidget in BUFFER. */
1183 void
1184 kill_buffer_xwidgets (Lisp_Object buffer)
1186 Lisp_Object tail, xwidget;
1187 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1189 xwidget = XCAR (tail);
1190 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1191 /* TODO free the GTK things in xw. */
1193 CHECK_XWIDGET (xwidget);
1194 struct xwidget *xw = XXWIDGET (xwidget);
1195 if (xw->widget_osr && xw->widgetwindow_osr)
1197 gtk_widget_destroy (xw->widget_osr);
1198 gtk_widget_destroy (xw->widgetwindow_osr);