; Merge from origin/emacs-25
[emacs.git] / src / xwidget.c
blobe6de5da8e694cb75e598522c56462946868ea986
1 /* Support for embedding graphical components in a buffer.
3 Copyright (C) 2011-2017 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/>. */
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, XIL ((intptr_t) lisp_callback),
396 lisp_value);
400 gboolean
401 webkit_download_cb (WebKitWebContext *webkitwebcontext,
402 WebKitDownload *arg1,
403 gpointer data)
405 WebKitWebView *view = webkit_download_get_web_view(arg1);
406 WebKitURIRequest *request = webkit_download_get_request(arg1);
407 struct xwidget *xw = g_object_get_data (G_OBJECT (view),
408 XG_XWIDGET);
410 store_xwidget_event_string (xw, "download-started",
411 webkit_uri_request_get_uri(request));
412 return FALSE;
415 static gboolean
416 webkit_decide_policy_cb (WebKitWebView *webView,
417 WebKitPolicyDecision *decision,
418 WebKitPolicyDecisionType type,
419 gpointer user_data)
421 switch (type) {
422 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
423 /* This function makes webkit send a download signal for all unknown
424 mime types. TODO: Defer the decision to Lisp, so that it's
425 possible to make Emacs handle mime text for instance. */
427 WebKitResponsePolicyDecision *response =
428 WEBKIT_RESPONSE_POLICY_DECISION (decision);
429 if (!webkit_response_policy_decision_is_mime_type_supported (response))
431 webkit_policy_decision_download (decision);
432 return TRUE;
434 else
435 return FALSE;
436 break;
438 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
439 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
441 WebKitNavigationPolicyDecision *navigation_decision =
442 WEBKIT_NAVIGATION_POLICY_DECISION (decision);
443 WebKitNavigationAction *navigation_action =
444 webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
445 WebKitURIRequest *request =
446 webkit_navigation_action_get_request (navigation_action);
448 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
449 store_xwidget_event_string (xw, "decide-policy",
450 webkit_uri_request_get_uri (request));
451 return FALSE;
452 break;
454 default:
455 return FALSE;
460 /* For gtk3 offscreen rendered widgets. */
461 static gboolean
462 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
464 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
465 struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget),
466 XG_XWIDGET_VIEW);
468 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
469 cairo_clip (cr);
471 gtk_widget_draw (xw->widget_osr, cr);
472 return FALSE;
475 static gboolean
476 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
477 gpointer user_data)
479 /* Copy events that arrive at the outer widget to the offscreen widget. */
480 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
481 GdkEvent *eventcopy = gdk_event_copy (event);
482 eventcopy->any.window = gtk_widget_get_window (xw->widget_osr);
484 /* TODO: This might leak events. They should be deallocated later,
485 perhaps in xwgir_event_cb. */
486 gtk_main_do_event (eventcopy);
488 /* Don't propagate this event further. */
489 return TRUE;
492 static gboolean
493 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
494 gpointer data)
496 struct xwidget_view *xv = data;
497 struct xwidget *xww = XXWIDGET (xv->model);
498 gdk_offscreen_window_set_embedder (gtk_widget_get_window
499 (xww->widgetwindow_osr),
500 gtk_widget_get_window (xv->widget));
501 return FALSE;
505 /* Initializes and does initial placement of an xwidget view on screen. */
506 static struct xwidget_view *
507 xwidget_init_view (struct xwidget *xww,
508 struct glyph_string *s,
509 int x, int y)
511 struct xwidget_view *xv = allocate_xwidget_view ();
512 Lisp_Object val;
514 XSETXWIDGET_VIEW (val, xv);
515 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
517 XSETWINDOW (xv->w, s->w);
518 XSETXWIDGET (xv->model, xww);
520 if (EQ (xww->type, Qwebkit))
522 xv->widget = gtk_drawing_area_new ();
523 /* Expose event handling. */
524 gtk_widget_set_app_paintable (xv->widget, TRUE);
525 gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK);
527 /* Draw the view on damage-event. */
528 g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event",
529 G_CALLBACK (offscreen_damage_event), xv->widget);
531 if (EQ (xww->type, Qwebkit))
533 g_signal_connect (G_OBJECT (xv->widget), "button-press-event",
534 G_CALLBACK (xwidget_osr_event_forward), NULL);
535 g_signal_connect (G_OBJECT (xv->widget), "button-release-event",
536 G_CALLBACK (xwidget_osr_event_forward), NULL);
537 g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event",
538 G_CALLBACK (xwidget_osr_event_forward), NULL);
540 else
542 /* xwgir debug, orthogonal to forwarding. */
543 g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event",
544 G_CALLBACK (xwidget_osr_event_set_embedder), xv);
546 g_signal_connect (G_OBJECT (xv->widget), "draw",
547 G_CALLBACK (xwidget_osr_draw_cb), NULL);
550 /* Widget realization.
552 Make container widget first, and put the actual widget inside the
553 container later. Drawing should crop container window if necessary
554 to handle case where xwidget is partially obscured by other Emacs
555 windows. Other containers than gtk_fixed where explored, but
556 gtk_fixed had the most predictable behavior so far. */
558 xv->emacswindow = FRAME_GTK_WIDGET (s->f);
559 xv->widgetwindow = gtk_fixed_new ();
560 gtk_widget_set_has_window (xv->widgetwindow, TRUE);
561 gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget);
563 /* Store some xwidget data in the gtk widgets. */
564 g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f);
565 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww);
566 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv);
567 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww);
568 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv);
570 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width,
571 xww->height);
572 gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height);
573 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y);
574 xv->x = x;
575 xv->y = y;
576 gtk_widget_show_all (xv->widgetwindow);
578 return xv;
581 void
582 x_draw_xwidget_glyph_string (struct glyph_string *s)
584 /* This method is called by the redisplay engine and places the
585 xwidget on screen. Moving and clipping is done here. Also view
586 initialization. */
587 struct xwidget *xww = s->xwidget;
588 struct xwidget_view *xv;
589 int clip_right;
590 int clip_bottom;
591 int clip_top;
592 int clip_left;
594 /* FIXME: The result of this call is discarded.
595 What if the lookup fails? */
596 xwidget_view_lookup (xww, s->w);
598 int x = s->x;
599 int y = s->y + (s->height / 2) - (xww->height / 2);
601 /* Do initialization here in the display loop because there is no
602 other time to know things like window placement etc. */
603 xv = xwidget_init_view (xww, s, x, y);
605 int text_area_x, text_area_y, text_area_width, text_area_height;
607 window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y,
608 &text_area_width, &text_area_height);
609 clip_left = max (0, text_area_x - x);
610 clip_right = max (clip_left,
611 min (xww->width, text_area_x + text_area_width - x));
612 clip_top = max (0, text_area_y - y);
613 clip_bottom = max (clip_top,
614 min (xww->height, text_area_y + text_area_height - y));
616 /* We are concerned with movement of the onscreen area. The area
617 might sit still when the widget actually moves. This happens
618 when an Emacs window border moves across a widget window. So, if
619 any corner of the outer widget clipping window moves, that counts
620 as movement here, even if it looks like no movement happens
621 because the widget sits still inside the clipping area. The
622 widget can also move inside the clipping area, which happens
623 later. */
624 bool moved = (xv->x + xv->clip_left != x + clip_left
625 || xv->y + xv->clip_top != y + clip_top);
626 xv->x = x;
627 xv->y = y;
629 /* Has it moved? */
630 if (moved)
631 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
632 xv->widgetwindow, x + clip_left, y + clip_top);
634 /* Clip the widget window if some parts happen to be outside
635 drawable area. An Emacs window is not a gtk window. A gtk window
636 covers the entire frame. Clipping might have changed even if we
637 haven't actually moved; try to figure out when we need to reclip
638 for real. */
639 if (xv->clip_right != clip_right
640 || xv->clip_bottom != clip_bottom
641 || xv->clip_top != clip_top || xv->clip_left != clip_left)
643 gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left,
644 clip_bottom - clip_top);
645 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
646 -clip_top);
648 xv->clip_right = clip_right;
649 xv->clip_bottom = clip_bottom;
650 xv->clip_top = clip_top;
651 xv->clip_left = clip_left;
654 /* If emacs wants to repaint the area where the widget lives, queue
655 a redraw. It seems its possible to get out of sync with emacs
656 redraws so emacs background sometimes shows up instead of the
657 xwidgets background. It's just a visual glitch though. */
658 if (!xwidget_hidden (xv))
660 gtk_widget_queue_draw (xv->widgetwindow);
661 gtk_widget_queue_draw (xv->widget);
665 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
666 #define WEBKIT_FN_INIT() \
667 CHECK_XWIDGET (xwidget); \
668 struct xwidget *xw = XXWIDGET (xwidget); \
669 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
671 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
672 return Qnil; \
675 DEFUN ("xwidget-webkit-goto-uri",
676 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
677 2, 2, 0,
678 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
679 (Lisp_Object xwidget, Lisp_Object uri)
681 WEBKIT_FN_INIT ();
682 CHECK_STRING (uri);
683 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
684 return Qnil;
687 DEFUN ("xwidget-webkit-zoom",
688 Fxwidget_webkit_zoom, Sxwidget_webkit_zoom,
689 2, 2, 0,
690 doc: /* Change the zoom factor of the xwidget webkit instance
691 referenced by XWIDGET. */)
692 (Lisp_Object xwidget, Lisp_Object factor)
694 WEBKIT_FN_INIT ();
695 if (FLOATP (factor))
697 double zoom_change = XFLOAT_DATA (factor);
698 webkit_web_view_set_zoom_level
699 (WEBKIT_WEB_VIEW (xw->widget_osr),
700 webkit_web_view_get_zoom_level
701 (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change);
703 return Qnil;
707 DEFUN ("xwidget-webkit-execute-script",
708 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
709 2, 3, 0,
710 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. If
711 FUN is provided, feed the JavaScript return value to the single
712 argument procedure FUN.*/)
713 (Lisp_Object xwidget, Lisp_Object script, Lisp_Object fun)
715 WEBKIT_FN_INIT ();
716 CHECK_STRING (script);
717 if (!NILP (fun) && !FUNCTIONP (fun))
718 wrong_type_argument (Qinvalid_function, fun);
720 GAsyncReadyCallback callback
721 = FUNCTIONP (fun) ? webkit_javascript_finished_cb : NULL;
723 /* FIXME: The following hack assumes USE_LSB_TAG. */
724 verify (USE_LSB_TAG);
725 /* FIXME: This hack might lead to disaster if FUN is garbage
726 collected before store_xwidget_js_callback_event makes it visible
727 to Lisp again. See the FIXME in webkit_javascript_finished_cb. */
728 gpointer callback_arg = (gpointer) (intptr_t) XLI (fun);
730 /* JavaScript execution happens asynchronously. If an elisp
731 callback function is provided we pass it to the C callback
732 procedure that retrieves the return value. */
733 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw->widget_osr),
734 SSDATA (script),
735 NULL, /* cancelable */
736 callback, callback_arg);
737 return Qnil;
740 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
741 doc: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
742 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
744 CHECK_XWIDGET (xwidget);
745 CHECK_RANGED_INTEGER (new_width, 0, INT_MAX);
746 CHECK_RANGED_INTEGER (new_height, 0, INT_MAX);
747 struct xwidget *xw = XXWIDGET (xwidget);
748 int w = XFASTINT (new_width);
749 int h = XFASTINT (new_height);
751 xw->width = w;
752 xw->height = h;
754 /* If there is an offscreen widget resize it first. */
755 if (xw->widget_osr)
757 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
758 xw->height);
759 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
760 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
761 xw->height);
764 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
766 if (XWIDGET_VIEW_P (XCAR (tail)))
768 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
769 if (XXWIDGET (xv->model) == xw)
770 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
771 xw->height);
775 return Qnil;
781 DEFUN ("xwidget-size-request",
782 Fxwidget_size_request, Sxwidget_size_request,
783 1, 1, 0,
784 doc: /* Return the desired size of the XWIDGET.
785 This can be used to read the xwidget desired size, and resizes the
786 Emacs allocated area accordingly. */)
787 (Lisp_Object xwidget)
789 CHECK_XWIDGET (xwidget);
790 GtkRequisition requisition;
791 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
792 return list2 (make_number (requisition.width),
793 make_number (requisition.height));
796 DEFUN ("xwidgetp",
797 Fxwidgetp, Sxwidgetp,
798 1, 1, 0,
799 doc: /* Return t if OBJECT is an xwidget. */)
800 (Lisp_Object object)
802 return XWIDGETP (object) ? Qt : Qnil;
805 DEFUN ("xwidget-view-p",
806 Fxwidget_view_p, Sxwidget_view_p,
807 1, 1, 0,
808 doc: /* Return t if OBJECT is an xwidget-view. */)
809 (Lisp_Object object)
811 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
814 DEFUN ("xwidget-info",
815 Fxwidget_info, Sxwidget_info,
816 1, 1, 0,
817 doc: /* Return XWIDGET properties in a vector.
818 Currently [TYPE TITLE WIDTH HEIGHT]. */)
819 (Lisp_Object xwidget)
821 CHECK_XWIDGET (xwidget);
822 struct xwidget *xw = XXWIDGET (xwidget);
823 return CALLN (Fvector, xw->type, xw->title,
824 make_natnum (xw->width), make_natnum (xw->height));
827 DEFUN ("xwidget-view-info",
828 Fxwidget_view_info, Sxwidget_view_info,
829 1, 1, 0,
830 doc: /* Return properties of XWIDGET-VIEW in a vector.
831 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
832 (Lisp_Object xwidget_view)
834 CHECK_XWIDGET_VIEW (xwidget_view);
835 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
836 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
837 make_number (xv->clip_right), make_number (xv->clip_bottom),
838 make_number (xv->clip_top), make_number (xv->clip_left));
841 DEFUN ("xwidget-view-model",
842 Fxwidget_view_model, Sxwidget_view_model,
843 1, 1, 0,
844 doc: /* Return the model associated with XWIDGET-VIEW. */)
845 (Lisp_Object xwidget_view)
847 CHECK_XWIDGET_VIEW (xwidget_view);
848 return XXWIDGET_VIEW (xwidget_view)->model;
851 DEFUN ("xwidget-view-window",
852 Fxwidget_view_window, Sxwidget_view_window,
853 1, 1, 0,
854 doc: /* Return the window of XWIDGET-VIEW. */)
855 (Lisp_Object xwidget_view)
857 CHECK_XWIDGET_VIEW (xwidget_view);
858 return XXWIDGET_VIEW (xwidget_view)->w;
862 DEFUN ("delete-xwidget-view",
863 Fdelete_xwidget_view, Sdelete_xwidget_view,
864 1, 1, 0,
865 doc: /* Delete the XWIDGET-VIEW. */)
866 (Lisp_Object xwidget_view)
868 CHECK_XWIDGET_VIEW (xwidget_view);
869 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
870 gtk_widget_destroy (xv->widgetwindow);
871 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
872 /* xv->model still has signals pointing to the view. There can be
873 several views. Find the matching signals and delete them all. */
874 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
875 G_SIGNAL_MATCH_DATA,
876 0, 0, 0, 0,
877 xv->widget);
878 return Qnil;
881 DEFUN ("xwidget-view-lookup",
882 Fxwidget_view_lookup, Sxwidget_view_lookup,
883 1, 2, 0,
884 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
885 If WINDOW is unspecified or nil, use the selected window.
886 Return nil if no association is found. */)
887 (Lisp_Object xwidget, Lisp_Object window)
889 CHECK_XWIDGET (xwidget);
891 if (NILP (window))
892 window = Fselected_window ();
893 CHECK_WINDOW (window);
895 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
896 tail = XCDR (tail))
898 Lisp_Object xwidget_view = XCAR (tail);
899 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
900 && EQ (Fxwidget_view_window (xwidget_view), window))
901 return xwidget_view;
904 return Qnil;
907 DEFUN ("xwidget-plist",
908 Fxwidget_plist, Sxwidget_plist,
909 1, 1, 0,
910 doc: /* Return the plist of XWIDGET. */)
911 (Lisp_Object xwidget)
913 CHECK_XWIDGET (xwidget);
914 return XXWIDGET (xwidget)->plist;
917 DEFUN ("xwidget-buffer",
918 Fxwidget_buffer, Sxwidget_buffer,
919 1, 1, 0,
920 doc: /* Return the buffer of XWIDGET. */)
921 (Lisp_Object xwidget)
923 CHECK_XWIDGET (xwidget);
924 return XXWIDGET (xwidget)->buffer;
927 DEFUN ("set-xwidget-plist",
928 Fset_xwidget_plist, Sset_xwidget_plist,
929 2, 2, 0,
930 doc: /* Replace the plist of XWIDGET with PLIST.
931 Returns PLIST. */)
932 (Lisp_Object xwidget, Lisp_Object plist)
934 CHECK_XWIDGET (xwidget);
935 CHECK_LIST (plist);
937 XXWIDGET (xwidget)->plist = plist;
938 return plist;
941 DEFUN ("set-xwidget-query-on-exit-flag",
942 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
943 2, 2, 0,
944 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
945 If the second argument FLAG is non-nil, Emacs will query the user before
946 exiting or killing a buffer if XWIDGET is running.
947 This function returns FLAG. */)
948 (Lisp_Object xwidget, Lisp_Object flag)
950 CHECK_XWIDGET (xwidget);
951 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
952 return flag;
955 DEFUN ("xwidget-query-on-exit-flag",
956 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
957 1, 1, 0,
958 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
959 (Lisp_Object xwidget)
961 CHECK_XWIDGET (xwidget);
962 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
965 void
966 syms_of_xwidget (void)
968 defsubr (&Smake_xwidget);
969 defsubr (&Sxwidgetp);
970 DEFSYM (Qxwidgetp, "xwidgetp");
971 defsubr (&Sxwidget_view_p);
972 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
973 defsubr (&Sxwidget_info);
974 defsubr (&Sxwidget_view_info);
975 defsubr (&Sxwidget_resize);
976 defsubr (&Sget_buffer_xwidgets);
977 defsubr (&Sxwidget_view_model);
978 defsubr (&Sxwidget_view_window);
979 defsubr (&Sxwidget_view_lookup);
980 defsubr (&Sxwidget_query_on_exit_flag);
981 defsubr (&Sset_xwidget_query_on_exit_flag);
983 defsubr (&Sxwidget_webkit_goto_uri);
984 defsubr (&Sxwidget_webkit_zoom);
985 defsubr (&Sxwidget_webkit_execute_script);
986 DEFSYM (Qwebkit, "webkit");
988 defsubr (&Sxwidget_size_request);
989 defsubr (&Sdelete_xwidget_view);
991 defsubr (&Sxwidget_plist);
992 defsubr (&Sxwidget_buffer);
993 defsubr (&Sset_xwidget_plist);
995 DEFSYM (Qxwidget, "xwidget");
997 DEFSYM (QCxwidget, ":xwidget");
998 DEFSYM (QCtitle, ":title");
1000 /* Do not forget to update the docstring of make-xwidget if you add
1001 new types. */
1003 DEFSYM (Qvertical, "vertical");
1004 DEFSYM (Qhorizontal, "horizontal");
1006 DEFSYM (QCplist, ":plist");
1008 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1009 doc: /* xwidgets list. */);
1010 Vxwidget_list = Qnil;
1012 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1013 doc: /* xwidget views list. */);
1014 Vxwidget_view_list = Qnil;
1016 Fprovide (intern ("xwidget-internal"), Qnil);
1020 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1021 valid xwidget specification is a list whose car is the symbol
1022 `xwidget', and whose rest is a property list. The property list must
1023 contain a value for key `:type'. That value must be the name of a
1024 supported xwidget type. The rest of the property list depends on the
1025 xwidget type. */
1027 bool
1028 valid_xwidget_spec_p (Lisp_Object object)
1030 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1034 /* Find a value associated with key in spec. */
1035 static Lisp_Object
1036 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1038 Lisp_Object tail;
1040 eassert (valid_xwidget_spec_p (spec));
1042 for (tail = XCDR (spec);
1043 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1045 if (EQ (XCAR (tail), key))
1046 return XCAR (XCDR (tail));
1049 return Qnil;
1053 void
1054 xwidget_view_delete_all_in_window (struct window *w)
1056 struct xwidget_view *xv = NULL;
1057 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1058 tail = XCDR (tail))
1060 if (XWIDGET_VIEW_P (XCAR (tail)))
1062 xv = XXWIDGET_VIEW (XCAR (tail));
1063 if (XWINDOW (xv->w) == w)
1065 Fdelete_xwidget_view (XCAR (tail));
1071 static struct xwidget_view *
1072 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1074 Lisp_Object xwidget, window, ret;
1075 XSETXWIDGET (xwidget, xw);
1076 XSETWINDOW (window, w);
1078 ret = Fxwidget_view_lookup (xwidget, window);
1080 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1083 struct xwidget *
1084 lookup_xwidget (Lisp_Object spec)
1086 /* When a xwidget lisp spec is found initialize the C struct that is
1087 used in the C code. This is done by redisplay so values change
1088 if the spec changes. So, take special care of one-shot events. */
1089 Lisp_Object value;
1090 struct xwidget *xw;
1092 value = xwidget_spec_value (spec, QCxwidget);
1093 xw = XXWIDGET (value);
1095 return xw;
1098 /* Set up detection of touched xwidget. */
1099 static void
1100 xwidget_start_redisplay (void)
1102 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1103 tail = XCDR (tail))
1105 if (XWIDGET_VIEW_P (XCAR (tail)))
1106 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1110 /* The xwidget was touched during redisplay, so it isn't a candidate
1111 for hiding. */
1112 static void
1113 xwidget_touch (struct xwidget_view *xv)
1115 xv->redisplayed = true;
1118 static bool
1119 xwidget_touched (struct xwidget_view *xv)
1121 return xv->redisplayed;
1124 /* Redisplay has ended, now we should hide untouched xwidgets. */
1125 void
1126 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1128 int i;
1129 int area;
1131 xwidget_start_redisplay ();
1132 /* Iterate desired glyph matrix of window here, hide gtk widgets
1133 not in the desired matrix.
1135 This only takes care of xwidgets in active windows. If a window
1136 goes away from the screen, xwidget views must be deleted.
1138 dump_glyph_matrix (matrix, 2); */
1139 for (i = 0; i < matrix->nrows; ++i)
1141 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1142 struct glyph_row *row;
1143 row = MATRIX_ROW (matrix, i);
1144 if (row->enabled_p)
1145 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1147 struct glyph *glyph = row->glyphs[area];
1148 struct glyph *glyph_end = glyph + row->used[area];
1149 for (; glyph < glyph_end; ++glyph)
1150 if (glyph->type == XWIDGET_GLYPH)
1152 /* The only call to xwidget_end_redisplay is in dispnew.
1153 xwidget_end_redisplay (w->current_matrix); */
1154 struct xwidget_view *xv
1155 = xwidget_view_lookup (glyph->u.xwidget, w);
1156 /* FIXME: Is it safe to assume xwidget_view_lookup
1157 always succeeds here? If so, this comment can be removed.
1158 If not, the code probably needs fixing. */
1159 eassume (xv);
1160 xwidget_touch (xv);
1165 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1166 tail = XCDR (tail))
1168 if (XWIDGET_VIEW_P (XCAR (tail)))
1170 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1172 /* "touched" is only meaningful for the current window, so
1173 disregard other views. */
1174 if (XWINDOW (xv->w) == w)
1176 if (xwidget_touched (xv))
1177 xwidget_show_view (xv);
1178 else
1179 xwidget_hide_view (xv);
1185 /* Kill all xwidget in BUFFER. */
1186 void
1187 kill_buffer_xwidgets (Lisp_Object buffer)
1189 Lisp_Object tail, xwidget;
1190 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1192 xwidget = XCAR (tail);
1193 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1194 /* TODO free the GTK things in xw. */
1196 CHECK_XWIDGET (xwidget);
1197 struct xwidget *xw = XXWIDGET (xwidget);
1198 if (xw->widget_osr && xw->widgetwindow_osr)
1200 gtk_widget_destroy (xw->widget_osr);
1201 gtk_widget_destroy (xw->widgetwindow_osr);