Port xref-tests to master branch
[emacs.git] / src / xwidget.c
blob82449f7a2152ab27a44f976807c00e0ce67526a9
1 /* Support for embedding graphical components in a buffer.
3 Copyright (C) 2011-2016 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #include "xwidget.h"
24 #include <signal.h>
26 #include <stdio.h>
27 #include <setjmp.h>
28 #ifdef HAVE_X_WINDOWS
30 #include "lisp.h"
31 #include "blockinput.h"
32 #include "syssignal.h"
34 #include "xterm.h"
35 #include <X11/cursorfont.h>
37 #ifndef makedev
38 # include <sys/types.h>
39 #endif
41 #ifdef BSD_SYSTEM
42 # include <sys/ioctl.h>
43 #endif
45 #include "systime.h"
47 #ifndef INCLUDED_FCNTL
48 # include <fcntl.h>
49 #endif
50 #include <ctype.h>
51 #include <errno.h>
52 #include <setjmp.h>
53 #include <sys/stat.h>
55 #include "charset.h"
56 #include "character.h"
57 #include "coding.h"
58 #include "ccl.h"
59 #include "frame.h"
60 #include "dispextern.h"
61 #include "fontset.h"
62 #include "termhooks.h"
63 #include "termopts.h"
64 #include "termchar.h"
65 #include "disptab.h"
66 #include "buffer.h"
67 #include "window.h"
68 #include "keyboard.h"
69 #include "intervals.h"
70 #include "process.h"
71 #include "atimer.h"
72 #include "keymap.h"
75 #ifdef USE_X_TOOLKIT
76 #include <X11/Shell.h>
77 #endif
78 #include <X11/extensions/Xcomposite.h>
79 #include <X11/extensions/Xrender.h>
80 #include <cairo.h>
81 #ifdef HAVE_SYS_TIME_H
82 #include <sys/time.h>
83 #endif
84 #ifdef HAVE_UNISTD_H
85 #include <unistd.h>
86 #endif
88 #include "gtkutil.h"
89 #include "font.h"
90 #endif /* HAVE_X_WINDOWS */
92 #include <gtk/gtk.h>
93 #include <gdk/gdk.h>
95 #include <gtk/gtkx.h>
97 #include "emacsgtkfixed.h"
99 #include <wchar.h>
101 #include <webkit/webkitwebview.h>
102 #include <webkit/webkitwebplugindatabase.h>
103 #include <webkit/webkitwebplugin.h>
104 #include <webkit/webkitglobals.h>
105 #include <webkit/webkitwebnavigationaction.h>
106 #include <webkit/webkitdownload.h>
107 #include <webkit/webkitwebpolicydecision.h>
109 static struct xwidget *
110 allocate_xwidget (void)
112 return ALLOCATE_PSEUDOVECTOR (struct xwidget, height, PVEC_XWIDGET);
115 static struct xwidget_view *
116 allocate_xwidget_view (void)
118 return ALLOCATE_PSEUDOVECTOR (struct xwidget_view, redisplayed,
119 PVEC_XWIDGET_VIEW);
122 #define XSETXWIDGET(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET)
123 #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET_VIEW)
125 static struct xwidget_view *xwidget_view_lookup (struct xwidget *,
126 struct window *);
127 static void webkit_document_load_finished_cb (WebKitWebView *, WebKitWebFrame *,
128 gpointer);
129 static gboolean webkit_download_cb (WebKitWebView *, WebKitDownload *, gpointer);
131 static gboolean
132 webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *,
133 WebKitWebFrame *,
134 WebKitNetworkRequest *,
135 gchar *,
136 WebKitWebPolicyDecision *,
137 gpointer);
139 static gboolean
140 webkit_new_window_policy_decision_requested_cb (WebKitWebView *,
141 WebKitWebFrame *,
142 WebKitNetworkRequest *,
143 WebKitWebNavigationAction *,
144 WebKitWebPolicyDecision *,
145 gpointer);
147 static gboolean
148 webkit_navigation_policy_decision_requested_cb (WebKitWebView *,
149 WebKitWebFrame *,
150 WebKitNetworkRequest *,
151 WebKitWebNavigationAction *,
152 WebKitWebPolicyDecision *,
153 gpointer);
157 DEFUN ("make-xwidget",
158 Fmake_xwidget, Smake_xwidget,
159 5, 6, 0,
160 doc: /* Make an xwidget of TYPE.
161 If BUFFER is nil, use the current buffer.
162 If BUFFER is a string and no such buffer exists, create it.
163 TYPE is a symbol which can take one of the following values:
165 - webkit
167 Returns the newly constructed xwidget, or nil if construction fails. */)
168 (Lisp_Object type,
169 Lisp_Object title, Lisp_Object width, Lisp_Object height,
170 Lisp_Object arguments, Lisp_Object buffer)
172 CHECK_SYMBOL (type);
173 CHECK_NATNUM (width);
174 CHECK_NATNUM (height);
176 struct xwidget *xw = allocate_xwidget ();
177 Lisp_Object val;
178 xw->type = type;
179 xw->title = title;
180 xw->buffer = NILP (buffer) ? Fcurrent_buffer () : Fget_buffer_create (buffer);
181 xw->height = XFASTINT (height);
182 xw->width = XFASTINT (width);
183 xw->kill_without_query = false;
184 XSETXWIDGET (val, xw);
185 Vxwidget_list = Fcons (val, Vxwidget_list);
186 xw->widgetwindow_osr = NULL;
187 xw->widget_osr = NULL;
188 xw->plist = Qnil;
190 if (EQ (xw->type, Qwebkit))
192 block_input ();
193 xw->widgetwindow_osr = gtk_offscreen_window_new ();
194 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
195 xw->height);
197 /* WebKit OSR is the only scrolled component at the moment. */
198 xw->widgetscrolledwindow_osr = NULL;
200 if (EQ (xw->type, Qwebkit))
202 xw->widgetscrolledwindow_osr = gtk_scrolled_window_new (NULL, NULL);
203 gtk_scrolled_window_set_min_content_height
204 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
205 xw->height);
206 gtk_scrolled_window_set_min_content_width
207 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
208 xw->width);
209 gtk_scrolled_window_set_policy
210 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
211 GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
213 xw->widget_osr = webkit_web_view_new ();
214 gtk_container_add (GTK_CONTAINER (xw->widgetscrolledwindow_osr),
215 GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr)));
218 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
219 xw->height);
221 if (EQ (xw->type, Qwebkit))
223 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
224 xw->widgetscrolledwindow_osr);
226 else
228 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
229 xw->widget_osr);
232 gtk_widget_show (xw->widget_osr);
233 gtk_widget_show (xw->widgetwindow_osr);
234 gtk_widget_show (xw->widgetscrolledwindow_osr);
236 /* Store some xwidget data in the gtk widgets for convenient
237 retrieval in the event handlers. */
238 g_object_set_data (G_OBJECT (xw->widget_osr), XG_XWIDGET, xw);
239 g_object_set_data (G_OBJECT (xw->widgetwindow_osr), XG_XWIDGET, xw);
241 /* signals */
242 if (EQ (xw->type, Qwebkit))
244 g_signal_connect (G_OBJECT (xw->widget_osr),
245 "document-load-finished",
246 G_CALLBACK (webkit_document_load_finished_cb), xw);
248 g_signal_connect (G_OBJECT (xw->widget_osr),
249 "download-requested",
250 G_CALLBACK (webkit_download_cb), xw);
252 g_signal_connect (G_OBJECT (xw->widget_osr),
253 "mime-type-policy-decision-requested",
254 G_CALLBACK
255 (webkit_mime_type_policy_typedecision_requested_cb),
256 xw);
258 g_signal_connect (G_OBJECT (xw->widget_osr),
259 "new-window-policy-decision-requested",
260 G_CALLBACK
261 (webkit_new_window_policy_decision_requested_cb),
262 xw);
264 g_signal_connect (G_OBJECT (xw->widget_osr),
265 "navigation-policy-decision-requested",
266 G_CALLBACK
267 (webkit_navigation_policy_decision_requested_cb),
268 xw);
271 unblock_input ();
274 return val;
277 DEFUN ("get-buffer-xwidgets", Fget_buffer_xwidgets, Sget_buffer_xwidgets,
278 1, 1, 0,
279 doc: /* Return a list of xwidgets associated with BUFFER.
280 BUFFER may be a buffer or the name of one. */)
281 (Lisp_Object buffer)
283 Lisp_Object xw, tail, xw_list;
285 if (NILP (buffer))
286 return Qnil;
287 buffer = Fget_buffer (buffer);
288 if (NILP (buffer))
289 return Qnil;
291 xw_list = Qnil;
293 for (tail = Vxwidget_list; CONSP (tail); tail = XCDR (tail))
295 xw = XCAR (tail);
296 if (XWIDGETP (xw) && EQ (Fxwidget_buffer (xw), buffer))
297 xw_list = Fcons (xw, xw_list);
299 return xw_list;
302 static bool
303 xwidget_hidden (struct xwidget_view *xv)
305 return xv->hidden;
308 static void
309 xwidget_show_view (struct xwidget_view *xv)
311 xv->hidden = false;
312 gtk_widget_show (xv->widgetwindow);
313 gtk_fixed_move (GTK_FIXED (xv->emacswindow),
314 xv->widgetwindow,
315 xv->x + xv->clip_left,
316 xv->y + xv->clip_top);
319 /* Hide an xwidget view. */
320 static void
321 xwidget_hide_view (struct xwidget_view *xv)
323 xv->hidden = true;
324 gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow,
325 10000, 10000);
328 /* When the off-screen webkit master view changes this signal is called.
329 It copies the bitmap from the off-screen instance. */
330 static gboolean
331 offscreen_damage_event (GtkWidget *widget, GdkEvent *event,
332 gpointer xv_widget)
334 /* Queue a redraw of onscreen widget.
335 There is a guard against receiving an invalid widget,
336 which should only happen if we failed to remove the
337 specific signal handler for the damage event. */
338 if (GTK_IS_WIDGET (xv_widget))
339 gtk_widget_queue_draw (GTK_WIDGET (xv_widget));
340 else
341 printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
342 xv_widget);
344 return FALSE;
347 static void
348 store_xwidget_event_string (struct xwidget *xw, const char *eventname,
349 const char *eventstr)
351 struct input_event event;
352 Lisp_Object xwl;
353 XSETXWIDGET (xwl, xw);
354 EVENT_INIT (event);
355 event.kind = XWIDGET_EVENT;
356 event.frame_or_window = Qnil;
357 event.arg = list3 (intern (eventname), xwl, build_string (eventstr));
358 kbd_buffer_store_event (&event);
361 /* TODO deprecated, use load-status. */
362 void
363 webkit_document_load_finished_cb (WebKitWebView *webkitwebview,
364 WebKitWebFrame *arg1,
365 gpointer data)
367 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
368 XG_XWIDGET);
370 store_xwidget_event_string (xw, "document-load-finished", "");
373 gboolean
374 webkit_download_cb (WebKitWebView *webkitwebview,
375 WebKitDownload *arg1,
376 gpointer data)
378 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
379 XG_XWIDGET);
380 store_xwidget_event_string (xw, "download-requested",
381 webkit_download_get_uri (arg1));
382 return FALSE;
385 static gboolean
386 webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *webView,
387 WebKitWebFrame *frame,
388 WebKitNetworkRequest *request,
389 gchar *mimetype,
390 WebKitWebPolicyDecision *policy_decision,
391 gpointer user_data)
393 /* This function makes webkit send a download signal for all unknown
394 mime types. TODO: Defer the decision to Lisp, so that it's
395 possible to make Emacs handle mime text for instance. */
396 if (!webkit_web_view_can_show_mime_type (webView, mimetype))
398 webkit_web_policy_decision_download (policy_decision);
399 return TRUE;
401 else
402 return FALSE;
405 static gboolean
406 webkit_new_window_policy_decision_requested_cb (WebKitWebView *webView,
407 WebKitWebFrame *frame,
408 WebKitNetworkRequest *request,
409 WebKitWebNavigationAction *navigation_action,
410 WebKitWebPolicyDecision *policy_decision,
411 gpointer user_data)
413 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
414 webkit_web_navigation_action_get_original_uri (navigation_action);
416 store_xwidget_event_string (xw, "new-window-policy-decision-requested",
417 webkit_web_navigation_action_get_original_uri
418 (navigation_action));
419 return FALSE;
422 static gboolean
423 webkit_navigation_policy_decision_requested_cb (WebKitWebView *webView,
424 WebKitWebFrame *frame,
425 WebKitNetworkRequest *request,
426 WebKitWebNavigationAction *navigation_action,
427 WebKitWebPolicyDecision *policy_decision,
428 gpointer user_data)
430 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
431 store_xwidget_event_string (xw, "navigation-policy-decision-requested",
432 webkit_web_navigation_action_get_original_uri
433 (navigation_action));
434 return FALSE;
437 /* For gtk3 offscreen rendered widgets. */
438 static gboolean
439 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
441 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
442 struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget),
443 XG_XWIDGET_VIEW);
445 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
446 cairo_clip (cr);
448 if (xw->widgetscrolledwindow_osr != NULL)
449 gtk_widget_draw (xw->widgetscrolledwindow_osr, cr);
450 else
451 gtk_widget_draw (xw->widget_osr, cr);
452 return FALSE;
455 static gboolean
456 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
457 gpointer user_data)
459 /* Copy events that arrive at the outer widget to the offscreen widget. */
460 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
461 GdkEvent *eventcopy = gdk_event_copy (event);
462 eventcopy->any.window = gtk_widget_get_window (xw->widget_osr);
464 /* TODO: This might leak events. They should be deallocated later,
465 perhaps in xwgir_event_cb. */
466 gtk_main_do_event (eventcopy);
468 /* Don't propagate this event further. */
469 return TRUE;
472 static gboolean
473 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
474 gpointer data)
476 struct xwidget_view *xv = data;
477 struct xwidget *xww = XXWIDGET (xv->model);
478 gdk_offscreen_window_set_embedder (gtk_widget_get_window
479 (xww->widgetwindow_osr),
480 gtk_widget_get_window (xv->widget));
481 return FALSE;
485 /* Initializes and does initial placement of an xwidget view on screen. */
486 static struct xwidget_view *
487 xwidget_init_view (struct xwidget *xww,
488 struct glyph_string *s,
489 int x, int y)
491 struct xwidget_view *xv = allocate_xwidget_view ();
492 Lisp_Object val;
494 XSETXWIDGET_VIEW (val, xv);
495 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
497 XSETWINDOW (xv->w, s->w);
498 XSETXWIDGET (xv->model, xww);
500 if (EQ (xww->type, Qwebkit))
502 xv->widget = gtk_drawing_area_new ();
503 /* Expose event handling. */
504 gtk_widget_set_app_paintable (xv->widget, TRUE);
505 gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK);
507 /* Draw the view on damage-event. */
508 g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event",
509 G_CALLBACK (offscreen_damage_event), xv->widget);
511 if (EQ (xww->type, Qwebkit))
513 g_signal_connect (G_OBJECT (xv->widget), "button-press-event",
514 G_CALLBACK (xwidget_osr_event_forward), NULL);
515 g_signal_connect (G_OBJECT (xv->widget), "button-release-event",
516 G_CALLBACK (xwidget_osr_event_forward), NULL);
517 g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event",
518 G_CALLBACK (xwidget_osr_event_forward), NULL);
520 else
522 /* xwgir debug, orthogonal to forwarding. */
523 g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event",
524 G_CALLBACK (xwidget_osr_event_set_embedder), xv);
526 g_signal_connect (G_OBJECT (xv->widget), "draw",
527 G_CALLBACK (xwidget_osr_draw_cb), NULL);
530 /* Widget realization.
532 Make container widget first, and put the actual widget inside the
533 container later. Drawing should crop container window if necessary
534 to handle case where xwidget is partially obscured by other Emacs
535 windows. Other containers than gtk_fixed where explored, but
536 gtk_fixed had the most predictable behavior so far. */
538 xv->emacswindow = FRAME_GTK_WIDGET (s->f);
539 xv->widgetwindow = gtk_fixed_new ();
540 gtk_widget_set_has_window (xv->widgetwindow, TRUE);
541 gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget);
543 /* Store some xwidget data in the gtk widgets. */
544 g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f);
545 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww);
546 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv);
547 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww);
548 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv);
550 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width,
551 xww->height);
552 gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height);
553 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y);
554 xv->x = x;
555 xv->y = y;
556 gtk_widget_show_all (xv->widgetwindow);
558 return xv;
561 void
562 x_draw_xwidget_glyph_string (struct glyph_string *s)
564 /* This method is called by the redisplay engine and places the
565 xwidget on screen. Moving and clipping is done here. Also view
566 initialization. */
567 struct xwidget *xww = s->xwidget;
568 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
569 int clip_right;
570 int clip_bottom;
571 int clip_top;
572 int clip_left;
574 int x = s->x;
575 int y = s->y + (s->height / 2) - (xww->height / 2);
577 /* Do initialization here in the display loop because there is no
578 other time to know things like window placement etc. */
579 xv = xwidget_init_view (xww, s, x, y);
581 int text_area_x, text_area_y, text_area_width, text_area_height;
583 window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y,
584 &text_area_width, &text_area_height);
585 clip_left = max (0, text_area_x - x);
586 clip_right = max (clip_left,
587 min (xww->width, text_area_x + text_area_width - x));
588 clip_top = max (0, text_area_y - y);
589 clip_bottom = max (clip_top,
590 min (xww->height, text_area_y + text_area_height - y));
592 /* We are concerned with movement of the onscreen area. The area
593 might sit still when the widget actually moves. This happens
594 when an Emacs window border moves across a widget window. So, if
595 any corner of the outer widget clipping window moves, that counts
596 as movement here, even if it looks like no movement happens
597 because the widget sits still inside the clipping area. The
598 widget can also move inside the clipping area, which happens
599 later. */
600 bool moved = (xv->x + xv->clip_left != x + clip_left
601 || xv->y + xv->clip_top != y + clip_top);
602 xv->x = x;
603 xv->y = y;
605 /* Has it moved? */
606 if (moved)
607 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
608 xv->widgetwindow, x + clip_left, y + clip_top);
610 /* Clip the widget window if some parts happen to be outside
611 drawable area. An Emacs window is not a gtk window. A gtk window
612 covers the entire frame. Clipping might have changed even if we
613 haven't actually moved; try to figure out when we need to reclip
614 for real. */
615 if (xv->clip_right != clip_right
616 || xv->clip_bottom != clip_bottom
617 || xv->clip_top != clip_top || xv->clip_left != clip_left)
619 gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left,
620 clip_bottom - clip_top);
621 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
622 -clip_top);
624 xv->clip_right = clip_right;
625 xv->clip_bottom = clip_bottom;
626 xv->clip_top = clip_top;
627 xv->clip_left = clip_left;
630 /* If emacs wants to repaint the area where the widget lives, queue
631 a redraw. It seems its possible to get out of sync with emacs
632 redraws so emacs background sometimes shows up instead of the
633 xwidgets background. It's just a visual glitch though. */
634 if (!xwidget_hidden (xv))
636 gtk_widget_queue_draw (xv->widgetwindow);
637 gtk_widget_queue_draw (xv->widget);
641 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
642 #define WEBKIT_FN_INIT() \
643 CHECK_XWIDGET (xwidget); \
644 struct xwidget *xw = XXWIDGET (xwidget); \
645 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
647 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
648 return Qnil; \
651 DEFUN ("xwidget-webkit-goto-uri",
652 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
653 2, 2, 0,
654 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
655 (Lisp_Object xwidget, Lisp_Object uri)
657 WEBKIT_FN_INIT ();
658 CHECK_STRING (uri);
659 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
660 return Qnil;
664 DEFUN ("xwidget-webkit-execute-script",
665 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
666 2, 2, 0,
667 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. */)
668 (Lisp_Object xwidget, Lisp_Object script)
670 WEBKIT_FN_INIT ();
671 CHECK_STRING (script);
672 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (xw->widget_osr),
673 SSDATA (script));
674 return Qnil;
677 DEFUN ("xwidget-webkit-get-title",
678 Fxwidget_webkit_get_title, Sxwidget_webkit_get_title,
679 1, 1, 0,
680 doc: /* Return the title from the Webkit instance in XWIDGET.
681 This can be used to work around the lack of a return value from the
682 exec method. */ )
683 (Lisp_Object xwidget)
685 /* TODO support multibyte strings. */
686 WEBKIT_FN_INIT ();
687 const gchar *str =
688 webkit_web_view_get_title (WEBKIT_WEB_VIEW (xw->widget_osr));
689 if (str == 0)
691 /* TODO maybe return Qnil instead. I suppose webkit returns
692 null pointer when doc is not properly loaded or something. */
693 return build_string ("");
695 return build_string (str);
698 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
699 doc: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
700 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
702 CHECK_XWIDGET (xwidget);
703 CHECK_NATNUM (new_width);
704 CHECK_NATNUM (new_height);
705 struct xwidget *xw = XXWIDGET (xwidget);
706 int w = XFASTINT (new_width);
707 int h = XFASTINT (new_height);
709 xw->width = w;
710 xw->height = h;
712 /* If there is an offscreen widget resize it first. */
713 if (xw->widget_osr)
715 /* Use minimum size. */
716 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr),
717 xw->width, xw->height);
719 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
720 xw->height);
721 gtk_scrolled_window_set_min_content_height
722 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
723 xw->height);
724 gtk_scrolled_window_set_min_content_width
725 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
726 xw->width);
728 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
732 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
734 if (XWIDGET_VIEW_P (XCAR (tail)))
736 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
737 if (XXWIDGET (xv->model) == xw)
738 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
739 xw->height);
743 return Qnil;
748 DEFUN ("xwidget-set-adjustment",
749 Fxwidget_set_adjustment, Sxwidget_set_adjustment, 4, 4, 0,
750 doc: /* Set native scrolling for XWIDGET.
751 AXIS can be `vertical' or `horizontal'.
752 If RELATIVE is t, scroll relative, otherwise absolutely.
753 VALUE is the amount to scroll, either relatively or absolutely. */)
754 (Lisp_Object xwidget, Lisp_Object axis, Lisp_Object relative,
755 Lisp_Object value)
757 CHECK_XWIDGET (xwidget);
758 CHECK_NUMBER (value);
759 struct xwidget *xw = XXWIDGET (xwidget);
760 GtkAdjustment *adjustment
761 = ((EQ (Qhorizontal, axis)
762 ? gtk_scrolled_window_get_hadjustment
763 : gtk_scrolled_window_get_vadjustment)
764 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)));
765 double final_value = XINT (value);
766 if (EQ (Qt, relative))
767 final_value += gtk_adjustment_get_value (adjustment);
768 gtk_adjustment_set_value (adjustment, final_value);
769 return Qnil;
773 DEFUN ("xwidget-size-request",
774 Fxwidget_size_request, Sxwidget_size_request,
775 1, 1, 0,
776 doc: /* Return the desired size of the XWIDGET.
777 This can be used to read the xwidget desired size, and resizes the
778 Emacs allocated area accordingly. */)
779 (Lisp_Object xwidget)
781 CHECK_XWIDGET (xwidget);
782 GtkRequisition requisition;
783 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
784 return list2 (make_number (requisition.width),
785 make_number (requisition.height));
788 DEFUN ("xwidgetp",
789 Fxwidgetp, Sxwidgetp,
790 1, 1, 0,
791 doc: /* Return t if OBJECT is an xwidget. */)
792 (Lisp_Object object)
794 return XWIDGETP (object) ? Qt : Qnil;
797 DEFUN ("xwidget-view-p",
798 Fxwidget_view_p, Sxwidget_view_p,
799 1, 1, 0,
800 doc: /* Return t if OBJECT is an xwidget-view. */)
801 (Lisp_Object object)
803 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
806 DEFUN ("xwidget-info",
807 Fxwidget_info, Sxwidget_info,
808 1, 1, 0,
809 doc: /* Return XWIDGET properties in a vector.
810 Currently [TYPE TITLE WIDTH HEIGHT]. */)
811 (Lisp_Object xwidget)
813 CHECK_XWIDGET (xwidget);
814 struct xwidget *xw = XXWIDGET (xwidget);
815 return CALLN (Fvector, xw->type, xw->title,
816 make_natnum (xw->width), make_natnum (xw->height));
819 DEFUN ("xwidget-view-info",
820 Fxwidget_view_info, Sxwidget_view_info,
821 1, 1, 0,
822 doc: /* Return properties of XWIDGET-VIEW in a vector.
823 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
824 (Lisp_Object xwidget_view)
826 CHECK_XWIDGET_VIEW (xwidget_view);
827 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
828 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
829 make_number (xv->clip_right), make_number (xv->clip_bottom),
830 make_number (xv->clip_top), make_number (xv->clip_left));
833 DEFUN ("xwidget-view-model",
834 Fxwidget_view_model, Sxwidget_view_model,
835 1, 1, 0,
836 doc: /* Return the model associated with XWIDGET-VIEW. */)
837 (Lisp_Object xwidget_view)
839 CHECK_XWIDGET_VIEW (xwidget_view);
840 return XXWIDGET_VIEW (xwidget_view)->model;
843 DEFUN ("xwidget-view-window",
844 Fxwidget_view_window, Sxwidget_view_window,
845 1, 1, 0,
846 doc: /* Return the window of XWIDGET-VIEW. */)
847 (Lisp_Object xwidget_view)
849 CHECK_XWIDGET_VIEW (xwidget_view);
850 return XXWIDGET_VIEW (xwidget_view)->w;
854 DEFUN ("delete-xwidget-view",
855 Fdelete_xwidget_view, Sdelete_xwidget_view,
856 1, 1, 0,
857 doc: /* Delete the XWIDGET-VIEW. */)
858 (Lisp_Object xwidget_view)
860 CHECK_XWIDGET_VIEW (xwidget_view);
861 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
862 gtk_widget_destroy (xv->widgetwindow);
863 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
864 /* xv->model still has signals pointing to the view. There can be
865 several views. Find the matching signals and delete them all. */
866 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
867 G_SIGNAL_MATCH_DATA,
868 0, 0, 0, 0,
869 xv->widget);
870 return Qnil;
873 DEFUN ("xwidget-view-lookup",
874 Fxwidget_view_lookup, Sxwidget_view_lookup,
875 1, 2, 0,
876 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
877 If WINDOW is unspecified or nil, use the selected window.
878 Return nil if no association is found. */)
879 (Lisp_Object xwidget, Lisp_Object window)
881 CHECK_XWIDGET (xwidget);
883 if (NILP (window))
884 window = Fselected_window ();
885 CHECK_WINDOW (window);
887 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
888 tail = XCDR (tail))
890 Lisp_Object xwidget_view = XCAR (tail);
891 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
892 && EQ (Fxwidget_view_window (xwidget_view), window))
893 return xwidget_view;
896 return Qnil;
899 DEFUN ("xwidget-plist",
900 Fxwidget_plist, Sxwidget_plist,
901 1, 1, 0,
902 doc: /* Return the plist of XWIDGET. */)
903 (Lisp_Object xwidget)
905 CHECK_XWIDGET (xwidget);
906 return XXWIDGET (xwidget)->plist;
909 DEFUN ("xwidget-buffer",
910 Fxwidget_buffer, Sxwidget_buffer,
911 1, 1, 0,
912 doc: /* Return the buffer of XWIDGET. */)
913 (Lisp_Object xwidget)
915 CHECK_XWIDGET (xwidget);
916 return XXWIDGET (xwidget)->buffer;
919 DEFUN ("set-xwidget-plist",
920 Fset_xwidget_plist, Sset_xwidget_plist,
921 2, 2, 0,
922 doc: /* Replace the plist of XWIDGET with PLIST.
923 Returns PLIST. */)
924 (Lisp_Object xwidget, Lisp_Object plist)
926 CHECK_XWIDGET (xwidget);
927 CHECK_LIST (plist);
929 XXWIDGET (xwidget)->plist = plist;
930 return plist;
933 DEFUN ("set-xwidget-query-on-exit-flag",
934 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
935 2, 2, 0,
936 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
937 If the second argument FLAG is non-nil, Emacs will query the user before
938 exiting or killing a buffer if XWIDGET is running.
939 This function returns FLAG. */)
940 (Lisp_Object xwidget, Lisp_Object flag)
942 CHECK_XWIDGET (xwidget);
943 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
944 return flag;
947 DEFUN ("xwidget-query-on-exit-flag",
948 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
949 1, 1, 0,
950 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
951 (Lisp_Object xwidget)
953 CHECK_XWIDGET (xwidget);
954 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
957 void
958 syms_of_xwidget (void)
960 defsubr (&Smake_xwidget);
961 defsubr (&Sxwidgetp);
962 DEFSYM (Qxwidgetp, "xwidgetp");
963 defsubr (&Sxwidget_view_p);
964 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
965 defsubr (&Sxwidget_info);
966 defsubr (&Sxwidget_view_info);
967 defsubr (&Sxwidget_resize);
968 defsubr (&Sget_buffer_xwidgets);
969 defsubr (&Sxwidget_view_model);
970 defsubr (&Sxwidget_view_window);
971 defsubr (&Sxwidget_view_lookup);
972 defsubr (&Sxwidget_query_on_exit_flag);
973 defsubr (&Sset_xwidget_query_on_exit_flag);
975 defsubr (&Sxwidget_webkit_goto_uri);
976 defsubr (&Sxwidget_webkit_execute_script);
977 defsubr (&Sxwidget_webkit_get_title);
978 DEFSYM (Qwebkit, "webkit");
980 defsubr (&Sxwidget_size_request);
981 defsubr (&Sdelete_xwidget_view);
983 defsubr (&Sxwidget_plist);
984 defsubr (&Sxwidget_buffer);
985 defsubr (&Sset_xwidget_plist);
987 defsubr (&Sxwidget_set_adjustment);
989 DEFSYM (Qxwidget, "xwidget");
991 DEFSYM (QCxwidget, ":xwidget");
992 DEFSYM (QCtitle, ":title");
994 /* Do not forget to update the docstring of make-xwidget if you add
995 new types. */
997 DEFSYM (Qvertical, "vertical");
998 DEFSYM (Qhorizontal, "horizontal");
1000 DEFSYM (QCplist, ":plist");
1002 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1003 doc: /* xwidgets list. */);
1004 Vxwidget_list = Qnil;
1006 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1007 doc: /* xwidget views list. */);
1008 Vxwidget_view_list = Qnil;
1010 Fprovide (intern ("xwidget-internal"), Qnil);
1014 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1015 valid xwidget specification is a list whose car is the symbol
1016 `xwidget', and whose rest is a property list. The property list must
1017 contain a value for key `:type'. That value must be the name of a
1018 supported xwidget type. The rest of the property list depends on the
1019 xwidget type. */
1021 bool
1022 valid_xwidget_spec_p (Lisp_Object object)
1024 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1028 /* Find a value associated with key in spec. */
1029 static Lisp_Object
1030 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1032 Lisp_Object tail;
1034 eassert (valid_xwidget_spec_p (spec));
1036 for (tail = XCDR (spec);
1037 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1039 if (EQ (XCAR (tail), key))
1040 return XCAR (XCDR (tail));
1043 return Qnil;
1047 void
1048 xwidget_view_delete_all_in_window (struct window *w)
1050 struct xwidget_view *xv = NULL;
1051 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1052 tail = XCDR (tail))
1054 if (XWIDGET_VIEW_P (XCAR (tail)))
1056 xv = XXWIDGET_VIEW (XCAR (tail));
1057 if (XWINDOW (xv->w) == w)
1059 Fdelete_xwidget_view (XCAR (tail));
1065 static struct xwidget_view *
1066 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1068 Lisp_Object xwidget, window, ret;
1069 XSETXWIDGET (xwidget, xw);
1070 XSETWINDOW (window, w);
1072 ret = Fxwidget_view_lookup (xwidget, window);
1074 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1077 struct xwidget *
1078 lookup_xwidget (Lisp_Object spec)
1080 /* When a xwidget lisp spec is found initialize the C struct that is
1081 used in the C code. This is done by redisplay so values change
1082 if the spec changes. So, take special care of one-shot events. */
1083 Lisp_Object value;
1084 struct xwidget *xw;
1086 value = xwidget_spec_value (spec, QCxwidget);
1087 xw = XXWIDGET (value);
1089 return xw;
1092 /* Set up detection of touched xwidget. */
1093 static void
1094 xwidget_start_redisplay (void)
1096 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1097 tail = XCDR (tail))
1099 if (XWIDGET_VIEW_P (XCAR (tail)))
1100 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1104 /* The xwidget was touched during redisplay, so it isn't a candidate
1105 for hiding. */
1106 static void
1107 xwidget_touch (struct xwidget_view *xv)
1109 xv->redisplayed = true;
1112 static bool
1113 xwidget_touched (struct xwidget_view *xv)
1115 return xv->redisplayed;
1118 /* Redisplay has ended, now we should hide untouched xwidgets. */
1119 void
1120 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1122 int i;
1123 int area;
1125 xwidget_start_redisplay ();
1126 /* Iterate desired glyph matrix of window here, hide gtk widgets
1127 not in the desired matrix.
1129 This only takes care of xwidgets in active windows. If a window
1130 goes away from the screen, xwidget views must be deleted.
1132 dump_glyph_matrix (matrix, 2); */
1133 for (i = 0; i < matrix->nrows; ++i)
1135 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1136 struct glyph_row *row;
1137 row = MATRIX_ROW (matrix, i);
1138 if (row->enabled_p)
1139 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1141 struct glyph *glyph = row->glyphs[area];
1142 struct glyph *glyph_end = glyph + row->used[area];
1143 for (; glyph < glyph_end; ++glyph)
1144 if (glyph->type == XWIDGET_GLYPH)
1146 /* The only call to xwidget_end_redisplay is in dispnew.
1147 xwidget_end_redisplay (w->current_matrix); */
1148 xwidget_touch (xwidget_view_lookup (glyph->u.xwidget, w));
1153 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1154 tail = XCDR (tail))
1156 if (XWIDGET_VIEW_P (XCAR (tail)))
1158 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1160 /* "touched" is only meaningful for the current window, so
1161 disregard other views. */
1162 if (XWINDOW (xv->w) == w)
1164 if (xwidget_touched (xv))
1165 xwidget_show_view (xv);
1166 else
1167 xwidget_hide_view (xv);
1173 /* Kill all xwidget in BUFFER. */
1174 void
1175 kill_buffer_xwidgets (Lisp_Object buffer)
1177 Lisp_Object tail, xwidget;
1178 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1180 xwidget = XCAR (tail);
1181 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1182 /* TODO free the GTK things in xw. */
1184 CHECK_XWIDGET (xwidget);
1185 struct xwidget *xw = XXWIDGET (xwidget);
1186 if (xw->widget_osr && xw->widgetwindow_osr)
1188 gtk_widget_destroy (xw->widget_osr);
1189 gtk_widget_destroy (xw->widgetwindow_osr);