Resurrect earlier fix of resize_frame_windows
[emacs.git] / src / xwidget.c
blobd438d8790986c7298efc4fc775f5cee1d92d6303
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 7, 8, 0,
160 doc: /* Make an xwidget from BEG to END 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-osr
167 Returns the newly constructed xwidget, or nil if construction fails. */)
168 (Lisp_Object beg, Lisp_Object end, 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);
175 /* This should work a bit like "make-button"
176 (make-button BEG END &rest PROPERTIES)
177 TYPE etc. should be keyword args eventually.
178 (make-xwidget 3 3 'button "oei" 31 31 nil)
179 (xwidget-info (car xwidget-list)) */
180 struct xwidget *xw = allocate_xwidget ();
181 Lisp_Object val;
182 xw->type = type;
183 xw->title = title;
184 xw->buffer = NILP (buffer) ? Fcurrent_buffer () : Fget_buffer_create (buffer);
185 xw->height = XFASTINT (height);
186 xw->width = XFASTINT (width);
187 xw->kill_without_query = false;
188 XSETXWIDGET (val, xw);
189 Vxwidget_list = Fcons (val, Vxwidget_list);
190 xw->widgetwindow_osr = NULL;
191 xw->widget_osr = NULL;
192 xw->plist = Qnil;
194 if (EQ (xw->type, Qwebkit_osr))
196 block_input ();
197 xw->widgetwindow_osr = gtk_offscreen_window_new ();
198 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
199 xw->height);
201 /* WebKit OSR is the only scrolled component at the moment. */
202 xw->widgetscrolledwindow_osr = NULL;
204 if (EQ (xw->type, Qwebkit_osr))
206 xw->widgetscrolledwindow_osr = gtk_scrolled_window_new (NULL, NULL);
207 gtk_scrolled_window_set_min_content_height
208 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
209 xw->height);
210 gtk_scrolled_window_set_min_content_width
211 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
212 xw->width);
213 gtk_scrolled_window_set_policy
214 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
215 GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
217 xw->widget_osr = webkit_web_view_new ();
218 gtk_container_add (GTK_CONTAINER (xw->widgetscrolledwindow_osr),
219 GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr)));
222 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
223 xw->height);
225 if (EQ (xw->type, Qwebkit_osr))
227 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
228 xw->widgetscrolledwindow_osr);
230 else
232 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
233 xw->widget_osr);
236 gtk_widget_show (xw->widget_osr);
237 gtk_widget_show (xw->widgetwindow_osr);
238 gtk_widget_show (xw->widgetscrolledwindow_osr);
240 /* Store some xwidget data in the gtk widgets for convenient
241 retrieval in the event handlers. */
242 g_object_set_data (G_OBJECT (xw->widget_osr), XG_XWIDGET, xw);
243 g_object_set_data (G_OBJECT (xw->widgetwindow_osr), XG_XWIDGET, xw);
245 /* signals */
246 if (EQ (xw->type, Qwebkit_osr))
248 g_signal_connect (G_OBJECT (xw->widget_osr),
249 "document-load-finished",
250 G_CALLBACK (webkit_document_load_finished_cb), xw);
252 g_signal_connect (G_OBJECT (xw->widget_osr),
253 "download-requested",
254 G_CALLBACK (webkit_download_cb), xw);
256 g_signal_connect (G_OBJECT (xw->widget_osr),
257 "mime-type-policy-decision-requested",
258 G_CALLBACK
259 (webkit_mime_type_policy_typedecision_requested_cb),
260 xw);
262 g_signal_connect (G_OBJECT (xw->widget_osr),
263 "new-window-policy-decision-requested",
264 G_CALLBACK
265 (webkit_new_window_policy_decision_requested_cb),
266 xw);
268 g_signal_connect (G_OBJECT (xw->widget_osr),
269 "navigation-policy-decision-requested",
270 G_CALLBACK
271 (webkit_navigation_policy_decision_requested_cb),
272 xw);
275 unblock_input ();
278 return val;
281 DEFUN ("get-buffer-xwidgets", Fget_buffer_xwidgets, Sget_buffer_xwidgets,
282 1, 1, 0,
283 doc: /* Return a list of xwidgets associated with BUFFER.
284 BUFFER may be a buffer or the name of one. */)
285 (Lisp_Object buffer)
287 Lisp_Object xw, tail, xw_list;
289 if (NILP (buffer))
290 return Qnil;
291 buffer = Fget_buffer (buffer);
292 if (NILP (buffer))
293 return Qnil;
295 xw_list = Qnil;
297 for (tail = Vxwidget_list; CONSP (tail); tail = XCDR (tail))
299 xw = XCAR (tail);
300 if (XWIDGETP (xw) && EQ (Fxwidget_buffer (xw), buffer))
301 xw_list = Fcons (xw, xw_list);
303 return xw_list;
306 static bool
307 xwidget_hidden (struct xwidget_view *xv)
309 return xv->hidden;
312 static void
313 xwidget_show_view (struct xwidget_view *xv)
315 xv->hidden = false;
316 gtk_widget_show (xv->widgetwindow);
317 gtk_fixed_move (GTK_FIXED (xv->emacswindow),
318 xv->widgetwindow,
319 xv->x + xv->clip_left,
320 xv->y + xv->clip_top);
323 /* Hide an xwidget view. */
324 static void
325 xwidget_hide_view (struct xwidget_view *xv)
327 xv->hidden = true;
328 gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow,
329 10000, 10000);
332 /* When the off-screen webkit master view changes this signal is called.
333 It copies the bitmap from the off-screen instance. */
334 static gboolean
335 offscreen_damage_event (GtkWidget *widget, GdkEvent *event,
336 gpointer xv_widget)
338 /* Queue a redraw of onscreen widget.
339 There is a guard against receiving an invalid widget,
340 which should only happen if we failed to remove the
341 specific signal handler for the damage event. */
342 if (GTK_IS_WIDGET (xv_widget))
343 gtk_widget_queue_draw (GTK_WIDGET (xv_widget));
344 else
345 printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
346 xv_widget);
348 return FALSE;
351 static void
352 store_xwidget_event_string (struct xwidget *xw, const char *eventname,
353 const char *eventstr)
355 struct input_event event;
356 Lisp_Object xwl;
357 XSETXWIDGET (xwl, xw);
358 EVENT_INIT (event);
359 event.kind = XWIDGET_EVENT;
360 event.frame_or_window = Qnil;
361 event.arg = list3 (intern (eventname), xwl, build_string (eventstr));
362 kbd_buffer_store_event (&event);
365 /* TODO deprecated, use load-status. */
366 void
367 webkit_document_load_finished_cb (WebKitWebView *webkitwebview,
368 WebKitWebFrame *arg1,
369 gpointer data)
371 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
372 XG_XWIDGET);
374 store_xwidget_event_string (xw, "document-load-finished", "");
377 gboolean
378 webkit_download_cb (WebKitWebView *webkitwebview,
379 WebKitDownload *arg1,
380 gpointer data)
382 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
383 XG_XWIDGET);
384 store_xwidget_event_string (xw, "download-requested",
385 webkit_download_get_uri (arg1));
386 return FALSE;
389 static gboolean
390 webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *webView,
391 WebKitWebFrame *frame,
392 WebKitNetworkRequest *request,
393 gchar *mimetype,
394 WebKitWebPolicyDecision *policy_decision,
395 gpointer user_data)
397 /* This function makes webkit send a download signal for all unknown
398 mime types. TODO: Defer the decision to Lisp, so that it's
399 possible to make Emacs handle mime text for instance. */
400 if (!webkit_web_view_can_show_mime_type (webView, mimetype))
402 webkit_web_policy_decision_download (policy_decision);
403 return TRUE;
405 else
406 return FALSE;
409 static gboolean
410 webkit_new_window_policy_decision_requested_cb (WebKitWebView *webView,
411 WebKitWebFrame *frame,
412 WebKitNetworkRequest *request,
413 WebKitWebNavigationAction *navigation_action,
414 WebKitWebPolicyDecision *policy_decision,
415 gpointer user_data)
417 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
418 webkit_web_navigation_action_get_original_uri (navigation_action);
420 store_xwidget_event_string (xw, "new-window-policy-decision-requested",
421 webkit_web_navigation_action_get_original_uri
422 (navigation_action));
423 return FALSE;
426 static gboolean
427 webkit_navigation_policy_decision_requested_cb (WebKitWebView *webView,
428 WebKitWebFrame *frame,
429 WebKitNetworkRequest *request,
430 WebKitWebNavigationAction *navigation_action,
431 WebKitWebPolicyDecision *policy_decision,
432 gpointer user_data)
434 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
435 store_xwidget_event_string (xw, "navigation-policy-decision-requested",
436 webkit_web_navigation_action_get_original_uri
437 (navigation_action));
438 return FALSE;
441 /* For gtk3 offscreen rendered widgets. */
442 static gboolean
443 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
445 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
446 struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget),
447 XG_XWIDGET_VIEW);
449 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
450 cairo_clip (cr);
452 if (xw->widgetscrolledwindow_osr != NULL)
453 gtk_widget_draw (xw->widgetscrolledwindow_osr, cr);
454 else
455 gtk_widget_draw (xw->widget_osr, cr);
456 return FALSE;
459 static gboolean
460 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
461 gpointer user_data)
463 /* Copy events that arrive at the outer widget to the offscreen widget. */
464 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
465 GdkEvent *eventcopy = gdk_event_copy (event);
466 eventcopy->any.window = gtk_widget_get_window (xw->widget_osr);
468 /* TODO: This might leak events. They should be deallocated later,
469 perhaps in xwgir_event_cb. */
470 gtk_main_do_event (eventcopy);
472 /* Don't propagate this event further. */
473 return TRUE;
476 static gboolean
477 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
478 gpointer data)
480 struct xwidget_view *xv = data;
481 struct xwidget *xww = XXWIDGET (xv->model);
482 gdk_offscreen_window_set_embedder (gtk_widget_get_window
483 (xww->widgetwindow_osr),
484 gtk_widget_get_window (xv->widget));
485 return FALSE;
489 /* Initializes and does initial placement of an xwidget view on screen. */
490 static struct xwidget_view *
491 xwidget_init_view (struct xwidget *xww,
492 struct glyph_string *s,
493 int x, int y)
495 struct xwidget_view *xv = allocate_xwidget_view ();
496 Lisp_Object val;
498 XSETXWIDGET_VIEW (val, xv);
499 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
501 XSETWINDOW (xv->w, s->w);
502 XSETXWIDGET (xv->model, xww);
504 if (EQ (xww->type, Qwebkit_osr))
506 xv->widget = gtk_drawing_area_new ();
507 /* Expose event handling. */
508 gtk_widget_set_app_paintable (xv->widget, TRUE);
509 gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK);
511 /* Draw the view on damage-event. */
512 g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event",
513 G_CALLBACK (offscreen_damage_event), xv->widget);
515 if (EQ (xww->type, Qwebkit_osr))
517 g_signal_connect (G_OBJECT (xv->widget), "button-press-event",
518 G_CALLBACK (xwidget_osr_event_forward), NULL);
519 g_signal_connect (G_OBJECT (xv->widget), "button-release-event",
520 G_CALLBACK (xwidget_osr_event_forward), NULL);
521 g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event",
522 G_CALLBACK (xwidget_osr_event_forward), NULL);
524 else
526 /* xwgir debug, orthogonal to forwarding. */
527 g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event",
528 G_CALLBACK (xwidget_osr_event_set_embedder), xv);
530 g_signal_connect (G_OBJECT (xv->widget), "draw",
531 G_CALLBACK (xwidget_osr_draw_cb), NULL);
534 /* Widget realization.
536 Make container widget first, and put the actual widget inside the
537 container later. Drawing should crop container window if necessary
538 to handle case where xwidget is partially obscured by other Emacs
539 windows. Other containers than gtk_fixed where explored, but
540 gtk_fixed had the most predictable behavior so far. */
542 xv->emacswindow = FRAME_GTK_WIDGET (s->f);
543 xv->widgetwindow = gtk_fixed_new ();
544 gtk_widget_set_has_window (xv->widgetwindow, TRUE);
545 gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget);
547 /* Store some xwidget data in the gtk widgets. */
548 g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f);
549 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww);
550 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv);
551 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww);
552 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv);
554 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width,
555 xww->height);
556 gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height);
557 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y);
558 xv->x = x;
559 xv->y = y;
560 gtk_widget_show_all (xv->widgetwindow);
562 return xv;
565 void
566 x_draw_xwidget_glyph_string (struct glyph_string *s)
568 /* This method is called by the redisplay engine and places the
569 xwidget on screen. Moving and clipping is done here. Also view
570 initialization. */
571 struct xwidget *xww = s->xwidget;
572 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
573 int clip_right;
574 int clip_bottom;
575 int clip_top;
576 int clip_left;
578 int x = s->x;
579 int y = s->y + (s->height / 2) - (xww->height / 2);
581 /* Do initialization here in the display loop because there is no
582 other time to know things like window placement etc. */
583 xv = xwidget_init_view (xww, s, x, y);
585 /* Calculate clipping, which is used for all manner of onscreen
586 xwidget views. Each widget border can get clipped by other emacs
587 objects so there are four clipping variables. */
588 clip_right =
589 min (xww->width,
590 WINDOW_RIGHT_EDGE_X (s->w) - x -
591 WINDOW_RIGHT_SCROLL_BAR_AREA_WIDTH (s->w) -
592 WINDOW_RIGHT_FRINGE_WIDTH (s->w));
593 clip_left =
594 max (0,
595 WINDOW_LEFT_EDGE_X (s->w) - x +
596 WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (s->w) +
597 WINDOW_LEFT_FRINGE_WIDTH (s->w));
599 clip_bottom =
600 min (xww->height,
601 WINDOW_BOTTOM_EDGE_Y (s->w) - WINDOW_MODE_LINE_HEIGHT (s->w) - y);
602 clip_top = max (0, WINDOW_TOP_EDGE_Y (s->w) - y);
604 /* We are concerned with movement of the onscreen area. The area
605 might sit still when the widget actually moves. This happens
606 when an Emacs window border moves across a widget window. So, if
607 any corner of the outer widget clipping window moves, that counts
608 as movement here, even if it looks like no movement happens
609 because the widget sits still inside the clipping area. The
610 widget can also move inside the clipping area, which happens
611 later. */
612 bool moved = (xv->x + xv->clip_left != x + clip_left
613 || xv->y + xv->clip_top != y + clip_top);
614 xv->x = x;
615 xv->y = y;
617 /* Has it moved? */
618 if (moved)
619 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
620 xv->widgetwindow, x + clip_left, y + clip_top);
622 /* Clip the widget window if some parts happen to be outside
623 drawable area. An Emacs window is not a gtk window. A gtk window
624 covers the entire frame. Clipping might have changed even if we
625 haven't actually moved; try to figure out when we need to reclip
626 for real. */
627 if (xv->clip_right != clip_right
628 || xv->clip_bottom != clip_bottom
629 || xv->clip_top != clip_top || xv->clip_left != clip_left)
631 gtk_widget_set_size_request (xv->widgetwindow, clip_right + clip_left,
632 clip_bottom + clip_top);
633 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
634 -clip_top);
636 xv->clip_right = clip_right;
637 xv->clip_bottom = clip_bottom;
638 xv->clip_top = clip_top;
639 xv->clip_left = clip_left;
642 /* If emacs wants to repaint the area where the widget lives, queue
643 a redraw. It seems its possible to get out of sync with emacs
644 redraws so emacs background sometimes shows up instead of the
645 xwidgets background. It's just a visual glitch though. */
646 if (!xwidget_hidden (xv))
648 gtk_widget_queue_draw (xv->widgetwindow);
649 gtk_widget_queue_draw (xv->widget);
653 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
654 #define WEBKIT_FN_INIT() \
655 CHECK_XWIDGET (xwidget); \
656 struct xwidget *xw = XXWIDGET (xwidget); \
657 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
659 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
660 return Qnil; \
663 DEFUN ("xwidget-webkit-goto-uri",
664 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
665 2, 2, 0,
666 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
667 (Lisp_Object xwidget, Lisp_Object uri)
669 WEBKIT_FN_INIT ();
670 CHECK_STRING (uri);
671 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
672 return Qnil;
676 DEFUN ("xwidget-webkit-execute-script",
677 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
678 2, 2, 0,
679 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. */)
680 (Lisp_Object xwidget, Lisp_Object script)
682 WEBKIT_FN_INIT ();
683 CHECK_STRING (script);
684 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (xw->widget_osr),
685 SSDATA (script));
686 return Qnil;
689 DEFUN ("xwidget-webkit-get-title",
690 Fxwidget_webkit_get_title, Sxwidget_webkit_get_title,
691 1, 1, 0,
692 doc: /* Return the title from the Webkit instance in XWIDGET.
693 This can be used to work around the lack of a return value from the
694 exec method. */ )
695 (Lisp_Object xwidget)
697 /* TODO support multibyte strings. */
698 WEBKIT_FN_INIT ();
699 const gchar *str =
700 webkit_web_view_get_title (WEBKIT_WEB_VIEW (xw->widget_osr));
701 if (str == 0)
703 /* TODO maybe return Qnil instead. I suppose webkit returns
704 null pointer when doc is not properly loaded or something. */
705 return build_string ("");
707 return build_string (str);
710 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
711 doc: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
712 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
714 CHECK_XWIDGET (xwidget);
715 CHECK_NATNUM (new_width);
716 CHECK_NATNUM (new_height);
717 struct xwidget *xw = XXWIDGET (xwidget);
718 int w = XFASTINT (new_width);
719 int h = XFASTINT (new_height);
721 xw->width = w;
722 xw->height = h;
724 /* If there is an offscreen widget resize it first. */
725 if (xw->widget_osr)
727 /* Use minimum size. */
728 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr),
729 xw->width, xw->height);
731 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
732 xw->height);
733 gtk_scrolled_window_set_min_content_height
734 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
735 xw->height);
736 gtk_scrolled_window_set_min_content_width
737 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
738 xw->width);
740 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
744 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
746 if (XWIDGET_VIEW_P (XCAR (tail)))
748 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
749 if (XXWIDGET (xv->model) == xw)
750 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
751 xw->height);
755 return Qnil;
760 DEFUN ("xwidget-set-adjustment",
761 Fxwidget_set_adjustment, Sxwidget_set_adjustment, 4, 4, 0,
762 doc: /* Set native scrolling for XWIDGET.
763 AXIS can be `vertical' or `horizontal'.
764 If RELATIVE is t, scroll relative, otherwise absolutely.
765 VALUE is the amount to scroll, either relatively or absolutely. */)
766 (Lisp_Object xwidget, Lisp_Object axis, Lisp_Object relative,
767 Lisp_Object value)
769 CHECK_XWIDGET (xwidget);
770 CHECK_NATNUM (value);
771 struct xwidget *xw = XXWIDGET (xwidget);
772 GtkAdjustment *adjustment
773 = ((EQ (Qhorizontal, axis)
774 ? gtk_scrolled_window_get_hadjustment
775 : gtk_scrolled_window_get_vadjustment)
776 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)));
777 double final_value = XFASTINT (value);
778 if (EQ (Qt, relative))
779 final_value += gtk_adjustment_get_value (adjustment);
780 gtk_adjustment_set_value (adjustment, final_value);
781 return Qnil;
785 DEFUN ("xwidget-size-request",
786 Fxwidget_size_request, Sxwidget_size_request,
787 1, 1, 0,
788 doc: /* Return the desired size of the XWIDGET.
789 This can be used to read the xwidget desired size, and resizes the
790 Emacs allocated area accordingly. */)
791 (Lisp_Object xwidget)
793 CHECK_XWIDGET (xwidget);
794 GtkRequisition requisition;
795 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
796 return list2 (make_number (requisition.width),
797 make_number (requisition.height));
800 DEFUN ("xwidgetp",
801 Fxwidgetp, Sxwidgetp,
802 1, 1, 0,
803 doc: /* Return t if OBJECT is an xwidget. */)
804 (Lisp_Object object)
806 return XWIDGETP (object) ? Qt : Qnil;
809 DEFUN ("xwidget-view-p",
810 Fxwidget_view_p, Sxwidget_view_p,
811 1, 1, 0,
812 doc: /* Return t if OBJECT is an xwidget-view. */)
813 (Lisp_Object object)
815 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
818 DEFUN ("xwidget-info",
819 Fxwidget_info, Sxwidget_info,
820 1, 1, 0,
821 doc: /* Return XWIDGET properties in a vector.
822 Currently [TYPE TITLE WIDTH HEIGHT]. */)
823 (Lisp_Object xwidget)
825 CHECK_XWIDGET (xwidget);
826 struct xwidget *xw = XXWIDGET (xwidget);
827 return CALLN (Fvector, xw->type, xw->title,
828 make_natnum (xw->width), make_natnum (xw->height));
831 DEFUN ("xwidget-view-info",
832 Fxwidget_view_info, Sxwidget_view_info,
833 1, 1, 0,
834 doc: /* Return properties of XWIDGET-VIEW in a vector.
835 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
836 (Lisp_Object xwidget_view)
838 CHECK_XWIDGET_VIEW (xwidget_view);
839 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
840 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
841 make_number (xv->clip_right), make_number (xv->clip_bottom),
842 make_number (xv->clip_top), make_number (xv->clip_left));
845 DEFUN ("xwidget-view-model",
846 Fxwidget_view_model, Sxwidget_view_model,
847 1, 1, 0,
848 doc: /* Return the model associated with XWIDGET-VIEW. */)
849 (Lisp_Object xwidget_view)
851 CHECK_XWIDGET_VIEW (xwidget_view);
852 return XXWIDGET_VIEW (xwidget_view)->model;
855 DEFUN ("xwidget-view-window",
856 Fxwidget_view_window, Sxwidget_view_window,
857 1, 1, 0,
858 doc: /* Return the window of XWIDGET-VIEW. */)
859 (Lisp_Object xwidget_view)
861 CHECK_XWIDGET_VIEW (xwidget_view);
862 return XXWIDGET_VIEW (xwidget_view)->w;
866 DEFUN ("delete-xwidget-view",
867 Fdelete_xwidget_view, Sdelete_xwidget_view,
868 1, 1, 0,
869 doc: /* Delete the XWIDGET-VIEW. */)
870 (Lisp_Object xwidget_view)
872 CHECK_XWIDGET_VIEW (xwidget_view);
873 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
874 gtk_widget_destroy (xv->widgetwindow);
875 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
876 /* xv->model still has signals pointing to the view. There can be
877 several views. Find the matching signals and delete them all. */
878 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
879 G_SIGNAL_MATCH_DATA,
880 0, 0, 0, 0,
881 xv->widget);
882 return Qnil;
885 DEFUN ("xwidget-view-lookup",
886 Fxwidget_view_lookup, Sxwidget_view_lookup,
887 1, 2, 0,
888 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
889 If WINDOW is unspecified or nil, use the selected window.
890 Return nil if no association is found. */)
891 (Lisp_Object xwidget, Lisp_Object window)
893 CHECK_XWIDGET (xwidget);
895 if (NILP (window))
896 window = Fselected_window ();
897 CHECK_WINDOW (window);
899 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
900 tail = XCDR (tail))
902 Lisp_Object xwidget_view = XCAR (tail);
903 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
904 && EQ (Fxwidget_view_window (xwidget_view), window))
905 return xwidget_view;
908 return Qnil;
911 DEFUN ("xwidget-plist",
912 Fxwidget_plist, Sxwidget_plist,
913 1, 1, 0,
914 doc: /* Return the plist of XWIDGET. */)
915 (Lisp_Object xwidget)
917 CHECK_XWIDGET (xwidget);
918 return XXWIDGET (xwidget)->plist;
921 DEFUN ("xwidget-buffer",
922 Fxwidget_buffer, Sxwidget_buffer,
923 1, 1, 0,
924 doc: /* Return the buffer of XWIDGET. */)
925 (Lisp_Object xwidget)
927 CHECK_XWIDGET (xwidget);
928 return XXWIDGET (xwidget)->buffer;
931 DEFUN ("set-xwidget-plist",
932 Fset_xwidget_plist, Sset_xwidget_plist,
933 2, 2, 0,
934 doc: /* Replace the plist of XWIDGET with PLIST.
935 Returns PLIST. */)
936 (Lisp_Object xwidget, Lisp_Object plist)
938 CHECK_XWIDGET (xwidget);
939 CHECK_LIST (plist);
941 XXWIDGET (xwidget)->plist = plist;
942 return plist;
945 DEFUN ("set-xwidget-query-on-exit-flag",
946 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
947 2, 2, 0,
948 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
949 If the second argument FLAG is non-nil, Emacs will query the user before
950 exiting or killing a buffer if XWIDGET is running.
951 This function returns FLAG. */)
952 (Lisp_Object xwidget, Lisp_Object flag)
954 CHECK_XWIDGET (xwidget);
955 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
956 return flag;
959 DEFUN ("xwidget-query-on-exit-flag",
960 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
961 1, 1, 0,
962 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
963 (Lisp_Object xwidget)
965 CHECK_XWIDGET (xwidget);
966 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
969 void
970 syms_of_xwidget (void)
972 defsubr (&Smake_xwidget);
973 defsubr (&Sxwidgetp);
974 DEFSYM (Qxwidgetp, "xwidgetp");
975 defsubr (&Sxwidget_view_p);
976 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
977 defsubr (&Sxwidget_info);
978 defsubr (&Sxwidget_view_info);
979 defsubr (&Sxwidget_resize);
980 defsubr (&Sget_buffer_xwidgets);
981 defsubr (&Sxwidget_view_model);
982 defsubr (&Sxwidget_view_window);
983 defsubr (&Sxwidget_view_lookup);
984 defsubr (&Sxwidget_query_on_exit_flag);
985 defsubr (&Sset_xwidget_query_on_exit_flag);
987 defsubr (&Sxwidget_webkit_goto_uri);
988 defsubr (&Sxwidget_webkit_execute_script);
989 defsubr (&Sxwidget_webkit_get_title);
990 DEFSYM (Qwebkit_osr, "webkit-osr");
992 defsubr (&Sxwidget_size_request);
993 defsubr (&Sdelete_xwidget_view);
995 defsubr (&Sxwidget_plist);
996 defsubr (&Sxwidget_buffer);
997 defsubr (&Sset_xwidget_plist);
999 defsubr (&Sxwidget_set_adjustment);
1001 DEFSYM (Qxwidget, "xwidget");
1003 DEFSYM (QCxwidget, ":xwidget");
1004 DEFSYM (QCtitle, ":title");
1006 /* Do not forget to update the docstring of make-xwidget if you add
1007 new types. */
1009 DEFSYM (Qvertical, "vertical");
1010 DEFSYM (Qhorizontal, "horizontal");
1012 DEFSYM (QCplist, ":plist");
1014 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1015 doc: /* xwidgets list. */);
1016 Vxwidget_list = Qnil;
1018 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1019 doc: /* xwidget views list. */);
1020 Vxwidget_view_list = Qnil;
1022 Fprovide (intern ("xwidget-internal"), Qnil);
1026 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1027 valid xwidget specification is a list whose car is the symbol
1028 `xwidget', and whose rest is a property list. The property list must
1029 contain a value for key `:type'. That value must be the name of a
1030 supported xwidget type. The rest of the property list depends on the
1031 xwidget type. */
1033 bool
1034 valid_xwidget_spec_p (Lisp_Object object)
1036 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1040 /* Find a value associated with key in spec. */
1041 static Lisp_Object
1042 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1044 Lisp_Object tail;
1046 eassert (valid_xwidget_spec_p (spec));
1048 for (tail = XCDR (spec);
1049 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1051 if (EQ (XCAR (tail), key))
1052 return XCAR (XCDR (tail));
1055 return Qnil;
1059 void
1060 xwidget_view_delete_all_in_window (struct window *w)
1062 struct xwidget_view *xv = NULL;
1063 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1064 tail = XCDR (tail))
1066 if (XWIDGET_VIEW_P (XCAR (tail)))
1068 xv = XXWIDGET_VIEW (XCAR (tail));
1069 if (XWINDOW (xv->w) == w)
1071 Fdelete_xwidget_view (XCAR (tail));
1077 static struct xwidget_view *
1078 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1080 Lisp_Object xwidget, window, ret;
1081 XSETXWIDGET (xwidget, xw);
1082 XSETWINDOW (window, w);
1084 ret = Fxwidget_view_lookup (xwidget, window);
1086 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1089 struct xwidget *
1090 lookup_xwidget (Lisp_Object spec)
1092 /* When a xwidget lisp spec is found initialize the C struct that is
1093 used in the C code. This is done by redisplay so values change
1094 if the spec changes. So, take special care of one-shot events. */
1095 Lisp_Object value;
1096 struct xwidget *xw;
1098 value = xwidget_spec_value (spec, QCxwidget);
1099 xw = XXWIDGET (value);
1101 return xw;
1104 /* Set up detection of touched xwidget. */
1105 static void
1106 xwidget_start_redisplay (void)
1108 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1109 tail = XCDR (tail))
1111 if (XWIDGET_VIEW_P (XCAR (tail)))
1112 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1116 /* The xwidget was touched during redisplay, so it isn't a candidate
1117 for hiding. */
1118 static void
1119 xwidget_touch (struct xwidget_view *xv)
1121 xv->redisplayed = true;
1124 static bool
1125 xwidget_touched (struct xwidget_view *xv)
1127 return xv->redisplayed;
1130 /* Redisplay has ended, now we should hide untouched xwidgets. */
1131 void
1132 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1134 int i;
1135 int area;
1137 xwidget_start_redisplay ();
1138 /* Iterate desired glyph matrix of window here, hide gtk widgets
1139 not in the desired matrix.
1141 This only takes care of xwidgets in active windows. If a window
1142 goes away from the screen, xwidget views must be deleted.
1144 dump_glyph_matrix (matrix, 2); */
1145 for (i = 0; i < matrix->nrows; ++i)
1147 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1148 struct glyph_row *row;
1149 row = MATRIX_ROW (matrix, i);
1150 if (row->enabled_p)
1151 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1153 struct glyph *glyph = row->glyphs[area];
1154 struct glyph *glyph_end = glyph + row->used[area];
1155 for (; glyph < glyph_end; ++glyph)
1156 if (glyph->type == XWIDGET_GLYPH)
1158 /* The only call to xwidget_end_redisplay is in dispnew.
1159 xwidget_end_redisplay (w->current_matrix); */
1160 xwidget_touch (xwidget_view_lookup (glyph->u.xwidget, w));
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);