]> code.delx.au - gnu-emacs/blob - src/xwidget.c
4f1df1aabdfc13e22d30b0af234b7d82ee60ce94
[gnu-emacs] / src / xwidget.c
1 /* Support for embedding graphical components in a buffer.
2
3 Copyright (C) 2011-2016 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
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.
11
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.
16
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/>. */
19
20 #include <config.h>
21
22 #include "xwidget.h"
23
24 #include <signal.h>
25
26 #include <stdio.h>
27 #include <setjmp.h>
28 #ifdef HAVE_X_WINDOWS
29
30 #include "lisp.h"
31 #include "blockinput.h"
32 #include "syssignal.h"
33
34 #include "xterm.h"
35 #include <X11/cursorfont.h>
36
37 #ifndef makedev
38 # include <sys/types.h>
39 #endif
40
41 #ifdef BSD_SYSTEM
42 # include <sys/ioctl.h>
43 #endif
44
45 #include "systime.h"
46
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>
54
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"
73
74
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
87
88 #include "gtkutil.h"
89 #include "font.h"
90 #endif /* HAVE_X_WINDOWS */
91
92 #include <gtk/gtk.h>
93 #include <gdk/gdk.h>
94
95 #include <gtk/gtkx.h>
96
97 #include "emacsgtkfixed.h"
98
99 #include <wchar.h>
100
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>
108
109 static struct xwidget *
110 allocate_xwidget (void)
111 {
112 return ALLOCATE_PSEUDOVECTOR (struct xwidget, height, PVEC_XWIDGET);
113 }
114
115 static struct xwidget_view *
116 allocate_xwidget_view (void)
117 {
118 return ALLOCATE_PSEUDOVECTOR (struct xwidget_view, redisplayed,
119 PVEC_XWIDGET_VIEW);
120 }
121
122 #define XSETXWIDGET(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET)
123 #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET_VIEW)
124
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);
130
131 static gboolean
132 webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *,
133 WebKitWebFrame *,
134 WebKitNetworkRequest *,
135 gchar *,
136 WebKitWebPolicyDecision *,
137 gpointer);
138
139 static gboolean
140 webkit_new_window_policy_decision_requested_cb (WebKitWebView *,
141 WebKitWebFrame *,
142 WebKitNetworkRequest *,
143 WebKitWebNavigationAction *,
144 WebKitWebPolicyDecision *,
145 gpointer);
146
147 static gboolean
148 webkit_navigation_policy_decision_requested_cb (WebKitWebView *,
149 WebKitWebFrame *,
150 WebKitNetworkRequest *,
151 WebKitWebNavigationAction *,
152 WebKitWebPolicyDecision *,
153 gpointer);
154
155
156
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:
164
165 - webkit
166
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)
171 {
172 CHECK_SYMBOL (type);
173 CHECK_NATNUM (width);
174 CHECK_NATNUM (height);
175
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;
189
190 if (EQ (xw->type, Qwebkit_osr))
191 {
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);
196
197 /* WebKit OSR is the only scrolled component at the moment. */
198 xw->widgetscrolledwindow_osr = NULL;
199
200 if (EQ (xw->type, Qwebkit_osr))
201 {
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);
212
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)));
216 }
217
218 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
219 xw->height);
220
221 if (EQ (xw->type, Qwebkit_osr))
222 {
223 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
224 xw->widgetscrolledwindow_osr);
225 }
226 else
227 {
228 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
229 xw->widget_osr);
230 }
231
232 gtk_widget_show (xw->widget_osr);
233 gtk_widget_show (xw->widgetwindow_osr);
234 gtk_widget_show (xw->widgetscrolledwindow_osr);
235
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);
240
241 /* signals */
242 if (EQ (xw->type, Qwebkit_osr))
243 {
244 g_signal_connect (G_OBJECT (xw->widget_osr),
245 "document-load-finished",
246 G_CALLBACK (webkit_document_load_finished_cb), xw);
247
248 g_signal_connect (G_OBJECT (xw->widget_osr),
249 "download-requested",
250 G_CALLBACK (webkit_download_cb), xw);
251
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);
257
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);
263
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);
269 }
270
271 unblock_input ();
272 }
273
274 return val;
275 }
276
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)
282 {
283 Lisp_Object xw, tail, xw_list;
284
285 if (NILP (buffer))
286 return Qnil;
287 buffer = Fget_buffer (buffer);
288 if (NILP (buffer))
289 return Qnil;
290
291 xw_list = Qnil;
292
293 for (tail = Vxwidget_list; CONSP (tail); tail = XCDR (tail))
294 {
295 xw = XCAR (tail);
296 if (XWIDGETP (xw) && EQ (Fxwidget_buffer (xw), buffer))
297 xw_list = Fcons (xw, xw_list);
298 }
299 return xw_list;
300 }
301
302 static bool
303 xwidget_hidden (struct xwidget_view *xv)
304 {
305 return xv->hidden;
306 }
307
308 static void
309 xwidget_show_view (struct xwidget_view *xv)
310 {
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);
317 }
318
319 /* Hide an xwidget view. */
320 static void
321 xwidget_hide_view (struct xwidget_view *xv)
322 {
323 xv->hidden = true;
324 gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow,
325 10000, 10000);
326 }
327
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)
333 {
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);
343
344 return FALSE;
345 }
346
347 static void
348 store_xwidget_event_string (struct xwidget *xw, const char *eventname,
349 const char *eventstr)
350 {
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);
359 }
360
361 /* TODO deprecated, use load-status. */
362 void
363 webkit_document_load_finished_cb (WebKitWebView *webkitwebview,
364 WebKitWebFrame *arg1,
365 gpointer data)
366 {
367 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
368 XG_XWIDGET);
369
370 store_xwidget_event_string (xw, "document-load-finished", "");
371 }
372
373 gboolean
374 webkit_download_cb (WebKitWebView *webkitwebview,
375 WebKitDownload *arg1,
376 gpointer data)
377 {
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;
383 }
384
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)
392 {
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))
397 {
398 webkit_web_policy_decision_download (policy_decision);
399 return TRUE;
400 }
401 else
402 return FALSE;
403 }
404
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)
412 {
413 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
414 webkit_web_navigation_action_get_original_uri (navigation_action);
415
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;
420 }
421
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)
429 {
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;
435 }
436
437 /* For gtk3 offscreen rendered widgets. */
438 static gboolean
439 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
440 {
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);
444
445 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
446 cairo_clip (cr);
447
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;
453 }
454
455 static gboolean
456 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
457 gpointer user_data)
458 {
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);
463
464 /* TODO: This might leak events. They should be deallocated later,
465 perhaps in xwgir_event_cb. */
466 gtk_main_do_event (eventcopy);
467
468 /* Don't propagate this event further. */
469 return TRUE;
470 }
471
472 static gboolean
473 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
474 gpointer data)
475 {
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;
482 }
483
484
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)
490 {
491 struct xwidget_view *xv = allocate_xwidget_view ();
492 Lisp_Object val;
493
494 XSETXWIDGET_VIEW (val, xv);
495 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
496
497 XSETWINDOW (xv->w, s->w);
498 XSETXWIDGET (xv->model, xww);
499
500 if (EQ (xww->type, Qwebkit_osr))
501 {
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);
506
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);
510
511 if (EQ (xww->type, Qwebkit_osr))
512 {
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);
519 }
520 else
521 {
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);
525 }
526 g_signal_connect (G_OBJECT (xv->widget), "draw",
527 G_CALLBACK (xwidget_osr_draw_cb), NULL);
528 }
529
530 /* Widget realization.
531
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. */
537
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);
542
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);
549
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);
557
558 return xv;
559 }
560
561 void
562 x_draw_xwidget_glyph_string (struct glyph_string *s)
563 {
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;
573
574 int x = s->x;
575 int y = s->y + (s->height / 2) - (xww->height / 2);
576
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);
580
581 /* Calculate clipping, which is used for all manner of onscreen
582 xwidget views. Each widget border can get clipped by other emacs
583 objects so there are four clipping variables. */
584 clip_right =
585 min (xww->width,
586 WINDOW_RIGHT_EDGE_X (s->w) - x -
587 WINDOW_RIGHT_SCROLL_BAR_AREA_WIDTH (s->w) -
588 WINDOW_RIGHT_FRINGE_WIDTH (s->w));
589 clip_left =
590 max (0,
591 WINDOW_LEFT_EDGE_X (s->w) - x +
592 WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (s->w) +
593 WINDOW_LEFT_FRINGE_WIDTH (s->w));
594
595 clip_bottom =
596 min (xww->height,
597 WINDOW_BOTTOM_EDGE_Y (s->w) - WINDOW_MODE_LINE_HEIGHT (s->w) - y);
598 clip_top = max (0, WINDOW_TOP_EDGE_Y (s->w) - y);
599
600 /* We are concerned with movement of the onscreen area. The area
601 might sit still when the widget actually moves. This happens
602 when an Emacs window border moves across a widget window. So, if
603 any corner of the outer widget clipping window moves, that counts
604 as movement here, even if it looks like no movement happens
605 because the widget sits still inside the clipping area. The
606 widget can also move inside the clipping area, which happens
607 later. */
608 bool moved = (xv->x + xv->clip_left != x + clip_left
609 || xv->y + xv->clip_top != y + clip_top);
610 xv->x = x;
611 xv->y = y;
612
613 /* Has it moved? */
614 if (moved)
615 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
616 xv->widgetwindow, x + clip_left, y + clip_top);
617
618 /* Clip the widget window if some parts happen to be outside
619 drawable area. An Emacs window is not a gtk window. A gtk window
620 covers the entire frame. Clipping might have changed even if we
621 haven't actually moved; try to figure out when we need to reclip
622 for real. */
623 if (xv->clip_right != clip_right
624 || xv->clip_bottom != clip_bottom
625 || xv->clip_top != clip_top || xv->clip_left != clip_left)
626 {
627 gtk_widget_set_size_request (xv->widgetwindow, clip_right + clip_left,
628 clip_bottom + clip_top);
629 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
630 -clip_top);
631
632 xv->clip_right = clip_right;
633 xv->clip_bottom = clip_bottom;
634 xv->clip_top = clip_top;
635 xv->clip_left = clip_left;
636 }
637
638 /* If emacs wants to repaint the area where the widget lives, queue
639 a redraw. It seems its possible to get out of sync with emacs
640 redraws so emacs background sometimes shows up instead of the
641 xwidgets background. It's just a visual glitch though. */
642 if (!xwidget_hidden (xv))
643 {
644 gtk_widget_queue_draw (xv->widgetwindow);
645 gtk_widget_queue_draw (xv->widget);
646 }
647 }
648
649 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
650 #define WEBKIT_FN_INIT() \
651 CHECK_XWIDGET (xwidget); \
652 struct xwidget *xw = XXWIDGET (xwidget); \
653 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
654 { \
655 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
656 return Qnil; \
657 }
658
659 DEFUN ("xwidget-webkit-goto-uri",
660 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
661 2, 2, 0,
662 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
663 (Lisp_Object xwidget, Lisp_Object uri)
664 {
665 WEBKIT_FN_INIT ();
666 CHECK_STRING (uri);
667 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
668 return Qnil;
669 }
670
671
672 DEFUN ("xwidget-webkit-execute-script",
673 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
674 2, 2, 0,
675 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. */)
676 (Lisp_Object xwidget, Lisp_Object script)
677 {
678 WEBKIT_FN_INIT ();
679 CHECK_STRING (script);
680 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (xw->widget_osr),
681 SSDATA (script));
682 return Qnil;
683 }
684
685 DEFUN ("xwidget-webkit-get-title",
686 Fxwidget_webkit_get_title, Sxwidget_webkit_get_title,
687 1, 1, 0,
688 doc: /* Return the title from the Webkit instance in XWIDGET.
689 This can be used to work around the lack of a return value from the
690 exec method. */ )
691 (Lisp_Object xwidget)
692 {
693 /* TODO support multibyte strings. */
694 WEBKIT_FN_INIT ();
695 const gchar *str =
696 webkit_web_view_get_title (WEBKIT_WEB_VIEW (xw->widget_osr));
697 if (str == 0)
698 {
699 /* TODO maybe return Qnil instead. I suppose webkit returns
700 null pointer when doc is not properly loaded or something. */
701 return build_string ("");
702 }
703 return build_string (str);
704 }
705
706 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
707 doc: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
708 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
709 {
710 CHECK_XWIDGET (xwidget);
711 CHECK_NATNUM (new_width);
712 CHECK_NATNUM (new_height);
713 struct xwidget *xw = XXWIDGET (xwidget);
714 int w = XFASTINT (new_width);
715 int h = XFASTINT (new_height);
716
717 xw->width = w;
718 xw->height = h;
719
720 /* If there is an offscreen widget resize it first. */
721 if (xw->widget_osr)
722 {
723 /* Use minimum size. */
724 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr),
725 xw->width, xw->height);
726
727 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
728 xw->height);
729 gtk_scrolled_window_set_min_content_height
730 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
731 xw->height);
732 gtk_scrolled_window_set_min_content_width
733 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
734 xw->width);
735
736 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
737
738 }
739
740 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
741 {
742 if (XWIDGET_VIEW_P (XCAR (tail)))
743 {
744 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
745 if (XXWIDGET (xv->model) == xw)
746 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
747 xw->height);
748 }
749 }
750
751 return Qnil;
752 }
753
754
755
756 DEFUN ("xwidget-set-adjustment",
757 Fxwidget_set_adjustment, Sxwidget_set_adjustment, 4, 4, 0,
758 doc: /* Set native scrolling for XWIDGET.
759 AXIS can be `vertical' or `horizontal'.
760 If RELATIVE is t, scroll relative, otherwise absolutely.
761 VALUE is the amount to scroll, either relatively or absolutely. */)
762 (Lisp_Object xwidget, Lisp_Object axis, Lisp_Object relative,
763 Lisp_Object value)
764 {
765 CHECK_XWIDGET (xwidget);
766 CHECK_NUMBER (value);
767 struct xwidget *xw = XXWIDGET (xwidget);
768 GtkAdjustment *adjustment
769 = ((EQ (Qhorizontal, axis)
770 ? gtk_scrolled_window_get_hadjustment
771 : gtk_scrolled_window_get_vadjustment)
772 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)));
773 double final_value = XINT (value);
774 if (EQ (Qt, relative))
775 final_value += gtk_adjustment_get_value (adjustment);
776 gtk_adjustment_set_value (adjustment, final_value);
777 return Qnil;
778 }
779
780
781 DEFUN ("xwidget-size-request",
782 Fxwidget_size_request, Sxwidget_size_request,
783 1, 1, 0,
784 doc: /* Return the desired size of the XWIDGET.
785 This can be used to read the xwidget desired size, and resizes the
786 Emacs allocated area accordingly. */)
787 (Lisp_Object xwidget)
788 {
789 CHECK_XWIDGET (xwidget);
790 GtkRequisition requisition;
791 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
792 return list2 (make_number (requisition.width),
793 make_number (requisition.height));
794 }
795
796 DEFUN ("xwidgetp",
797 Fxwidgetp, Sxwidgetp,
798 1, 1, 0,
799 doc: /* Return t if OBJECT is an xwidget. */)
800 (Lisp_Object object)
801 {
802 return XWIDGETP (object) ? Qt : Qnil;
803 }
804
805 DEFUN ("xwidget-view-p",
806 Fxwidget_view_p, Sxwidget_view_p,
807 1, 1, 0,
808 doc: /* Return t if OBJECT is an xwidget-view. */)
809 (Lisp_Object object)
810 {
811 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
812 }
813
814 DEFUN ("xwidget-info",
815 Fxwidget_info, Sxwidget_info,
816 1, 1, 0,
817 doc: /* Return XWIDGET properties in a vector.
818 Currently [TYPE TITLE WIDTH HEIGHT]. */)
819 (Lisp_Object xwidget)
820 {
821 CHECK_XWIDGET (xwidget);
822 struct xwidget *xw = XXWIDGET (xwidget);
823 return CALLN (Fvector, xw->type, xw->title,
824 make_natnum (xw->width), make_natnum (xw->height));
825 }
826
827 DEFUN ("xwidget-view-info",
828 Fxwidget_view_info, Sxwidget_view_info,
829 1, 1, 0,
830 doc: /* Return properties of XWIDGET-VIEW in a vector.
831 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
832 (Lisp_Object xwidget_view)
833 {
834 CHECK_XWIDGET_VIEW (xwidget_view);
835 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
836 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
837 make_number (xv->clip_right), make_number (xv->clip_bottom),
838 make_number (xv->clip_top), make_number (xv->clip_left));
839 }
840
841 DEFUN ("xwidget-view-model",
842 Fxwidget_view_model, Sxwidget_view_model,
843 1, 1, 0,
844 doc: /* Return the model associated with XWIDGET-VIEW. */)
845 (Lisp_Object xwidget_view)
846 {
847 CHECK_XWIDGET_VIEW (xwidget_view);
848 return XXWIDGET_VIEW (xwidget_view)->model;
849 }
850
851 DEFUN ("xwidget-view-window",
852 Fxwidget_view_window, Sxwidget_view_window,
853 1, 1, 0,
854 doc: /* Return the window of XWIDGET-VIEW. */)
855 (Lisp_Object xwidget_view)
856 {
857 CHECK_XWIDGET_VIEW (xwidget_view);
858 return XXWIDGET_VIEW (xwidget_view)->w;
859 }
860
861
862 DEFUN ("delete-xwidget-view",
863 Fdelete_xwidget_view, Sdelete_xwidget_view,
864 1, 1, 0,
865 doc: /* Delete the XWIDGET-VIEW. */)
866 (Lisp_Object xwidget_view)
867 {
868 CHECK_XWIDGET_VIEW (xwidget_view);
869 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
870 gtk_widget_destroy (xv->widgetwindow);
871 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
872 /* xv->model still has signals pointing to the view. There can be
873 several views. Find the matching signals and delete them all. */
874 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
875 G_SIGNAL_MATCH_DATA,
876 0, 0, 0, 0,
877 xv->widget);
878 return Qnil;
879 }
880
881 DEFUN ("xwidget-view-lookup",
882 Fxwidget_view_lookup, Sxwidget_view_lookup,
883 1, 2, 0,
884 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
885 If WINDOW is unspecified or nil, use the selected window.
886 Return nil if no association is found. */)
887 (Lisp_Object xwidget, Lisp_Object window)
888 {
889 CHECK_XWIDGET (xwidget);
890
891 if (NILP (window))
892 window = Fselected_window ();
893 CHECK_WINDOW (window);
894
895 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
896 tail = XCDR (tail))
897 {
898 Lisp_Object xwidget_view = XCAR (tail);
899 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
900 && EQ (Fxwidget_view_window (xwidget_view), window))
901 return xwidget_view;
902 }
903
904 return Qnil;
905 }
906
907 DEFUN ("xwidget-plist",
908 Fxwidget_plist, Sxwidget_plist,
909 1, 1, 0,
910 doc: /* Return the plist of XWIDGET. */)
911 (Lisp_Object xwidget)
912 {
913 CHECK_XWIDGET (xwidget);
914 return XXWIDGET (xwidget)->plist;
915 }
916
917 DEFUN ("xwidget-buffer",
918 Fxwidget_buffer, Sxwidget_buffer,
919 1, 1, 0,
920 doc: /* Return the buffer of XWIDGET. */)
921 (Lisp_Object xwidget)
922 {
923 CHECK_XWIDGET (xwidget);
924 return XXWIDGET (xwidget)->buffer;
925 }
926
927 DEFUN ("set-xwidget-plist",
928 Fset_xwidget_plist, Sset_xwidget_plist,
929 2, 2, 0,
930 doc: /* Replace the plist of XWIDGET with PLIST.
931 Returns PLIST. */)
932 (Lisp_Object xwidget, Lisp_Object plist)
933 {
934 CHECK_XWIDGET (xwidget);
935 CHECK_LIST (plist);
936
937 XXWIDGET (xwidget)->plist = plist;
938 return plist;
939 }
940
941 DEFUN ("set-xwidget-query-on-exit-flag",
942 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
943 2, 2, 0,
944 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
945 If the second argument FLAG is non-nil, Emacs will query the user before
946 exiting or killing a buffer if XWIDGET is running.
947 This function returns FLAG. */)
948 (Lisp_Object xwidget, Lisp_Object flag)
949 {
950 CHECK_XWIDGET (xwidget);
951 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
952 return flag;
953 }
954
955 DEFUN ("xwidget-query-on-exit-flag",
956 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
957 1, 1, 0,
958 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
959 (Lisp_Object xwidget)
960 {
961 CHECK_XWIDGET (xwidget);
962 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
963 }
964
965 void
966 syms_of_xwidget (void)
967 {
968 defsubr (&Smake_xwidget);
969 defsubr (&Sxwidgetp);
970 DEFSYM (Qxwidgetp, "xwidgetp");
971 defsubr (&Sxwidget_view_p);
972 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
973 defsubr (&Sxwidget_info);
974 defsubr (&Sxwidget_view_info);
975 defsubr (&Sxwidget_resize);
976 defsubr (&Sget_buffer_xwidgets);
977 defsubr (&Sxwidget_view_model);
978 defsubr (&Sxwidget_view_window);
979 defsubr (&Sxwidget_view_lookup);
980 defsubr (&Sxwidget_query_on_exit_flag);
981 defsubr (&Sset_xwidget_query_on_exit_flag);
982
983 defsubr (&Sxwidget_webkit_goto_uri);
984 defsubr (&Sxwidget_webkit_execute_script);
985 defsubr (&Sxwidget_webkit_get_title);
986 DEFSYM (Qwebkit_osr, "webkit");
987
988 defsubr (&Sxwidget_size_request);
989 defsubr (&Sdelete_xwidget_view);
990
991 defsubr (&Sxwidget_plist);
992 defsubr (&Sxwidget_buffer);
993 defsubr (&Sset_xwidget_plist);
994
995 defsubr (&Sxwidget_set_adjustment);
996
997 DEFSYM (Qxwidget, "xwidget");
998
999 DEFSYM (QCxwidget, ":xwidget");
1000 DEFSYM (QCtitle, ":title");
1001
1002 /* Do not forget to update the docstring of make-xwidget if you add
1003 new types. */
1004
1005 DEFSYM (Qvertical, "vertical");
1006 DEFSYM (Qhorizontal, "horizontal");
1007
1008 DEFSYM (QCplist, ":plist");
1009
1010 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1011 doc: /* xwidgets list. */);
1012 Vxwidget_list = Qnil;
1013
1014 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1015 doc: /* xwidget views list. */);
1016 Vxwidget_view_list = Qnil;
1017
1018 Fprovide (intern ("xwidget-internal"), Qnil);
1019 }
1020
1021
1022 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1023 valid xwidget specification is a list whose car is the symbol
1024 `xwidget', and whose rest is a property list. The property list must
1025 contain a value for key `:type'. That value must be the name of a
1026 supported xwidget type. The rest of the property list depends on the
1027 xwidget type. */
1028
1029 bool
1030 valid_xwidget_spec_p (Lisp_Object object)
1031 {
1032 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1033 }
1034
1035
1036 /* Find a value associated with key in spec. */
1037 static Lisp_Object
1038 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1039 {
1040 Lisp_Object tail;
1041
1042 eassert (valid_xwidget_spec_p (spec));
1043
1044 for (tail = XCDR (spec);
1045 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1046 {
1047 if (EQ (XCAR (tail), key))
1048 return XCAR (XCDR (tail));
1049 }
1050
1051 return Qnil;
1052 }
1053
1054
1055 void
1056 xwidget_view_delete_all_in_window (struct window *w)
1057 {
1058 struct xwidget_view *xv = NULL;
1059 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1060 tail = XCDR (tail))
1061 {
1062 if (XWIDGET_VIEW_P (XCAR (tail)))
1063 {
1064 xv = XXWIDGET_VIEW (XCAR (tail));
1065 if (XWINDOW (xv->w) == w)
1066 {
1067 Fdelete_xwidget_view (XCAR (tail));
1068 }
1069 }
1070 }
1071 }
1072
1073 static struct xwidget_view *
1074 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1075 {
1076 Lisp_Object xwidget, window, ret;
1077 XSETXWIDGET (xwidget, xw);
1078 XSETWINDOW (window, w);
1079
1080 ret = Fxwidget_view_lookup (xwidget, window);
1081
1082 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1083 }
1084
1085 struct xwidget *
1086 lookup_xwidget (Lisp_Object spec)
1087 {
1088 /* When a xwidget lisp spec is found initialize the C struct that is
1089 used in the C code. This is done by redisplay so values change
1090 if the spec changes. So, take special care of one-shot events. */
1091 Lisp_Object value;
1092 struct xwidget *xw;
1093
1094 value = xwidget_spec_value (spec, QCxwidget);
1095 xw = XXWIDGET (value);
1096
1097 return xw;
1098 }
1099
1100 /* Set up detection of touched xwidget. */
1101 static void
1102 xwidget_start_redisplay (void)
1103 {
1104 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1105 tail = XCDR (tail))
1106 {
1107 if (XWIDGET_VIEW_P (XCAR (tail)))
1108 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1109 }
1110 }
1111
1112 /* The xwidget was touched during redisplay, so it isn't a candidate
1113 for hiding. */
1114 static void
1115 xwidget_touch (struct xwidget_view *xv)
1116 {
1117 xv->redisplayed = true;
1118 }
1119
1120 static bool
1121 xwidget_touched (struct xwidget_view *xv)
1122 {
1123 return xv->redisplayed;
1124 }
1125
1126 /* Redisplay has ended, now we should hide untouched xwidgets. */
1127 void
1128 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1129 {
1130 int i;
1131 int area;
1132
1133 xwidget_start_redisplay ();
1134 /* Iterate desired glyph matrix of window here, hide gtk widgets
1135 not in the desired matrix.
1136
1137 This only takes care of xwidgets in active windows. If a window
1138 goes away from the screen, xwidget views must be deleted.
1139
1140 dump_glyph_matrix (matrix, 2); */
1141 for (i = 0; i < matrix->nrows; ++i)
1142 {
1143 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1144 struct glyph_row *row;
1145 row = MATRIX_ROW (matrix, i);
1146 if (row->enabled_p)
1147 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1148 {
1149 struct glyph *glyph = row->glyphs[area];
1150 struct glyph *glyph_end = glyph + row->used[area];
1151 for (; glyph < glyph_end; ++glyph)
1152 if (glyph->type == XWIDGET_GLYPH)
1153 {
1154 /* The only call to xwidget_end_redisplay is in dispnew.
1155 xwidget_end_redisplay (w->current_matrix); */
1156 xwidget_touch (xwidget_view_lookup (glyph->u.xwidget, w));
1157 }
1158 }
1159 }
1160
1161 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1162 tail = XCDR (tail))
1163 {
1164 if (XWIDGET_VIEW_P (XCAR (tail)))
1165 {
1166 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1167
1168 /* "touched" is only meaningful for the current window, so
1169 disregard other views. */
1170 if (XWINDOW (xv->w) == w)
1171 {
1172 if (xwidget_touched (xv))
1173 xwidget_show_view (xv);
1174 else
1175 xwidget_hide_view (xv);
1176 }
1177 }
1178 }
1179 }
1180
1181 /* Kill all xwidget in BUFFER. */
1182 void
1183 kill_buffer_xwidgets (Lisp_Object buffer)
1184 {
1185 Lisp_Object tail, xwidget;
1186 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1187 {
1188 xwidget = XCAR (tail);
1189 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1190 /* TODO free the GTK things in xw. */
1191 {
1192 CHECK_XWIDGET (xwidget);
1193 struct xwidget *xw = XXWIDGET (xwidget);
1194 if (xw->widget_osr && xw->widgetwindow_osr)
1195 {
1196 gtk_widget_destroy (xw->widget_osr);
1197 gtk_widget_destroy (xw->widgetwindow_osr);
1198 }
1199 }
1200 }
1201 }