]> code.delx.au - gnu-emacs/blob - src/xwidget.c
54b8004fec84944bf9a7d91485d9e5beefabed2b
[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 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 (WebKitWebView *webView,
391 WebKitWebFrame *frame,
392 WebKitNetworkRequest *request,
393 gchar *mimetype,
394 WebKitWebPolicyDecision *policy_decision,
395 gpointer user_data)
396 {
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))
401 {
402 webkit_web_policy_decision_download (policy_decision);
403 return TRUE;
404 }
405 else
406 return FALSE;
407 }
408
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)
416 {
417 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
418 webkit_web_navigation_action_get_original_uri (navigation_action);
419
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;
424 }
425
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)
433 {
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;
439 }
440
441 /* For gtk3 offscreen rendered widgets. */
442 static gboolean
443 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
444 {
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);
448
449 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
450 cairo_clip (cr);
451
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;
457 }
458
459 static gboolean
460 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
461 gpointer user_data)
462 {
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);
467
468 /* TODO: This might leak events. They should be deallocated later,
469 perhaps in xwgir_event_cb. */
470 gtk_main_do_event (eventcopy);
471
472 /* Don't propagate this event further. */
473 return TRUE;
474 }
475
476 static gboolean
477 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
478 gpointer data)
479 {
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;
486 }
487
488
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)
494 {
495 struct xwidget_view *xv = allocate_xwidget_view ();
496 Lisp_Object val;
497
498 XSETXWIDGET_VIEW (val, xv);
499 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
500
501 XSETWINDOW (xv->w, s->w);
502 XSETXWIDGET (xv->model, xww);
503
504 if (EQ (xww->type, Qwebkit_osr))
505 {
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);
510
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);
514
515 if (EQ (xww->type, Qwebkit_osr))
516 {
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);
523 }
524 else
525 {
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);
529 }
530 g_signal_connect (G_OBJECT (xv->widget), "draw",
531 G_CALLBACK (xwidget_osr_draw_cb), NULL);
532 }
533
534 /* Widget realization.
535
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. */
541
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);
546
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);
553
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);
561
562 return xv;
563 }
564
565 void
566 x_draw_xwidget_glyph_string (struct glyph_string *s)
567 {
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;
577
578 int x = s->x;
579 int y = s->y + (s->height / 2) - (xww->height / 2);
580
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);
584
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));
598
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);
603
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;
616
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);
621
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)
630 {
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);
635
636 xv->clip_right = clip_right;
637 xv->clip_bottom = clip_bottom;
638 xv->clip_top = clip_top;
639 xv->clip_left = clip_left;
640 }
641
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))
647 {
648 gtk_widget_queue_draw (xv->widgetwindow);
649 gtk_widget_queue_draw (xv->widget);
650 }
651 }
652
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)) \
658 { \
659 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
660 return Qnil; \
661 }
662
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)
668 {
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;
673 }
674
675
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)
681 {
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;
687 }
688
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)
696 {
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)
702 {
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 ("");
706 }
707 return build_string (str);
708 }
709
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)
713 {
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);
720
721 xw->width = w;
722 xw->height = h;
723
724 /* If there is an offscreen widget resize it first. */
725 if (xw->widget_osr)
726 {
727 /* Use minimum size. */
728 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr),
729 xw->width, xw->height);
730
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);
739
740 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
741
742 }
743
744 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
745 {
746 if (XWIDGET_VIEW_P (XCAR (tail)))
747 {
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);
752 }
753 }
754
755 return Qnil;
756 }
757
758
759
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)
768 {
769 CHECK_XWIDGET (xwidget);
770 CHECK_NUMBER (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;
782 }
783
784
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)
792 {
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));
798 }
799
800 DEFUN ("xwidgetp",
801 Fxwidgetp, Sxwidgetp,
802 1, 1, 0,
803 doc: /* Return t if OBJECT is an xwidget. */)
804 (Lisp_Object object)
805 {
806 return XWIDGETP (object) ? Qt : Qnil;
807 }
808
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)
814 {
815 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
816 }
817
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)
824 {
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));
829 }
830
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)
837 {
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));
843 }
844
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)
850 {
851 CHECK_XWIDGET_VIEW (xwidget_view);
852 return XXWIDGET_VIEW (xwidget_view)->model;
853 }
854
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)
860 {
861 CHECK_XWIDGET_VIEW (xwidget_view);
862 return XXWIDGET_VIEW (xwidget_view)->w;
863 }
864
865
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)
871 {
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;
883 }
884
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)
892 {
893 CHECK_XWIDGET (xwidget);
894
895 if (NILP (window))
896 window = Fselected_window ();
897 CHECK_WINDOW (window);
898
899 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
900 tail = XCDR (tail))
901 {
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;
906 }
907
908 return Qnil;
909 }
910
911 DEFUN ("xwidget-plist",
912 Fxwidget_plist, Sxwidget_plist,
913 1, 1, 0,
914 doc: /* Return the plist of XWIDGET. */)
915 (Lisp_Object xwidget)
916 {
917 CHECK_XWIDGET (xwidget);
918 return XXWIDGET (xwidget)->plist;
919 }
920
921 DEFUN ("xwidget-buffer",
922 Fxwidget_buffer, Sxwidget_buffer,
923 1, 1, 0,
924 doc: /* Return the buffer of XWIDGET. */)
925 (Lisp_Object xwidget)
926 {
927 CHECK_XWIDGET (xwidget);
928 return XXWIDGET (xwidget)->buffer;
929 }
930
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)
937 {
938 CHECK_XWIDGET (xwidget);
939 CHECK_LIST (plist);
940
941 XXWIDGET (xwidget)->plist = plist;
942 return plist;
943 }
944
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)
953 {
954 CHECK_XWIDGET (xwidget);
955 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
956 return flag;
957 }
958
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)
964 {
965 CHECK_XWIDGET (xwidget);
966 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
967 }
968
969 void
970 syms_of_xwidget (void)
971 {
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);
986
987 defsubr (&Sxwidget_webkit_goto_uri);
988 defsubr (&Sxwidget_webkit_execute_script);
989 defsubr (&Sxwidget_webkit_get_title);
990 DEFSYM (Qwebkit_osr, "webkit-osr");
991
992 defsubr (&Sxwidget_size_request);
993 defsubr (&Sdelete_xwidget_view);
994
995 defsubr (&Sxwidget_plist);
996 defsubr (&Sxwidget_buffer);
997 defsubr (&Sset_xwidget_plist);
998
999 defsubr (&Sxwidget_set_adjustment);
1000
1001 DEFSYM (Qxwidget, "xwidget");
1002
1003 DEFSYM (QCxwidget, ":xwidget");
1004 DEFSYM (QCtitle, ":title");
1005
1006 /* Do not forget to update the docstring of make-xwidget if you add
1007 new types. */
1008
1009 DEFSYM (Qvertical, "vertical");
1010 DEFSYM (Qhorizontal, "horizontal");
1011
1012 DEFSYM (QCplist, ":plist");
1013
1014 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
1015 doc: /* xwidgets list. */);
1016 Vxwidget_list = Qnil;
1017
1018 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
1019 doc: /* xwidget views list. */);
1020 Vxwidget_view_list = Qnil;
1021
1022 Fprovide (intern ("xwidget-internal"), Qnil);
1023 }
1024
1025
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. */
1032
1033 bool
1034 valid_xwidget_spec_p (Lisp_Object object)
1035 {
1036 return CONSP (object) && EQ (XCAR (object), Qxwidget);
1037 }
1038
1039
1040 /* Find a value associated with key in spec. */
1041 static Lisp_Object
1042 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
1043 {
1044 Lisp_Object tail;
1045
1046 eassert (valid_xwidget_spec_p (spec));
1047
1048 for (tail = XCDR (spec);
1049 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
1050 {
1051 if (EQ (XCAR (tail), key))
1052 return XCAR (XCDR (tail));
1053 }
1054
1055 return Qnil;
1056 }
1057
1058
1059 void
1060 xwidget_view_delete_all_in_window (struct window *w)
1061 {
1062 struct xwidget_view *xv = NULL;
1063 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1064 tail = XCDR (tail))
1065 {
1066 if (XWIDGET_VIEW_P (XCAR (tail)))
1067 {
1068 xv = XXWIDGET_VIEW (XCAR (tail));
1069 if (XWINDOW (xv->w) == w)
1070 {
1071 Fdelete_xwidget_view (XCAR (tail));
1072 }
1073 }
1074 }
1075 }
1076
1077 static struct xwidget_view *
1078 xwidget_view_lookup (struct xwidget *xw, struct window *w)
1079 {
1080 Lisp_Object xwidget, window, ret;
1081 XSETXWIDGET (xwidget, xw);
1082 XSETWINDOW (window, w);
1083
1084 ret = Fxwidget_view_lookup (xwidget, window);
1085
1086 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1087 }
1088
1089 struct xwidget *
1090 lookup_xwidget (Lisp_Object spec)
1091 {
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;
1097
1098 value = xwidget_spec_value (spec, QCxwidget);
1099 xw = XXWIDGET (value);
1100
1101 return xw;
1102 }
1103
1104 /* Set up detection of touched xwidget. */
1105 static void
1106 xwidget_start_redisplay (void)
1107 {
1108 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1109 tail = XCDR (tail))
1110 {
1111 if (XWIDGET_VIEW_P (XCAR (tail)))
1112 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1113 }
1114 }
1115
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)
1120 {
1121 xv->redisplayed = true;
1122 }
1123
1124 static bool
1125 xwidget_touched (struct xwidget_view *xv)
1126 {
1127 return xv->redisplayed;
1128 }
1129
1130 /* Redisplay has ended, now we should hide untouched xwidgets. */
1131 void
1132 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1133 {
1134 int i;
1135 int area;
1136
1137 xwidget_start_redisplay ();
1138 /* Iterate desired glyph matrix of window here, hide gtk widgets
1139 not in the desired matrix.
1140
1141 This only takes care of xwidgets in active windows. If a window
1142 goes away from the screen, xwidget views must be deleted.
1143
1144 dump_glyph_matrix (matrix, 2); */
1145 for (i = 0; i < matrix->nrows; ++i)
1146 {
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)
1152 {
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)
1157 {
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));
1161 }
1162 }
1163 }
1164
1165 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1166 tail = XCDR (tail))
1167 {
1168 if (XWIDGET_VIEW_P (XCAR (tail)))
1169 {
1170 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1171
1172 /* "touched" is only meaningful for the current window, so
1173 disregard other views. */
1174 if (XWINDOW (xv->w) == w)
1175 {
1176 if (xwidget_touched (xv))
1177 xwidget_show_view (xv);
1178 else
1179 xwidget_hide_view (xv);
1180 }
1181 }
1182 }
1183 }
1184
1185 /* Kill all xwidget in BUFFER. */
1186 void
1187 kill_buffer_xwidgets (Lisp_Object buffer)
1188 {
1189 Lisp_Object tail, xwidget;
1190 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1191 {
1192 xwidget = XCAR (tail);
1193 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1194 /* TODO free the GTK things in xw. */
1195 {
1196 CHECK_XWIDGET (xwidget);
1197 struct xwidget *xw = XXWIDGET (xwidget);
1198 if (xw->widget_osr && xw->widgetwindow_osr)
1199 {
1200 gtk_widget_destroy (xw->widget_osr);
1201 gtk_widget_destroy (xw->widgetwindow_osr);
1202 }
1203 }
1204 }
1205 }