]> code.delx.au - gnu-emacs/blob - src/xwidget.c
f436e95d686edf5f76c304a0021e58ab10d0a75c
[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
10 (at 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 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:
164
165 - webkit-osr
166
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)
171 {
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;
193
194 if (EQ (xw->type, Qwebkit_osr))
195 {
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);
200
201 /* WebKit OSR is the only scrolled component at the moment. */
202 xw->widgetscrolledwindow_osr = NULL;
203
204 if (EQ (xw->type, Qwebkit_osr))
205 {
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);
216
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)));
220 }
221
222 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
223 xw->height);
224
225 if (EQ (xw->type, Qwebkit_osr))
226 {
227 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
228 xw->widgetscrolledwindow_osr);
229 }
230 else
231 {
232 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
233 xw->widget_osr);
234 }
235
236 gtk_widget_show (xw->widget_osr);
237 gtk_widget_show (xw->widgetwindow_osr);
238 gtk_widget_show (xw->widgetscrolledwindow_osr);
239
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);
244
245 /* signals */
246 if (EQ (xw->type, Qwebkit_osr))
247 {
248 g_signal_connect (G_OBJECT (xw->widget_osr),
249 "document-load-finished",
250 G_CALLBACK (webkit_document_load_finished_cb), xw);
251
252 g_signal_connect (G_OBJECT (xw->widget_osr),
253 "download-requested",
254 G_CALLBACK (webkit_download_cb), xw);
255
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);
261
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);
267
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);
273 }
274
275 unblock_input ();
276 }
277
278 return val;
279 }
280
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)
286 {
287 Lisp_Object xw, tail, xw_list;
288
289 if (NILP (buffer))
290 return Qnil;
291 buffer = Fget_buffer (buffer);
292 if (NILP (buffer))
293 return Qnil;
294
295 xw_list = Qnil;
296
297 for (tail = Vxwidget_list; CONSP (tail); tail = XCDR (tail))
298 {
299 xw = XCAR (tail);
300 if (XWIDGETP (xw) && EQ (Fxwidget_buffer (xw), buffer))
301 xw_list = Fcons (xw, xw_list);
302 }
303 return xw_list;
304 }
305
306 static bool
307 xwidget_hidden (struct xwidget_view *xv)
308 {
309 return xv->hidden;
310 }
311
312 static void
313 xwidget_show_view (struct xwidget_view *xv)
314 {
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);
321 }
322
323 /* Hide an xwidget view. */
324 static void
325 xwidget_hide_view (struct xwidget_view *xv)
326 {
327 xv->hidden = true;
328 gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow,
329 10000, 10000);
330 }
331
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)
337 {
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);
347
348 return FALSE;
349 }
350
351 static void
352 store_xwidget_event_string (struct xwidget *xw, const char *eventname,
353 const char *eventstr)
354 {
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);
363 }
364
365 /* TODO deprecated, use load-status. */
366 void
367 webkit_document_load_finished_cb (WebKitWebView *webkitwebview,
368 WebKitWebFrame *arg1,
369 gpointer data)
370 {
371 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
372 XG_XWIDGET);
373
374 store_xwidget_event_string (xw, "document-load-finished", "");
375 }
376
377 gboolean
378 webkit_download_cb (WebKitWebView *webkitwebview,
379 WebKitDownload *arg1,
380 gpointer data)
381 {
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;
387 }
388
389 static gboolean
390 webkit_mime_type_policy_typedecision_requested_cb
391 (WebKitWebView *webView, WebKitWebFrame *frame, WebKitNetworkRequest *request,
392 gchar *mimetype, WebKitWebPolicyDecision *policy_decision, gpointer user_data)
393 {
394 /* This function makes webkit send a download signal for all unknown
395 mime types. TODO: Defer the decision to Lisp, so that it's
396 possible to make Emacs handle mime text for instance. */
397 if (!webkit_web_view_can_show_mime_type (webView, mimetype))
398 {
399 webkit_web_policy_decision_download (policy_decision);
400 return TRUE;
401 }
402 else
403 return FALSE;
404 }
405
406 static gboolean
407 webkit_new_window_policy_decision_requested_cb
408 (WebKitWebView *webView, WebKitWebFrame *frame, WebKitNetworkRequest *request,
409 WebKitWebNavigationAction *navigation_action,
410 WebKitWebPolicyDecision *policy_decision, gpointer user_data)
411 {
412 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
413 webkit_web_navigation_action_get_original_uri (navigation_action);
414
415 store_xwidget_event_string (xw, "new-window-policy-decision-requested",
416 webkit_web_navigation_action_get_original_uri
417 (navigation_action));
418 return FALSE;
419 }
420
421 static gboolean
422 webkit_navigation_policy_decision_requested_cb
423 (WebKitWebView *webView, WebKitWebFrame *frame, WebKitNetworkRequest *request,
424 WebKitWebNavigationAction *navigation_action,
425 WebKitWebPolicyDecision *policy_decision, gpointer user_data)
426 {
427 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
428 store_xwidget_event_string (xw, "navigation-policy-decision-requested",
429 webkit_web_navigation_action_get_original_uri
430 (navigation_action));
431 return FALSE;
432 }
433
434 /* For gtk3 offscreen rendered widgets. */
435 static gboolean
436 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
437 {
438 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
439 struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget),
440 XG_XWIDGET_VIEW);
441
442 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
443 cairo_clip (cr);
444
445 if (xw->widgetscrolledwindow_osr != NULL)
446 gtk_widget_draw (xw->widgetscrolledwindow_osr, cr);
447 else
448 gtk_widget_draw (xw->widget_osr, cr);
449 return FALSE;
450 }
451
452 static gboolean
453 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
454 gpointer user_data)
455 {
456 /* Copy events that arrive at the outer widget to the offscreen widget. */
457 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
458 GdkEvent *eventcopy = gdk_event_copy (event);
459 eventcopy->any.window = gtk_widget_get_window (xw->widget_osr);
460
461 /* TODO: This might leak events. They should be deallocated later,
462 perhaps in xwgir_event_cb. */
463 gtk_main_do_event (eventcopy);
464
465 /* Don't propagate this event further. */
466 return TRUE;
467 }
468
469 static gboolean
470 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
471 gpointer data)
472 {
473 struct xwidget_view *xv = data;
474 struct xwidget *xww = XXWIDGET (xv->model);
475 gdk_offscreen_window_set_embedder (gtk_widget_get_window
476 (xww->widgetwindow_osr),
477 gtk_widget_get_window (xv->widget));
478 return FALSE;
479 }
480
481
482 /* Initializes and does initial placement of an xwidget view on screen. */
483 static struct xwidget_view *
484 xwidget_init_view (struct xwidget *xww,
485 struct glyph_string *s,
486 int x, int y)
487 {
488 struct xwidget_view *xv = allocate_xwidget_view ();
489 Lisp_Object val;
490
491 XSETXWIDGET_VIEW (val, xv);
492 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
493
494 XSETWINDOW (xv->w, s->w);
495 XSETXWIDGET (xv->model, xww);
496
497 if (EQ (xww->type, Qwebkit_osr))
498 {
499 xv->widget = gtk_drawing_area_new ();
500 /* Expose event handling. */
501 gtk_widget_set_app_paintable (xv->widget, TRUE);
502 gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK);
503
504 /* Draw the view on damage-event. */
505 g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event",
506 G_CALLBACK (offscreen_damage_event), xv->widget);
507
508 if (EQ (xww->type, Qwebkit_osr))
509 {
510 g_signal_connect (G_OBJECT (xv->widget), "button-press-event",
511 G_CALLBACK (xwidget_osr_event_forward), NULL);
512 g_signal_connect (G_OBJECT (xv->widget), "button-release-event",
513 G_CALLBACK (xwidget_osr_event_forward), NULL);
514 g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event",
515 G_CALLBACK (xwidget_osr_event_forward), NULL);
516 }
517 else
518 {
519 /* xwgir debug, orthogonal to forwarding. */
520 g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event",
521 G_CALLBACK (xwidget_osr_event_set_embedder), xv);
522 }
523 g_signal_connect (G_OBJECT (xv->widget), "draw",
524 G_CALLBACK (xwidget_osr_draw_cb), NULL);
525 }
526
527 /* Widget realization.
528
529 Make container widget first, and put the actual widget inside the
530 container later. Drawing should crop container window if necessary
531 to handle case where xwidget is partially obscured by other Emacs
532 windows. Other containers than gtk_fixed where explored, but
533 gtk_fixed had the most predictable behavior so far. */
534
535 xv->emacswindow = FRAME_GTK_WIDGET (s->f);
536 xv->widgetwindow = gtk_fixed_new ();
537 gtk_widget_set_has_window (xv->widgetwindow, TRUE);
538 gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget);
539
540 /* Store some xwidget data in the gtk widgets. */
541 g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f);
542 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww);
543 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv);
544 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww);
545 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv);
546
547 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width,
548 xww->height);
549 gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height);
550 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y);
551 xv->x = x;
552 xv->y = y;
553 gtk_widget_show_all (xv->widgetwindow);
554
555 return xv;
556 }
557
558 void
559 x_draw_xwidget_glyph_string (struct glyph_string *s)
560 {
561 /* This method is called by the redisplay engine and places the
562 xwidget on screen. Moving and clipping is done here. Also view
563 initialization. */
564 struct xwidget *xww = s->xwidget;
565 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
566 int clip_right;
567 int clip_bottom;
568 int clip_top;
569 int clip_left;
570
571 int x = s->x;
572 int y = s->y + (s->height / 2) - (xww->height / 2);
573
574 /* Do initialization here in the display loop because there is no
575 other time to know things like window placement etc. */
576 xv = xwidget_init_view (xww, s, x, y);
577
578 /* Calculate clipping, which is used for all manner of onscreen
579 xwidget views. Each widget border can get clipped by other emacs
580 objects so there are four clipping variables. */
581 clip_right =
582 min (xww->width,
583 WINDOW_RIGHT_EDGE_X (s->w) - x -
584 WINDOW_RIGHT_SCROLL_BAR_AREA_WIDTH (s->w) -
585 WINDOW_RIGHT_FRINGE_WIDTH (s->w));
586 clip_left =
587 max (0,
588 WINDOW_LEFT_EDGE_X (s->w) - x +
589 WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (s->w) +
590 WINDOW_LEFT_FRINGE_WIDTH (s->w));
591
592 clip_bottom =
593 min (xww->height,
594 WINDOW_BOTTOM_EDGE_Y (s->w) - WINDOW_MODE_LINE_HEIGHT (s->w) - y);
595 clip_top = max (0, WINDOW_TOP_EDGE_Y (s->w) - y);
596
597 /* We are concerned with movement of the onscreen area. The area
598 might sit still when the widget actually moves. This happens
599 when an Emacs window border moves across a widget window. So, if
600 any corner of the outer widget clipping window moves, that counts
601 as movement here, even if it looks like no movement happens
602 because the widget sits still inside the clipping area. The
603 widget can also move inside the clipping area, which happens
604 later. */
605 bool moved = (xv->x + xv->clip_left != x + clip_left
606 || xv->y + xv->clip_top != y + clip_top);
607 xv->x = x;
608 xv->y = y;
609
610 /* Has it moved? */
611 if (moved)
612 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
613 xv->widgetwindow, x + clip_left, y + clip_top);
614
615 /* Clip the widget window if some parts happen to be outside
616 drawable area. An Emacs window is not a gtk window. A gtk window
617 covers the entire frame. Clipping might have changed even if we
618 haven't actually moved; try to figure out when we need to reclip
619 for real. */
620 if (xv->clip_right != clip_right
621 || xv->clip_bottom != clip_bottom
622 || xv->clip_top != clip_top || xv->clip_left != clip_left)
623 {
624 gtk_widget_set_size_request (xv->widgetwindow, clip_right + clip_left,
625 clip_bottom + clip_top);
626 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
627 -clip_top);
628
629 xv->clip_right = clip_right;
630 xv->clip_bottom = clip_bottom;
631 xv->clip_top = clip_top;
632 xv->clip_left = clip_left;
633 }
634
635 /* If emacs wants to repaint the area where the widget lives, queue
636 a redraw. It seems its possible to get out of sync with emacs
637 redraws so emacs background sometimes shows up instead of the
638 xwidgets background. It's just a visual glitch though. */
639 if (!xwidget_hidden (xv))
640 {
641 gtk_widget_queue_draw (xv->widgetwindow);
642 gtk_widget_queue_draw (xv->widget);
643 }
644 }
645
646 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
647 #define WEBKIT_FN_INIT() \
648 CHECK_XWIDGET (xwidget); \
649 struct xwidget *xw = XXWIDGET (xwidget); \
650 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
651 { \
652 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
653 return Qnil; \
654 }
655
656 DEFUN ("xwidget-webkit-goto-uri",
657 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
658 2, 2, 0,
659 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
660 (Lisp_Object xwidget, Lisp_Object uri)
661 {
662 WEBKIT_FN_INIT ();
663 CHECK_STRING (uri);
664 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
665 return Qnil;
666 }
667
668
669 DEFUN ("xwidget-webkit-execute-script",
670 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
671 2, 2, 0,
672 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. */)
673 (Lisp_Object xwidget, Lisp_Object script)
674 {
675 WEBKIT_FN_INIT ();
676 CHECK_STRING (script);
677 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (xw->widget_osr),
678 SSDATA (script));
679 return Qnil;
680 }
681
682 DEFUN ("xwidget-webkit-get-title",
683 Fxwidget_webkit_get_title, Sxwidget_webkit_get_title,
684 1, 1, 0,
685 doc: /* Return the title from the Webkit instance in XWIDGET.
686 This can be used to work around the lack of a return value from the
687 exec method. */ )
688 (Lisp_Object xwidget)
689 {
690 /* TODO support multibyte strings. */
691 WEBKIT_FN_INIT ();
692 const gchar *str =
693 webkit_web_view_get_title (WEBKIT_WEB_VIEW (xw->widget_osr));
694 if (str == 0)
695 {
696 /* TODO maybe return Qnil instead. I suppose webkit returns
697 null pointer when doc is not properly loaded or something. */
698 return build_string ("");
699 }
700 return build_string (str);
701 }
702
703 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
704 doc: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
705 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
706 {
707 CHECK_XWIDGET (xwidget);
708 CHECK_NATNUM (new_width);
709 CHECK_NATNUM (new_height);
710 struct xwidget *xw = XXWIDGET (xwidget);
711 int w = XFASTINT (new_width);
712 int h = XFASTINT (new_height);
713
714 xw->width = w;
715 xw->height = h;
716
717 /* If there is an offscreen widget resize it first. */
718 if (xw->widget_osr)
719 {
720 /* Use minimum size. */
721 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr),
722 xw->width, xw->height);
723
724 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
725 xw->height);
726 gtk_scrolled_window_set_min_content_height
727 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
728 xw->height);
729 gtk_scrolled_window_set_min_content_width
730 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
731 xw->width);
732
733 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
734
735 }
736
737 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
738 {
739 if (XWIDGET_VIEW_P (XCAR (tail)))
740 {
741 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
742 if (XXWIDGET (xv->model) == xw)
743 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
744 xw->height);
745 }
746 }
747
748 return Qnil;
749 }
750
751
752
753 DEFUN ("xwidget-set-adjustment",
754 Fxwidget_set_adjustment, Sxwidget_set_adjustment, 4, 4, 0,
755 doc: /* Set native scrolling for XWIDGET.
756 AXIS can be `vertical' or `horizontal'.
757 If RELATIVE is t, scroll relative, otherwise absolutely.
758 VALUE is the amount to scroll, either relatively or absolutely. */)
759 (Lisp_Object xwidget, Lisp_Object axis, Lisp_Object relative,
760 Lisp_Object value)
761 {
762 CHECK_XWIDGET (xwidget);
763 CHECK_NATNUM (value);
764 struct xwidget *xw = XXWIDGET (xwidget);
765 GtkAdjustment *adjustment
766 = ((EQ (Qhorizontal, axis)
767 ? gtk_scrolled_window_get_hadjustment
768 : gtk_scrolled_window_get_vadjustment)
769 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)));
770 double final_value = XFASTINT (value);
771 if (EQ (Qt, relative))
772 final_value += gtk_adjustment_get_value (adjustment);
773 gtk_adjustment_set_value (adjustment, final_value);
774 return Qnil;
775 }
776
777
778 DEFUN ("xwidget-size-request",
779 Fxwidget_size_request, Sxwidget_size_request,
780 1, 1, 0,
781 doc: /* Return the desired size of the XWIDGET.
782 This can be used to read the xwidget desired size, and resizes the
783 Emacs allocated area accordingly. */)
784 (Lisp_Object xwidget)
785 {
786 CHECK_XWIDGET (xwidget);
787 GtkRequisition requisition;
788 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
789 return list2 (make_number (requisition.width),
790 make_number (requisition.height));
791 }
792
793 DEFUN ("xwidgetp",
794 Fxwidgetp, Sxwidgetp,
795 1, 1, 0,
796 doc: /* Return t if OBJECT is an xwidget. */)
797 (Lisp_Object object)
798 {
799 return XWIDGETP (object) ? Qt : Qnil;
800 }
801
802 DEFUN ("xwidget-view-p",
803 Fxwidget_view_p, Sxwidget_view_p,
804 1, 1, 0,
805 doc: /* Return t if OBJECT is an xwidget-view. */)
806 (Lisp_Object object)
807 {
808 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
809 }
810
811 DEFUN ("xwidget-info",
812 Fxwidget_info, Sxwidget_info,
813 1, 1, 0,
814 doc: /* Return XWIDGET properties in a vector.
815 Currently [TYPE TITLE WIDTH HEIGHT]. */)
816 (Lisp_Object xwidget)
817 {
818 CHECK_XWIDGET (xwidget);
819 struct xwidget *xw = XXWIDGET (xwidget);
820 return CALLN (Fvector, xw->type, xw->title,
821 make_natnum (xw->width), make_natnum (xw->height));
822 }
823
824 DEFUN ("xwidget-view-info",
825 Fxwidget_view_info, Sxwidget_view_info,
826 1, 1, 0,
827 doc: /* Return properties of XWIDGET-VIEW in a vector.
828 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
829 (Lisp_Object xwidget_view)
830 {
831 CHECK_XWIDGET_VIEW (xwidget_view);
832 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
833 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
834 make_number (xv->clip_right), make_number (xv->clip_bottom),
835 make_number (xv->clip_top), make_number (xv->clip_left));
836 }
837
838 DEFUN ("xwidget-view-model",
839 Fxwidget_view_model, Sxwidget_view_model,
840 1, 1, 0,
841 doc: /* Return the model associated with XWIDGET-VIEW. */)
842 (Lisp_Object xwidget_view)
843 {
844 CHECK_XWIDGET_VIEW (xwidget_view);
845 return XXWIDGET_VIEW (xwidget_view)->model;
846 }
847
848 DEFUN ("xwidget-view-window",
849 Fxwidget_view_window, Sxwidget_view_window,
850 1, 1, 0,
851 doc: /* Return the window of XWIDGET-VIEW. */)
852 (Lisp_Object xwidget_view)
853 {
854 CHECK_XWIDGET_VIEW (xwidget_view);
855 return XXWIDGET_VIEW (xwidget_view)->w;
856 }
857
858
859 DEFUN ("delete-xwidget-view",
860 Fdelete_xwidget_view, Sdelete_xwidget_view,
861 1, 1, 0,
862 doc: /* Delete the XWIDGET-VIEW. */)
863 (Lisp_Object xwidget_view)
864 {
865 CHECK_XWIDGET_VIEW (xwidget_view);
866 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
867 gtk_widget_destroy (xv->widgetwindow);
868 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
869 /* xv->model still has signals pointing to the view. There can be
870 several views. Find the matching signals and delete them all. */
871 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
872 G_SIGNAL_MATCH_DATA,
873 0, 0, 0, 0,
874 xv->widget);
875 return Qnil;
876 }
877
878 DEFUN ("xwidget-view-lookup",
879 Fxwidget_view_lookup, Sxwidget_view_lookup,
880 1, 2, 0,
881 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
882 If WINDOW is unspecified or nil, use the selected window.
883 Return nil if no association is found. */)
884 (Lisp_Object xwidget, Lisp_Object window)
885 {
886 CHECK_XWIDGET (xwidget);
887
888 if (NILP (window))
889 window = Fselected_window ();
890 CHECK_WINDOW (window);
891
892 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
893 tail = XCDR (tail))
894 {
895 Lisp_Object xwidget_view = XCAR (tail);
896 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
897 && EQ (Fxwidget_view_window (xwidget_view), window))
898 return xwidget_view;
899 }
900
901 return Qnil;
902 }
903
904 DEFUN ("xwidget-plist",
905 Fxwidget_plist, Sxwidget_plist,
906 1, 1, 0,
907 doc: /* Return the plist of XWIDGET. */)
908 (Lisp_Object xwidget)
909 {
910 CHECK_XWIDGET (xwidget);
911 return XXWIDGET (xwidget)->plist;
912 }
913
914 DEFUN ("xwidget-buffer",
915 Fxwidget_buffer, Sxwidget_buffer,
916 1, 1, 0,
917 doc: /* Return the buffer of XWIDGET. */)
918 (Lisp_Object xwidget)
919 {
920 CHECK_XWIDGET (xwidget);
921 return XXWIDGET (xwidget)->buffer;
922 }
923
924 DEFUN ("set-xwidget-plist",
925 Fset_xwidget_plist, Sset_xwidget_plist,
926 2, 2, 0,
927 doc: /* Replace the plist of XWIDGET with PLIST.
928 Returns PLIST. */)
929 (Lisp_Object xwidget, Lisp_Object plist)
930 {
931 CHECK_XWIDGET (xwidget);
932 CHECK_LIST (plist);
933
934 XXWIDGET (xwidget)->plist = plist;
935 return plist;
936 }
937
938 DEFUN ("set-xwidget-query-on-exit-flag",
939 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
940 2, 2, 0,
941 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
942 If the second argument FLAG is non-nil, Emacs will query the user before
943 exiting or killing a buffer if XWIDGET is running.
944 This function returns FLAG. */)
945 (Lisp_Object xwidget, Lisp_Object flag)
946 {
947 CHECK_XWIDGET (xwidget);
948 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
949 return flag;
950 }
951
952 DEFUN ("xwidget-query-on-exit-flag",
953 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
954 1, 1, 0,
955 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
956 (Lisp_Object xwidget)
957 {
958 CHECK_XWIDGET (xwidget);
959 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
960 }
961
962 void
963 syms_of_xwidget (void)
964 {
965 defsubr (&Smake_xwidget);
966 defsubr (&Sxwidgetp);
967 DEFSYM (Qxwidgetp, "xwidgetp");
968 defsubr (&Sxwidget_view_p);
969 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
970 defsubr (&Sxwidget_info);
971 defsubr (&Sxwidget_view_info);
972 defsubr (&Sxwidget_resize);
973 defsubr (&Sget_buffer_xwidgets);
974 defsubr (&Sxwidget_view_model);
975 defsubr (&Sxwidget_view_window);
976 defsubr (&Sxwidget_view_lookup);
977 defsubr (&Sxwidget_query_on_exit_flag);
978 defsubr (&Sset_xwidget_query_on_exit_flag);
979
980 defsubr (&Sxwidget_webkit_goto_uri);
981 defsubr (&Sxwidget_webkit_execute_script);
982 defsubr (&Sxwidget_webkit_get_title);
983 DEFSYM (Qwebkit_osr, "webkit-osr");
984
985 defsubr (&Sxwidget_size_request);
986 defsubr (&Sdelete_xwidget_view);
987
988 defsubr (&Sxwidget_plist);
989 defsubr (&Sxwidget_buffer);
990 defsubr (&Sset_xwidget_plist);
991
992 defsubr (&Sxwidget_set_adjustment);
993
994 DEFSYM (Qxwidget, "xwidget");
995
996 DEFSYM (QCxwidget, ":xwidget");
997 DEFSYM (QCtitle, ":title");
998
999 /* Do not forget to update the docstring of make-xwidget if you add
1000 new types. */
1001
1002 DEFSYM (Qvertical, "vertical");
1003 DEFSYM (Qhorizontal, "horizontal");
1004
1005 DEFSYM (QCplist, ":plist");
1006
1007 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1008 doc: /* xwidgets list. */);
1009 Vxwidget_list = Qnil;
1010
1011 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1012 doc: /* xwidget views list. */);
1013 Vxwidget_view_list = Qnil;
1014
1015 Fprovide (intern ("xwidget-internal"), Qnil);
1016 }
1017
1018
1019 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
1020 valid xwidget specification is a list whose car is the symbol
1021 `xwidget', and whose rest is a property list. The property list must
1022 contain a value for key `:type'. That value must be the name of a
1023 supported xwidget type. The rest of the property list depends on the
1024 xwidget type. */
1025
1026 bool
1027 valid_xwidget_spec_p (Lisp_Object object)
1028 {
1029 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1030 }
1031
1032
1033 /* Find a value associated with key in spec. */
1034 static Lisp_Object
1035 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1036 {
1037 Lisp_Object tail;
1038
1039 eassert (valid_xwidget_spec_p (spec));
1040
1041 for (tail = XCDR (spec);
1042 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1043 {
1044 if (EQ (XCAR (tail), key))
1045 return XCAR (XCDR (tail));
1046 }
1047
1048 return Qnil;
1049 }
1050
1051
1052 void
1053 xwidget_view_delete_all_in_window (struct window *w)
1054 {
1055 struct xwidget_view *xv = NULL;
1056 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1057 tail = XCDR (tail))
1058 {
1059 if (XWIDGET_VIEW_P (XCAR (tail)))
1060 {
1061 xv = XXWIDGET_VIEW (XCAR (tail));
1062 if (XWINDOW (xv->w) == w)
1063 {
1064 Fdelete_xwidget_view (XCAR (tail));
1065 }
1066 }
1067 }
1068 }
1069
1070 static struct xwidget_view *
1071 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1072 {
1073 Lisp_Object xwidget, window, ret;
1074 XSETXWIDGET (xwidget, xw);
1075 XSETWINDOW (window, w);
1076
1077 ret = Fxwidget_view_lookup (xwidget, window);
1078
1079 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1080 }
1081
1082 struct xwidget *
1083 lookup_xwidget (Lisp_Object spec)
1084 {
1085 /* When a xwidget lisp spec is found initialize the C struct that is
1086 used in the C code. This is done by redisplay so values change
1087 if the spec changes. So, take special care of one-shot events. */
1088 Lisp_Object value;
1089 struct xwidget *xw;
1090
1091 value = xwidget_spec_value (spec, QCxwidget);
1092 xw = XXWIDGET (value);
1093
1094 return xw;
1095 }
1096
1097 /* Set up detection of touched xwidget. */
1098 static void
1099 xwidget_start_redisplay (void)
1100 {
1101 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1102 tail = XCDR (tail))
1103 {
1104 if (XWIDGET_VIEW_P (XCAR (tail)))
1105 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1106 }
1107 }
1108
1109 /* The xwidget was touched during redisplay, so it isn't a candidate
1110 for hiding. */
1111 static void
1112 xwidget_touch (struct xwidget_view *xv)
1113 {
1114 xv->redisplayed = true;
1115 }
1116
1117 static bool
1118 xwidget_touched (struct xwidget_view *xv)
1119 {
1120 return xv->redisplayed;
1121 }
1122
1123 /* Redisplay has ended, now we should hide untouched xwidgets. */
1124 void
1125 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1126 {
1127 int i;
1128 int area;
1129
1130 xwidget_start_redisplay ();
1131 /* Iterate desired glyph matrix of window here, hide gtk widgets
1132 not in the desired matrix.
1133
1134 This only takes care of xwidgets in active windows. If a window
1135 goes away from the screen, xwidget views must be deleted.
1136
1137 dump_glyph_matrix (matrix, 2); */
1138 for (i = 0; i < matrix->nrows; ++i)
1139 {
1140 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1141 struct glyph_row *row;
1142 row = MATRIX_ROW (matrix, i);
1143 if (row->enabled_p)
1144 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1145 {
1146 struct glyph *glyph = row->glyphs[area];
1147 struct glyph *glyph_end = glyph + row->used[area];
1148 for (; glyph < glyph_end; ++glyph)
1149 if (glyph->type == XWIDGET_GLYPH)
1150 {
1151 /* The only call to xwidget_end_redisplay is in dispnew.
1152 xwidget_end_redisplay (w->current_matrix); */
1153 xwidget_touch (xwidget_view_lookup (glyph->u.xwidget, w));
1154 }
1155 }
1156 }
1157
1158 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1159 tail = XCDR (tail))
1160 {
1161 if (XWIDGET_VIEW_P (XCAR (tail)))
1162 {
1163 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1164
1165 /* "touched" is only meaningful for the current window, so
1166 disregard other views. */
1167 if (XWINDOW (xv->w) == w)
1168 {
1169 if (xwidget_touched (xv))
1170 xwidget_show_view (xv);
1171 else
1172 xwidget_hide_view (xv);
1173 }
1174 }
1175 }
1176 }
1177
1178 /* Kill all xwidget in BUFFER. */
1179 void
1180 kill_buffer_xwidgets (Lisp_Object buffer)
1181 {
1182 Lisp_Object tail, xwidget;
1183 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1184 {
1185 xwidget = XCAR (tail);
1186 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1187 /* TODO free the GTK things in xw. */
1188 {
1189 CHECK_XWIDGET (xwidget);
1190 struct xwidget *xw = XXWIDGET (xwidget);
1191 if (xw->widget_osr && xw->widgetwindow_osr)
1192 {
1193 gtk_widget_destroy (xw->widget_osr);
1194 gtk_widget_destroy (xw->widgetwindow_osr);
1195 }
1196 }
1197 }
1198 }