; doc/emacs/misc.texi (Network Security): Fix typo.
[emacs.git] / src / xwidget.c
blob2a53966ef43b47fd2697bbc2ec509d1ffa73e706
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 arg)
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);
373 ptrdiff_t script_idx = (intptr_t) arg;
374 Lisp_Object script_callback = AREF (xw->script_callbacks, script_idx);
375 ASET (xw->script_callbacks, script_idx, Qnil);
376 if (!NILP (script_callback))
377 xfree (xmint_pointer (XCAR (script_callback)));
379 js_result = webkit_web_view_run_javascript_finish
380 (WEBKIT_WEB_VIEW (webview), result, &error);
382 if (!js_result)
384 g_warning ("Error running javascript: %s", error->message);
385 g_error_free (error);
386 return;
389 if (!NILP (script_callback) && !NILP (XCDR (script_callback)))
391 context = webkit_javascript_result_get_global_context (js_result);
392 value = webkit_javascript_result_get_value (js_result);
393 Lisp_Object lisp_value = webkit_js_to_lisp (context, value);
395 /* Register an xwidget event here, which then runs the callback.
396 This ensures that the callback runs in sync with the Emacs
397 event loop. */
398 store_xwidget_js_callback_event (xw, XCDR (script_callback), lisp_value);
401 webkit_javascript_result_unref (js_result);
405 gboolean
406 webkit_download_cb (WebKitWebContext *webkitwebcontext,
407 WebKitDownload *arg1,
408 gpointer data)
410 WebKitWebView *view = webkit_download_get_web_view(arg1);
411 WebKitURIRequest *request = webkit_download_get_request(arg1);
412 struct xwidget *xw = g_object_get_data (G_OBJECT (view),
413 XG_XWIDGET);
415 store_xwidget_event_string (xw, "download-started",
416 webkit_uri_request_get_uri(request));
417 return FALSE;
420 static gboolean
421 webkit_decide_policy_cb (WebKitWebView *webView,
422 WebKitPolicyDecision *decision,
423 WebKitPolicyDecisionType type,
424 gpointer user_data)
426 switch (type) {
427 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
428 /* This function makes webkit send a download signal for all unknown
429 mime types. TODO: Defer the decision to Lisp, so that it's
430 possible to make Emacs handle mime text for instance. */
432 WebKitResponsePolicyDecision *response =
433 WEBKIT_RESPONSE_POLICY_DECISION (decision);
434 if (!webkit_response_policy_decision_is_mime_type_supported (response))
436 webkit_policy_decision_download (decision);
437 return TRUE;
439 else
440 return FALSE;
441 break;
443 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
444 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
446 WebKitNavigationPolicyDecision *navigation_decision =
447 WEBKIT_NAVIGATION_POLICY_DECISION (decision);
448 WebKitNavigationAction *navigation_action =
449 webkit_navigation_policy_decision_get_navigation_action (navigation_decision);
450 WebKitURIRequest *request =
451 webkit_navigation_action_get_request (navigation_action);
453 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
454 store_xwidget_event_string (xw, "decide-policy",
455 webkit_uri_request_get_uri (request));
456 return FALSE;
457 break;
459 default:
460 return FALSE;
465 /* For gtk3 offscreen rendered widgets. */
466 static gboolean
467 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
469 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
470 struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget),
471 XG_XWIDGET_VIEW);
473 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
474 cairo_clip (cr);
476 gtk_widget_draw (xw->widget_osr, cr);
477 return FALSE;
480 static gboolean
481 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
482 gpointer user_data)
484 /* Copy events that arrive at the outer widget to the offscreen widget. */
485 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
486 GdkEvent *eventcopy = gdk_event_copy (event);
487 eventcopy->any.window = gtk_widget_get_window (xw->widget_osr);
489 /* TODO: This might leak events. They should be deallocated later,
490 perhaps in xwgir_event_cb. */
491 gtk_main_do_event (eventcopy);
493 /* Don't propagate this event further. */
494 return TRUE;
497 static gboolean
498 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
499 gpointer data)
501 struct xwidget_view *xv = data;
502 struct xwidget *xww = XXWIDGET (xv->model);
503 gdk_offscreen_window_set_embedder (gtk_widget_get_window
504 (xww->widgetwindow_osr),
505 gtk_widget_get_window (xv->widget));
506 return FALSE;
510 /* Initializes and does initial placement of an xwidget view on screen. */
511 static struct xwidget_view *
512 xwidget_init_view (struct xwidget *xww,
513 struct glyph_string *s,
514 int x, int y)
516 struct xwidget_view *xv = allocate_xwidget_view ();
517 Lisp_Object val;
519 XSETXWIDGET_VIEW (val, xv);
520 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
522 XSETWINDOW (xv->w, s->w);
523 XSETXWIDGET (xv->model, xww);
525 if (EQ (xww->type, Qwebkit))
527 xv->widget = gtk_drawing_area_new ();
528 /* Expose event handling. */
529 gtk_widget_set_app_paintable (xv->widget, TRUE);
530 gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK);
532 /* Draw the view on damage-event. */
533 g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event",
534 G_CALLBACK (offscreen_damage_event), xv->widget);
536 if (EQ (xww->type, Qwebkit))
538 g_signal_connect (G_OBJECT (xv->widget), "button-press-event",
539 G_CALLBACK (xwidget_osr_event_forward), NULL);
540 g_signal_connect (G_OBJECT (xv->widget), "button-release-event",
541 G_CALLBACK (xwidget_osr_event_forward), NULL);
542 g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event",
543 G_CALLBACK (xwidget_osr_event_forward), NULL);
545 else
547 /* xwgir debug, orthogonal to forwarding. */
548 g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event",
549 G_CALLBACK (xwidget_osr_event_set_embedder), xv);
551 g_signal_connect (G_OBJECT (xv->widget), "draw",
552 G_CALLBACK (xwidget_osr_draw_cb), NULL);
555 /* Widget realization.
557 Make container widget first, and put the actual widget inside the
558 container later. Drawing should crop container window if necessary
559 to handle case where xwidget is partially obscured by other Emacs
560 windows. Other containers than gtk_fixed where explored, but
561 gtk_fixed had the most predictable behavior so far. */
563 xv->emacswindow = FRAME_GTK_WIDGET (s->f);
564 xv->widgetwindow = gtk_fixed_new ();
565 gtk_widget_set_has_window (xv->widgetwindow, TRUE);
566 gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget);
568 /* Store some xwidget data in the gtk widgets. */
569 g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f);
570 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww);
571 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv);
572 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww);
573 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv);
575 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width,
576 xww->height);
577 gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height);
578 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y);
579 xv->x = x;
580 xv->y = y;
581 gtk_widget_show_all (xv->widgetwindow);
583 return xv;
586 void
587 x_draw_xwidget_glyph_string (struct glyph_string *s)
589 /* This method is called by the redisplay engine and places the
590 xwidget on screen. Moving and clipping is done here. Also view
591 initialization. */
592 struct xwidget *xww = s->xwidget;
593 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
594 int clip_right;
595 int clip_bottom;
596 int clip_top;
597 int clip_left;
599 int x = s->x;
600 int y = s->y + (s->height / 2) - (xww->height / 2);
602 /* Do initialization here in the display loop because there is no
603 other time to know things like window placement etc. Do not
604 create a new view if we have found one that is usable. */
605 if (!xv)
606 xv = xwidget_init_view (xww, s, x, y);
608 int text_area_x, text_area_y, text_area_width, text_area_height;
610 window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y,
611 &text_area_width, &text_area_height);
612 clip_left = max (0, text_area_x - x);
613 clip_right = max (clip_left,
614 min (xww->width, text_area_x + text_area_width - x));
615 clip_top = max (0, text_area_y - y);
616 clip_bottom = max (clip_top,
617 min (xww->height, text_area_y + text_area_height - y));
619 /* We are concerned with movement of the onscreen area. The area
620 might sit still when the widget actually moves. This happens
621 when an Emacs window border moves across a widget window. So, if
622 any corner of the outer widget clipping window moves, that counts
623 as movement here, even if it looks like no movement happens
624 because the widget sits still inside the clipping area. The
625 widget can also move inside the clipping area, which happens
626 later. */
627 bool moved = (xv->x + xv->clip_left != x + clip_left
628 || xv->y + xv->clip_top != y + clip_top);
629 xv->x = x;
630 xv->y = y;
632 /* Has it moved? */
633 if (moved)
634 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
635 xv->widgetwindow, x + clip_left, y + clip_top);
637 /* Clip the widget window if some parts happen to be outside
638 drawable area. An Emacs window is not a gtk window. A gtk window
639 covers the entire frame. Clipping might have changed even if we
640 haven't actually moved; try to figure out when we need to reclip
641 for real. */
642 if (xv->clip_right != clip_right
643 || xv->clip_bottom != clip_bottom
644 || xv->clip_top != clip_top || xv->clip_left != clip_left)
646 gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left,
647 clip_bottom - clip_top);
648 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
649 -clip_top);
651 xv->clip_right = clip_right;
652 xv->clip_bottom = clip_bottom;
653 xv->clip_top = clip_top;
654 xv->clip_left = clip_left;
657 /* If emacs wants to repaint the area where the widget lives, queue
658 a redraw. It seems its possible to get out of sync with emacs
659 redraws so emacs background sometimes shows up instead of the
660 xwidgets background. It's just a visual glitch though. */
661 if (!xwidget_hidden (xv))
663 gtk_widget_queue_draw (xv->widgetwindow);
664 gtk_widget_queue_draw (xv->widget);
668 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
669 #define WEBKIT_FN_INIT() \
670 CHECK_XWIDGET (xwidget); \
671 struct xwidget *xw = XXWIDGET (xwidget); \
672 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
674 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
675 return Qnil; \
678 DEFUN ("xwidget-webkit-goto-uri",
679 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
680 2, 2, 0,
681 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
682 (Lisp_Object xwidget, Lisp_Object uri)
684 WEBKIT_FN_INIT ();
685 CHECK_STRING (uri);
686 uri = ENCODE_FILE (uri);
687 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
688 return Qnil;
691 DEFUN ("xwidget-webkit-zoom",
692 Fxwidget_webkit_zoom, Sxwidget_webkit_zoom,
693 2, 2, 0,
694 doc: /* Change the zoom factor of the xwidget webkit instance referenced by XWIDGET. */)
695 (Lisp_Object xwidget, Lisp_Object factor)
697 WEBKIT_FN_INIT ();
698 if (FLOATP (factor))
700 double zoom_change = XFLOAT_DATA (factor);
701 webkit_web_view_set_zoom_level
702 (WEBKIT_WEB_VIEW (xw->widget_osr),
703 webkit_web_view_get_zoom_level
704 (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change);
706 return Qnil;
709 /* Save script and fun in the script/callback save vector and return
710 its index. */
711 static ptrdiff_t
712 save_script_callback (struct xwidget *xw, Lisp_Object script, Lisp_Object fun)
714 Lisp_Object cbs = xw->script_callbacks;
715 if (NILP (cbs))
716 xw->script_callbacks = cbs = Fmake_vector (make_number (32), Qnil);
718 /* Find first free index. */
719 ptrdiff_t idx;
720 for (idx = 0; !NILP (AREF (cbs, idx)); idx++)
721 if (idx + 1 == ASIZE (cbs))
723 xw->script_callbacks = cbs = larger_vector (cbs, 1, -1);
724 break;
727 ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun));
728 return idx;
731 DEFUN ("xwidget-webkit-execute-script",
732 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
733 2, 3, 0,
734 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT.
735 If FUN is provided, feed the JavaScript return value to the single
736 argument procedure FUN.*/)
737 (Lisp_Object xwidget, Lisp_Object script, Lisp_Object fun)
739 WEBKIT_FN_INIT ();
740 CHECK_STRING (script);
741 if (!NILP (fun) && !FUNCTIONP (fun))
742 wrong_type_argument (Qinvalid_function, fun);
744 script = ENCODE_SYSTEM (script);
746 /* Protect script and fun during GC. */
747 intptr_t idx = save_script_callback (xw, script, fun);
749 /* JavaScript execution happens asynchronously. If an elisp
750 callback function is provided we pass it to the C callback
751 procedure that retrieves the return value. */
752 gchar *script_string
753 = xmint_pointer (XCAR (AREF (xw->script_callbacks, idx)));
754 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw->widget_osr),
755 script_string,
756 NULL, /* cancelable */
757 webkit_javascript_finished_cb,
758 (gpointer) idx);
759 return Qnil;
762 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
763 doc: /* Resize XWIDGET to NEW_WIDTH, NEW_HEIGHT. */ )
764 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
766 CHECK_XWIDGET (xwidget);
767 CHECK_RANGED_INTEGER (new_width, 0, INT_MAX);
768 CHECK_RANGED_INTEGER (new_height, 0, INT_MAX);
769 struct xwidget *xw = XXWIDGET (xwidget);
770 int w = XFASTINT (new_width);
771 int h = XFASTINT (new_height);
773 xw->width = w;
774 xw->height = h;
776 /* If there is an offscreen widget resize it first. */
777 if (xw->widget_osr)
779 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
780 xw->height);
781 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
782 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
783 xw->height);
786 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
788 if (XWIDGET_VIEW_P (XCAR (tail)))
790 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
791 if (XXWIDGET (xv->model) == xw)
792 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
793 xw->height);
797 return Qnil;
803 DEFUN ("xwidget-size-request",
804 Fxwidget_size_request, Sxwidget_size_request,
805 1, 1, 0,
806 doc: /* Return the desired size of the XWIDGET.
807 This can be used to read the xwidget desired size, and resizes the
808 Emacs allocated area accordingly. */)
809 (Lisp_Object xwidget)
811 CHECK_XWIDGET (xwidget);
812 GtkRequisition requisition;
813 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
814 return list2 (make_number (requisition.width),
815 make_number (requisition.height));
818 DEFUN ("xwidgetp",
819 Fxwidgetp, Sxwidgetp,
820 1, 1, 0,
821 doc: /* Return t if OBJECT is an xwidget. */)
822 (Lisp_Object object)
824 return XWIDGETP (object) ? Qt : Qnil;
827 DEFUN ("xwidget-view-p",
828 Fxwidget_view_p, Sxwidget_view_p,
829 1, 1, 0,
830 doc: /* Return t if OBJECT is an xwidget-view. */)
831 (Lisp_Object object)
833 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
836 DEFUN ("xwidget-info",
837 Fxwidget_info, Sxwidget_info,
838 1, 1, 0,
839 doc: /* Return XWIDGET properties in a vector.
840 Currently [TYPE TITLE WIDTH HEIGHT]. */)
841 (Lisp_Object xwidget)
843 CHECK_XWIDGET (xwidget);
844 struct xwidget *xw = XXWIDGET (xwidget);
845 return CALLN (Fvector, xw->type, xw->title,
846 make_natnum (xw->width), make_natnum (xw->height));
849 DEFUN ("xwidget-view-info",
850 Fxwidget_view_info, Sxwidget_view_info,
851 1, 1, 0,
852 doc: /* Return properties of XWIDGET-VIEW in a vector.
853 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
854 (Lisp_Object xwidget_view)
856 CHECK_XWIDGET_VIEW (xwidget_view);
857 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
858 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
859 make_number (xv->clip_right), make_number (xv->clip_bottom),
860 make_number (xv->clip_top), make_number (xv->clip_left));
863 DEFUN ("xwidget-view-model",
864 Fxwidget_view_model, Sxwidget_view_model,
865 1, 1, 0,
866 doc: /* Return the model associated with XWIDGET-VIEW. */)
867 (Lisp_Object xwidget_view)
869 CHECK_XWIDGET_VIEW (xwidget_view);
870 return XXWIDGET_VIEW (xwidget_view)->model;
873 DEFUN ("xwidget-view-window",
874 Fxwidget_view_window, Sxwidget_view_window,
875 1, 1, 0,
876 doc: /* Return the window of XWIDGET-VIEW. */)
877 (Lisp_Object xwidget_view)
879 CHECK_XWIDGET_VIEW (xwidget_view);
880 return XXWIDGET_VIEW (xwidget_view)->w;
884 DEFUN ("delete-xwidget-view",
885 Fdelete_xwidget_view, Sdelete_xwidget_view,
886 1, 1, 0,
887 doc: /* Delete the XWIDGET-VIEW. */)
888 (Lisp_Object xwidget_view)
890 CHECK_XWIDGET_VIEW (xwidget_view);
891 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
892 gtk_widget_destroy (xv->widgetwindow);
893 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
894 /* xv->model still has signals pointing to the view. There can be
895 several views. Find the matching signals and delete them all. */
896 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
897 G_SIGNAL_MATCH_DATA,
898 0, 0, 0, 0,
899 xv->widget);
900 return Qnil;
903 DEFUN ("xwidget-view-lookup",
904 Fxwidget_view_lookup, Sxwidget_view_lookup,
905 1, 2, 0,
906 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
907 If WINDOW is unspecified or nil, use the selected window.
908 Return nil if no association is found. */)
909 (Lisp_Object xwidget, Lisp_Object window)
911 CHECK_XWIDGET (xwidget);
913 if (NILP (window))
914 window = Fselected_window ();
915 CHECK_WINDOW (window);
917 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
918 tail = XCDR (tail))
920 Lisp_Object xwidget_view = XCAR (tail);
921 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
922 && EQ (Fxwidget_view_window (xwidget_view), window))
923 return xwidget_view;
926 return Qnil;
929 DEFUN ("xwidget-plist",
930 Fxwidget_plist, Sxwidget_plist,
931 1, 1, 0,
932 doc: /* Return the plist of XWIDGET. */)
933 (Lisp_Object xwidget)
935 CHECK_XWIDGET (xwidget);
936 return XXWIDGET (xwidget)->plist;
939 DEFUN ("xwidget-buffer",
940 Fxwidget_buffer, Sxwidget_buffer,
941 1, 1, 0,
942 doc: /* Return the buffer of XWIDGET. */)
943 (Lisp_Object xwidget)
945 CHECK_XWIDGET (xwidget);
946 return XXWIDGET (xwidget)->buffer;
949 DEFUN ("set-xwidget-plist",
950 Fset_xwidget_plist, Sset_xwidget_plist,
951 2, 2, 0,
952 doc: /* Replace the plist of XWIDGET with PLIST.
953 Returns PLIST. */)
954 (Lisp_Object xwidget, Lisp_Object plist)
956 CHECK_XWIDGET (xwidget);
957 CHECK_LIST (plist);
959 XXWIDGET (xwidget)->plist = plist;
960 return plist;
963 DEFUN ("set-xwidget-query-on-exit-flag",
964 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
965 2, 2, 0,
966 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
967 If the second argument FLAG is non-nil, Emacs will query the user before
968 exiting or killing a buffer if XWIDGET is running.
969 This function returns FLAG. */)
970 (Lisp_Object xwidget, Lisp_Object flag)
972 CHECK_XWIDGET (xwidget);
973 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
974 return flag;
977 DEFUN ("xwidget-query-on-exit-flag",
978 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
979 1, 1, 0,
980 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
981 (Lisp_Object xwidget)
983 CHECK_XWIDGET (xwidget);
984 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
987 void
988 syms_of_xwidget (void)
990 defsubr (&Smake_xwidget);
991 defsubr (&Sxwidgetp);
992 DEFSYM (Qxwidgetp, "xwidgetp");
993 defsubr (&Sxwidget_view_p);
994 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
995 defsubr (&Sxwidget_info);
996 defsubr (&Sxwidget_view_info);
997 defsubr (&Sxwidget_resize);
998 defsubr (&Sget_buffer_xwidgets);
999 defsubr (&Sxwidget_view_model);
1000 defsubr (&Sxwidget_view_window);
1001 defsubr (&Sxwidget_view_lookup);
1002 defsubr (&Sxwidget_query_on_exit_flag);
1003 defsubr (&Sset_xwidget_query_on_exit_flag);
1005 defsubr (&Sxwidget_webkit_goto_uri);
1006 defsubr (&Sxwidget_webkit_zoom);
1007 defsubr (&Sxwidget_webkit_execute_script);
1008 DEFSYM (Qwebkit, "webkit");
1010 defsubr (&Sxwidget_size_request);
1011 defsubr (&Sdelete_xwidget_view);
1013 defsubr (&Sxwidget_plist);
1014 defsubr (&Sxwidget_buffer);
1015 defsubr (&Sset_xwidget_plist);
1017 DEFSYM (Qxwidget, "xwidget");
1019 DEFSYM (QCxwidget, ":xwidget");
1020 DEFSYM (QCtitle, ":title");
1022 /* Do not forget to update the docstring of make-xwidget if you add
1023 new types. */
1025 DEFSYM (Qvertical, "vertical");
1026 DEFSYM (Qhorizontal, "horizontal");
1028 DEFSYM (QCplist, ":plist");
1030 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1031 doc: /* xwidgets list. */);
1032 Vxwidget_list = Qnil;
1034 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1035 doc: /* xwidget views list. */);
1036 Vxwidget_view_list = Qnil;
1038 Fprovide (intern ("xwidget-internal"), Qnil);
1042 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1043 valid xwidget specification is a list whose car is the symbol
1044 `xwidget', and whose rest is a property list. The property list must
1045 contain a value for key `:type'. That value must be the name of a
1046 supported xwidget type. The rest of the property list depends on the
1047 xwidget type. */
1049 bool
1050 valid_xwidget_spec_p (Lisp_Object object)
1052 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1056 /* Find a value associated with key in spec. */
1057 static Lisp_Object
1058 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1060 Lisp_Object tail;
1062 eassert (valid_xwidget_spec_p (spec));
1064 for (tail = XCDR (spec);
1065 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1067 if (EQ (XCAR (tail), key))
1068 return XCAR (XCDR (tail));
1071 return Qnil;
1075 void
1076 xwidget_view_delete_all_in_window (struct window *w)
1078 struct xwidget_view *xv = NULL;
1079 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1080 tail = XCDR (tail))
1082 if (XWIDGET_VIEW_P (XCAR (tail)))
1084 xv = XXWIDGET_VIEW (XCAR (tail));
1085 if (XWINDOW (xv->w) == w)
1087 Fdelete_xwidget_view (XCAR (tail));
1093 static struct xwidget_view *
1094 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1096 Lisp_Object xwidget, window, ret;
1097 XSETXWIDGET (xwidget, xw);
1098 XSETWINDOW (window, w);
1100 ret = Fxwidget_view_lookup (xwidget, window);
1102 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1105 struct xwidget *
1106 lookup_xwidget (Lisp_Object spec)
1108 /* When a xwidget lisp spec is found initialize the C struct that is
1109 used in the C code. This is done by redisplay so values change
1110 if the spec changes. So, take special care of one-shot events. */
1111 Lisp_Object value;
1112 struct xwidget *xw;
1114 value = xwidget_spec_value (spec, QCxwidget);
1115 xw = XXWIDGET (value);
1117 return xw;
1120 /* Set up detection of touched xwidget. */
1121 static void
1122 xwidget_start_redisplay (void)
1124 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1125 tail = XCDR (tail))
1127 if (XWIDGET_VIEW_P (XCAR (tail)))
1128 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1132 /* The xwidget was touched during redisplay, so it isn't a candidate
1133 for hiding. */
1134 static void
1135 xwidget_touch (struct xwidget_view *xv)
1137 xv->redisplayed = true;
1140 static bool
1141 xwidget_touched (struct xwidget_view *xv)
1143 return xv->redisplayed;
1146 /* Redisplay has ended, now we should hide untouched xwidgets. */
1147 void
1148 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1150 int i;
1151 int area;
1153 xwidget_start_redisplay ();
1154 /* Iterate desired glyph matrix of window here, hide gtk widgets
1155 not in the desired matrix.
1157 This only takes care of xwidgets in active windows. If a window
1158 goes away from the screen, xwidget views must be deleted.
1160 dump_glyph_matrix (matrix, 2); */
1161 for (i = 0; i < matrix->nrows; ++i)
1163 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1164 struct glyph_row *row;
1165 row = MATRIX_ROW (matrix, i);
1166 if (row->enabled_p)
1167 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1169 struct glyph *glyph = row->glyphs[area];
1170 struct glyph *glyph_end = glyph + row->used[area];
1171 for (; glyph < glyph_end; ++glyph)
1172 if (glyph->type == XWIDGET_GLYPH)
1174 /* The only call to xwidget_end_redisplay is in dispnew.
1175 xwidget_end_redisplay (w->current_matrix); */
1176 struct xwidget_view *xv
1177 = xwidget_view_lookup (glyph->u.xwidget, w);
1178 /* FIXME: Is it safe to assume xwidget_view_lookup
1179 always succeeds here? If so, this comment can be removed.
1180 If not, the code probably needs fixing. */
1181 eassume (xv);
1182 xwidget_touch (xv);
1187 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1188 tail = XCDR (tail))
1190 if (XWIDGET_VIEW_P (XCAR (tail)))
1192 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1194 /* "touched" is only meaningful for the current window, so
1195 disregard other views. */
1196 if (XWINDOW (xv->w) == w)
1198 if (xwidget_touched (xv))
1199 xwidget_show_view (xv);
1200 else
1201 xwidget_hide_view (xv);
1207 /* Kill all xwidget in BUFFER. */
1208 void
1209 kill_buffer_xwidgets (Lisp_Object buffer)
1211 Lisp_Object tail, xwidget;
1212 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1214 xwidget = XCAR (tail);
1215 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1216 /* TODO free the GTK things in xw. */
1218 CHECK_XWIDGET (xwidget);
1219 struct xwidget *xw = XXWIDGET (xwidget);
1220 if (xw->widget_osr && xw->widgetwindow_osr)
1222 gtk_widget_destroy (xw->widget_osr);
1223 gtk_widget_destroy (xw->widgetwindow_osr);
1225 if (!NILP (xw->script_callbacks))
1226 for (ptrdiff_t idx = 0; idx < ASIZE (xw->script_callbacks); idx++)
1228 Lisp_Object cb = AREF (xw->script_callbacks, idx);
1229 if (!NILP (cb))
1230 xfree (xmint_pointer (XCAR (cb)));
1231 ASET (xw->script_callbacks, idx, Qnil);