/* Support for embedding graphical components in a buffer. Copyright (C) 2011-2015 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include #include #include #include #ifdef HAVE_X_WINDOWS #include "lisp.h" #include "blockinput.h" #include "syssignal.h" #include "xterm.h" #include #ifndef makedev # include #endif #ifdef BSD_SYSTEM # include #endif #include "systime.h" #ifndef INCLUDED_FCNTL # include #endif #include #include #include #include #include "charset.h" #include "character.h" #include "coding.h" #include "ccl.h" #include "frame.h" #include "dispextern.h" #include "fontset.h" #include "termhooks.h" #include "termopts.h" #include "termchar.h" #include "disptab.h" #include "buffer.h" #include "window.h" #include "keyboard.h" #include "intervals.h" #include "process.h" #include "atimer.h" #include "keymap.h" #ifdef USE_X_TOOLKIT #include #endif #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "gtkutil.h" #include "font.h" #endif /* HAVE_X_WINDOWS */ #include #include #include #include "emacsgtkfixed.h" #include #include #include #include #include #include #include #include #include "xwidget.h" static struct xwidget * allocate_xwidget (void) { return ALLOCATE_PSEUDOVECTOR (struct xwidget, height, PVEC_XWIDGET); } static struct xwidget_view * allocate_xwidget_view (void) { return ALLOCATE_PSEUDOVECTOR (struct xwidget_view, redisplayed, PVEC_XWIDGET_VIEW); } #define XSETXWIDGET(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET)) #define XSETXWIDGET_VIEW(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET_VIEW)) struct xwidget_view *xwidget_view_lookup (struct xwidget *, struct window *); Lisp_Object xwidget_spec_value (Lisp_Object , Lisp_Object , int *); gboolean offscreen_damage_event (GtkWidget * , GdkEvent * , gpointer ); void webkit_document_load_finished_cb (WebKitWebView *, WebKitWebFrame *, gpointer ); gboolean webkit_download_cb (WebKitWebView *, WebKitDownload *, gpointer); gboolean webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *, WebKitWebFrame *, WebKitNetworkRequest *, gchar *, WebKitWebPolicyDecision *, gpointer); gboolean webkit_new_window_policy_decision_requested_cb (WebKitWebView *, WebKitWebFrame *, WebKitNetworkRequest *, WebKitWebNavigationAction *, WebKitWebPolicyDecision *, gpointer); gboolean webkit_navigation_policy_decision_requested_cb (WebKitWebView *, WebKitWebFrame *, WebKitNetworkRequest *, WebKitWebNavigationAction *, WebKitWebPolicyDecision *, gpointer); DEFUN ("make-xwidget", Fmake_xwidget, Smake_xwidget, 7, 8, 0, doc: /* Make an xwidget from BEG to END of TYPE. If BUFFER is nil it uses the current buffer. If BUFFER is a string and no such buffer exists, it is created. TYPE is a symbol which can take one of the following values: - webkit_osr Returns the newly constructed xwidget, or nil if construction fails. */) (Lisp_Object beg, Lisp_Object end, Lisp_Object type, Lisp_Object title, Lisp_Object width, Lisp_Object height, Lisp_Object arguments, Lisp_Object buffer) { //should work a bit like "make-button"(make-button BEG END &rest PROPERTIES) // arg "type" and fwd should be keyword args eventually //(make-xwidget 3 3 'button "oei" 31 31 nil) //(xwidget-info (car xwidget-list)) struct xwidget *xw = allocate_xwidget (); Lisp_Object val; xw->type = type; xw->title = title; if (NILP (buffer)) buffer = Fcurrent_buffer (); // no need to gcpro because // Fcurrent_buffer doesn't // call Feval/eval_sub. else buffer = Fget_buffer_create (buffer); xw->buffer = buffer; xw->height = XFASTINT (height); xw->width = XFASTINT (width); xw->kill_without_query = 0; XSETXWIDGET (val, xw); // set the vectorlike_header of VAL // with the correct value Vxwidget_list = Fcons (val, Vxwidget_list); xw->widgetwindow_osr = NULL; xw->widget_osr = NULL; xw->plist = Qnil; if (EQ (xw->type, Qwebkit_osr)) { block_input (); xw->widgetwindow_osr = gtk_offscreen_window_new (); gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, xw->height); xw->widgetscrolledwindow_osr = NULL; //webkit osr is the //only scrolled //component atm if (EQ (xw->type, Qwebkit_osr)) { xw->widgetscrolledwindow_osr = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (xw-> widgetscrolledwindow_osr), xw->height); gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (xw-> widgetscrolledwindow_osr), xw->width); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS); xw->widget_osr = webkit_web_view_new (); gtk_container_add (GTK_CONTAINER (xw->widgetscrolledwindow_osr), GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr))); } gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, xw->height); if (EQ (xw->type, Qwebkit_osr)) { gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr), xw->widgetscrolledwindow_osr); } else { gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr), xw->widget_osr); } gtk_widget_show (xw->widget_osr); gtk_widget_show (xw->widgetwindow_osr); gtk_widget_show (xw->widgetscrolledwindow_osr); /* store some xwidget data in the gtk widgets for convenient retrieval in the event handlers. */ g_object_set_data (G_OBJECT (xw->widget_osr), XG_XWIDGET, (gpointer) (xw)); g_object_set_data (G_OBJECT (xw->widgetwindow_osr), XG_XWIDGET, (gpointer) (xw)); /* signals */ if (EQ (xw->type, Qwebkit_osr)) { g_signal_connect (G_OBJECT (xw->widget_osr), "document-load-finished", G_CALLBACK (webkit_document_load_finished_cb), xw); g_signal_connect (G_OBJECT (xw->widget_osr), "download-requested", G_CALLBACK (webkit_download_cb), xw); g_signal_connect (G_OBJECT (xw->widget_osr), "mime-type-policy-decision-requested", G_CALLBACK (webkit_mime_type_policy_typedecision_requested_cb), xw); g_signal_connect (G_OBJECT (xw->widget_osr), "new-window-policy-decision-requested", G_CALLBACK (webkit_new_window_policy_decision_requested_cb), xw); g_signal_connect (G_OBJECT (xw->widget_osr), "navigation-policy-decision-requested", G_CALLBACK (webkit_navigation_policy_decision_requested_cb), xw); } unblock_input (); } return val; } DEFUN ("get-buffer-xwidgets", Fget_buffer_xwidgets, Sget_buffer_xwidgets, 1, 1, 0, doc: /* Return a list of xwidgets associated with BUFFER. BUFFER may be a buffer or the name of one. */) (Lisp_Object buffer) { Lisp_Object xw, tail, xw_list; if (NILP (buffer)) return Qnil; buffer = Fget_buffer (buffer); if (NILP (buffer)) return Qnil; xw_list = Qnil; for (tail = Vxwidget_list; CONSP (tail); tail = XCDR (tail)) { xw = XCAR (tail); if (XWIDGETP (xw) && EQ (Fxwidget_buffer (xw), buffer)) xw_list = Fcons (xw, xw_list); } return xw_list; } static int xwidget_hidden (struct xwidget_view *xv) { return xv->hidden; } static void xwidget_show_view (struct xwidget_view *xv) { xv->hidden = 0; gtk_widget_show (xv->widgetwindow); gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow, xv->x + xv->clip_left, xv->y + xv->clip_top); } /* Hide an xvidget view. */ static void xwidget_hide_view (struct xwidget_view *xv) { xv->hidden = 1; gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow, 10000, 10000); } /* When the off-screen webkit master view changes this signal is called. It copies the bitmap from the off-screen instance. */ gboolean offscreen_damage_event (GtkWidget * widget, GdkEvent * event, gpointer xv_widget) { // Queue a redraw of onscreen widget. // There is a guard against receiving an invalid widget, // which should only happen if we failed to remove the // specific signal handler for the damage event. if (GTK_IS_WIDGET (xv_widget)) gtk_widget_queue_draw (GTK_WIDGET (xv_widget)); else printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n", (void *) xv_widget); return FALSE; } static void store_xwidget_event_string (struct xwidget *xw, const char *eventname, const char *eventstr) { struct input_event event; Lisp_Object xwl; XSETXWIDGET (xwl, xw); EVENT_INIT (event); event.kind = XWIDGET_EVENT; event.frame_or_window = Qnil; event.arg = Qnil; event.arg = Fcons (build_string (eventstr), event.arg); event.arg = Fcons (xwl, event.arg); event.arg = Fcons (intern (eventname), event.arg); kbd_buffer_store_event (&event); } //TODO deprecated, use load-status void webkit_document_load_finished_cb (WebKitWebView * webkitwebview, WebKitWebFrame * arg1, gpointer data) { struct xwidget *xw = (struct xwidget *) g_object_get_data (G_OBJECT (webkitwebview), XG_XWIDGET); store_xwidget_event_string (xw, "document-load-finished", ""); } gboolean webkit_download_cb (WebKitWebView * webkitwebview, WebKitDownload * arg1, gpointer data) { struct xwidget *xw = (struct xwidget *) g_object_get_data (G_OBJECT (webkitwebview), XG_XWIDGET); store_xwidget_event_string (xw, "download-requested", webkit_download_get_uri (arg1)); return FALSE; } gboolean webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *webView, WebKitWebFrame *frame, WebKitNetworkRequest * request, gchar * mimetype, WebKitWebPolicyDecision *policy_decision, gpointer user_data) { // This function makes webkit send a download signal for all unknown // mime types. TODO Defer the decision to lisp, so that its possible // to make Emacs handle teext mime for instance.xs if (!webkit_web_view_can_show_mime_type (webView, mimetype)) { webkit_web_policy_decision_download (policy_decision); return TRUE; } else { return FALSE; } } gboolean webkit_new_window_policy_decision_requested_cb (WebKitWebView *webView, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) { struct xwidget *xw = (struct xwidget *) g_object_get_data (G_OBJECT (webView), XG_XWIDGET); webkit_web_navigation_action_get_original_uri (navigation_action); store_xwidget_event_string (xw, "new-window-policy-decision-requested", webkit_web_navigation_action_get_original_uri (navigation_action)); return FALSE; } gboolean webkit_navigation_policy_decision_requested_cb (WebKitWebView *webView, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision * policy_decision, gpointer user_data) { struct xwidget *xw = (struct xwidget *) g_object_get_data (G_OBJECT (webView), XG_XWIDGET); store_xwidget_event_string (xw, "navigation-policy-decision-requested", webkit_web_navigation_action_get_original_uri (navigation_action)); return FALSE; } // For gtk3 offscreen rendered widgets. static gboolean xwidget_osr_draw_cb (GtkWidget * widget, cairo_t * cr, gpointer data) { struct xwidget *xw = (struct xwidget *) g_object_get_data (G_OBJECT (widget), XG_XWIDGET); struct xwidget_view *xv = (struct xwidget_view *) g_object_get_data (G_OBJECT (widget), XG_XWIDGET_VIEW); cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom); cairo_clip (cr); if (xw->widgetscrolledwindow_osr != NULL) gtk_widget_draw (xw->widgetscrolledwindow_osr, cr); else gtk_widget_draw (xw->widget_osr, cr); return FALSE; } static gboolean xwidget_osr_event_forward (GtkWidget * widget, GdkEvent * event, gpointer user_data) { /* Copy events that arrive at the outer widget to the offscreen widget. */ struct xwidget *xw = (struct xwidget *) g_object_get_data (G_OBJECT (widget), XG_XWIDGET); GdkEvent *eventcopy = gdk_event_copy (event); eventcopy->any.window = gtk_widget_get_window (xw->widget_osr); //TODO This might leak events. They should be deallocated later, //perhaps in xwgir_event_cb gtk_main_do_event (eventcopy); return TRUE; //dont propagate this event furter } static gboolean xwidget_osr_event_set_embedder (GtkWidget * widget, GdkEvent * event, gpointer data) { struct xwidget_view *xv = (struct xwidget_view *) data; struct xwidget *xww = XXWIDGET (xv->model); gdk_offscreen_window_set_embedder (gtk_widget_get_window (xww->widgetwindow_osr), gtk_widget_get_window (xv->widget)); return FALSE; } /* Initializes and does initial placement of an xwidget view on screen. */ static struct xwidget_view * xwidget_init_view (struct xwidget *xww, struct glyph_string *s, int x, int y) { struct xwidget_view *xv = allocate_xwidget_view (); Lisp_Object val; XSETXWIDGET_VIEW (val, xv); Vxwidget_view_list = Fcons (val, Vxwidget_view_list); XSETWINDOW (xv->w, s->w); XSETXWIDGET (xv->model, xww); if (EQ (xww->type, Qwebkit_osr)) { xv->widget = gtk_drawing_area_new (); // Expose event handling. gtk_widget_set_app_paintable (xv->widget, TRUE); gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK); /* Draw the view on damage-event */ g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event", G_CALLBACK (offscreen_damage_event), xv->widget); if (EQ (xww->type, Qwebkit_osr)) { g_signal_connect (G_OBJECT (xv->widget), "button-press-event", G_CALLBACK (xwidget_osr_event_forward), NULL); g_signal_connect (G_OBJECT (xv->widget), "button-release-event", G_CALLBACK (xwidget_osr_event_forward), NULL); g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event", G_CALLBACK (xwidget_osr_event_forward), NULL); } else { // xwgir debug , orthogonal to forwarding g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event", G_CALLBACK (xwidget_osr_event_set_embedder), xv); } g_signal_connect (G_OBJECT (xv->widget), "draw", G_CALLBACK (xwidget_osr_draw_cb), NULL); } // Widget realization. // Make container widget 1st, and put the actual widget inside the // container later. Drawing should crop container window if necessary // to handle case where xwidget is partially obscured by other Emacs // windows. Other containers than gtk_fixed where explored, but // gtk_fixed had the most predictable behaviour so far. xv->emacswindow = FRAME_GTK_WIDGET (s->f); xv->widgetwindow = gtk_fixed_new (); gtk_widget_set_has_window (xv->widgetwindow, TRUE); gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget); // Store some xwidget data in the gtk widgets. // The emacs frame. g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, (gpointer) (s->f)); // The xwidget. g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, (gpointer) (xww)); // The xwidget. g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, (gpointer) (xv)); // The xwidget window. g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, (gpointer) (xww)); // the xwidget view. g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, (gpointer) (xv)); gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width, xww->height); gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height); gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y); xv->x = x; xv->y = y; gtk_widget_show_all (xv->widgetwindow); return xv; } void x_draw_xwidget_glyph_string (struct glyph_string *s) { /* This method is called by the redisplay engine and places the xwidget on screen. Moving and clipping is done here. Also view initialization. */ struct xwidget *xww = s->xwidget; struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); int clip_right; int clip_bottom; int clip_top; int clip_left; int x = s->x; int y = s->y + (s->height / 2) - (xww->height / 2); int moved = 0; /* We do initialization here in the display loop because there is no other time to know things like window placement etc. */ xv = xwidget_init_view (xww, s, x, y); // Calculate clipping, which is used for all manner of onscreen // xwidget views. Each widget border can get clipped by other emacs // objects so there are four clipping variables. clip_right = min (xww->width, WINDOW_RIGHT_EDGE_X (s->w) - x - WINDOW_RIGHT_SCROLL_BAR_AREA_WIDTH (s->w) - WINDOW_RIGHT_FRINGE_WIDTH (s->w)); clip_left = max (0, WINDOW_LEFT_EDGE_X (s->w) - x + WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (s->w) + WINDOW_LEFT_FRINGE_WIDTH (s->w)); clip_bottom = min (xww->height, WINDOW_BOTTOM_EDGE_Y (s->w) - WINDOW_MODE_LINE_HEIGHT (s->w) - y); clip_top = max (0, WINDOW_TOP_EDGE_Y (s->w) - y); // We are conserned with movement of the onscreen area. The area // might sit still when the widget actually moves. This happens // when an Emacs window border moves across a widget window. So, if // any corner of the outer widget clipping window moves, that counts // as movement here, even if it looks like no movement happens // because the widget sits still inside the clipping area. The // widget can also move inside the clipping area, which happens // later moved = (xv->x + xv->clip_left != x + clip_left) || ((xv->y + xv->clip_top) != (y + clip_top)); xv->x = x; xv->y = y; if (moved) // Has it moved? { gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x + clip_left, y + clip_top); } // Clip the widget window if some parts happen to be outside // drawable area. An Emacs window is not a gtk window. A gtk window // covers the entire frame. Clipping might have changed even if we // havent actualy moved, we try figure out when we need to reclip // for real. if ((xv->clip_right != clip_right) || (xv->clip_bottom != clip_bottom) || (xv->clip_top != clip_top) || (xv->clip_left != clip_left)) { gtk_widget_set_size_request (xv->widgetwindow, clip_right + clip_left, clip_bottom + clip_top); gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, -clip_top); xv->clip_right = clip_right; xv->clip_bottom = clip_bottom; xv->clip_top = clip_top; xv->clip_left = clip_left; } // If emacs wants to repaint the area where the widget lives, queue // a redraw. It seems its possible to get out of sync with emacs // redraws so emacs background sometimes shows up instead of the // xwidgets background. It's just a visual glitch though. if (!xwidget_hidden (xv)) { gtk_widget_queue_draw (xv->widgetwindow); gtk_widget_queue_draw (xv->widget); } } // Macro that checks WEBKIT_IS_WEB_VIEW(xw->widget_osr) first #define WEBKIT_FN_INIT() \ struct xwidget* xw; \ CHECK_XWIDGET (xwidget); \ if (NILP (xwidget)) {printf("ERROR xwidget nil\n"); return Qnil;}; \ xw = XXWIDGET (xwidget); \ if (NULL == xw) printf("ERROR xw is 0\n"); \ if ((NULL == xw->widget_osr) || !WEBKIT_IS_WEB_VIEW(xw->widget_osr)){ \ printf ("ERROR xw->widget_osr does not hold a webkit instance\n");\ return Qnil;\ }; DEFUN ("xwidget-webkit-goto-uri", Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri, 2, 2, 0, doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */) (Lisp_Object xwidget, Lisp_Object uri) { WEBKIT_FN_INIT (); CHECK_STRING (uri); webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri)); return Qnil; } DEFUN ("xwidget-webkit-execute-script", Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, 2, 2, 0, doc: /* Make the Webkit XWIDGET execute javascript SCRIPT. */) (Lisp_Object xwidget, Lisp_Object script) { WEBKIT_FN_INIT (); CHECK_STRING (script); webkit_web_view_execute_script (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (script)); return Qnil; } DEFUN ("xwidget-webkit-get-title", Fxwidget_webkit_get_title, Sxwidget_webkit_get_title, 1, 1, 0, doc: /* Returns the title from the Webkit instance in XWIDGET. This can be used to work around the lack of a return value from the exec method. */ ) (Lisp_Object xwidget) { // TODO support multibyte strings WEBKIT_FN_INIT (); const gchar *str = webkit_web_view_get_title (WEBKIT_WEB_VIEW (xw->widget_osr)); if (str == 0) { // TODO maybe return Qnil instead. I suppose webkit returns // nullpointer when doc is not properly loaded or something return build_string (""); } return build_string (str); } DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, doc: /* Resize XWIDGET. NEW_WIDTH NEW_HEIGHT defines the new size. */ ) (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height) { CHECK_XWIDGET (xwidget); struct xwidget *xw = XXWIDGET (xwidget); struct xwidget_view *xv; int w, h; CHECK_NUMBER (new_width); CHECK_NUMBER (new_height); w = XFASTINT (new_width); h = XFASTINT (new_height); xw->width = w; xw->height = h; // If there is a offscreen widget resize it 1st. if (xw->widget_osr) { gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, xw->height); //minimum size gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, xw->height); gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (xw-> widgetscrolledwindow_osr), xw->height); gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (xw-> widgetscrolledwindow_osr), xw->width); gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr)); } for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { if (XWIDGET_VIEW_P (XCAR (tail))) { xv = XXWIDGET_VIEW (XCAR (tail)); if (XXWIDGET (xv->model) == xw) gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, xw->height); } } return Qnil; } DEFUN ("xwidget-set-adjustment", Fxwidget_set_adjustment, Sxwidget_set_adjustment, 4, 4, 0, doc: /* Set native scrolling for XWIDGET. AXIS can be 'vertical or 'horizontal. If RELATIVE is t, scroll relative, otherwise absolutely. VALUE is the amount to scroll, either relatively or absolutely. */) (Lisp_Object xwidget, Lisp_Object axis, Lisp_Object relative, Lisp_Object value) { CHECK_XWIDGET (xwidget); struct xwidget *xw = XXWIDGET (xwidget); GtkAdjustment *adjustment; float final_value = 0.0; adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)); if (EQ (Qvertical, axis)) { adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)); } if (EQ (Qhorizontal, axis)) { adjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)); } if (EQ (Qt, relative)) { final_value = gtk_adjustment_get_value (adjustment) + XFASTINT (value); } else { final_value = 0.0 + XFASTINT (value); } gtk_adjustment_set_value (adjustment, final_value); return Qnil; } DEFUN ("xwidget-size-request", Fxwidget_size_request, Sxwidget_size_request, 1, 1, 0, doc: /* Return the desired size of the XWIDGET. This can be used to read the xwidget desired size, and resizes the Emacs allocated area accordingly. */) (Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); GtkRequisition requisition; Lisp_Object rv; gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition); rv = Qnil; rv = Fcons (make_number (requisition.height), rv); rv = Fcons (make_number (requisition.width), rv); return rv; } DEFUN ("xwidgetp", Fxwidgetp, Sxwidgetp, 1, 1, 0, doc: /* Return t if OBJECT is a xwidget. */) (Lisp_Object object) { return XWIDGETP (object) ? Qt : Qnil; } DEFUN ("xwidget-view-p", Fxwidget_view_p, Sxwidget_view_p, 1, 1, 0, doc: /* Return t if OBJECT is a xwidget-view. */) (Lisp_Object object) { return XWIDGET_VIEW_P (object) ? Qt : Qnil; } DEFUN ("xwidget-info", Fxwidget_info, Sxwidget_info, 1, 1, 0, doc: /* Return XWIDGET properties in a vector. Currently [TYPE TITLE WIDTH HEIGHT]. */) (Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); Lisp_Object info, n; struct xwidget *xw = XXWIDGET (xwidget); info = Fmake_vector (make_number (4), Qnil); ASET (info, 0, xw->type); ASET (info, 1, xw->title); XSETFASTINT (n, xw->width); ASET (info, 2, n); XSETFASTINT (n, xw->height); ASET (info, 3, n); return info; } DEFUN ("xwidget-view-info", Fxwidget_view_info, Sxwidget_view_info, 1, 1, 0, doc: /* Return properties of XWIDGET-VIEW in a vector. Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT] */) (Lisp_Object xwidget_view) { CHECK_XWIDGET_VIEW (xwidget_view); struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); Lisp_Object info; info = Fmake_vector (make_number (6), Qnil); ASET (info, 0, make_number (xv->x)); ASET (info, 1, make_number (xv->y)); ASET (info, 2, make_number (xv->clip_right)); ASET (info, 3, make_number (xv->clip_bottom)); ASET (info, 4, make_number (xv->clip_top)); ASET (info, 5, make_number (xv->clip_left)); return info; } DEFUN ("xwidget-view-model", Fxwidget_view_model, Sxwidget_view_model, 1, 1, 0, doc: /* Return the model associated with XWIDGET-VIEW. */) (Lisp_Object xwidget_view) { CHECK_XWIDGET_VIEW (xwidget_view); return XXWIDGET_VIEW (xwidget_view)->model; } DEFUN ("xwidget-view-window", Fxwidget_view_window, Sxwidget_view_window, 1, 1, 0, doc: /* Return the window of XWIDGET-VIEW. */) (Lisp_Object xwidget_view) { CHECK_XWIDGET_VIEW (xwidget_view); return XXWIDGET_VIEW (xwidget_view)->w; } DEFUN ("delete-xwidget-view", Fdelete_xwidget_view, Sdelete_xwidget_view, 1, 1, 0, doc: /* Delete the XWIDGET-VIEW. */) (Lisp_Object xwidget_view) { CHECK_XWIDGET_VIEW (xwidget_view); struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); gtk_widget_destroy (xv->widgetwindow); Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list); // xv->model still has signals pointing to the view. There can be // several views. Find the matching signals and delete them all. g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, xv->widget); return Qnil; } DEFUN ("xwidget-view-lookup", Fxwidget_view_lookup, Sxwidget_view_lookup, 1, 2, 0, doc: /* Return the xwidget-view associated with XWIDGET in WINDOW if specified, otherwise it uses the selected window. Return nil if no association is found. */) (Lisp_Object xwidget, Lisp_Object window) { CHECK_XWIDGET (xwidget); if (NILP (window)) window = Fselected_window (); CHECK_WINDOW (window); for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { Lisp_Object xwidget_view = XCAR (tail); if (EQ (Fxwidget_view_model (xwidget_view), xwidget) && EQ (Fxwidget_view_window (xwidget_view), window)) return xwidget_view; } return Qnil; } DEFUN ("xwidget-plist", Fxwidget_plist, Sxwidget_plist, 1, 1, 0, doc: /* Return the plist of XWIDGET. */) (register Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); return XXWIDGET (xwidget)->plist; } DEFUN ("xwidget-buffer", Fxwidget_buffer, Sxwidget_buffer, 1, 1, 0, doc: /* Return the buffer of XWIDGET. */) (register Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); return XXWIDGET (xwidget)->buffer; } DEFUN ("set-xwidget-plist", Fset_xwidget_plist, Sset_xwidget_plist, 2, 2, 0, doc: /* Replace the plist of XWIDGET with PLIST. Returns PLIST. */) (register Lisp_Object xwidget, Lisp_Object plist) { CHECK_XWIDGET (xwidget); CHECK_LIST (plist); XXWIDGET (xwidget)->plist = plist; return plist; } DEFUN ("set-xwidget-query-on-exit-flag", Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag, 2, 2, 0, doc: /* Specify if query is needed for XWIDGET when Emacs is exited. If the second argument FLAG is non-nil, Emacs will queries the user before exiting or killing a buffer if XWIDGET is running. This function returns FLAG. */) (Lisp_Object xwidget, Lisp_Object flag) { CHECK_XWIDGET (xwidget); XXWIDGET (xwidget)->kill_without_query = NILP (flag); return flag; } DEFUN ("xwidget-query-on-exit-flag", Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag, 1, 1, 0, doc: /* Return the current value of query-on-exit flag for XWIDGET. */) (Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt); } void syms_of_xwidget (void) { defsubr (&Smake_xwidget); defsubr (&Sxwidgetp); DEFSYM (Qxwidgetp, "xwidgetp"); defsubr (&Sxwidget_view_p); DEFSYM (Qxwidget_view_p, "xwidget-view-p"); defsubr (&Sxwidget_info); defsubr (&Sxwidget_view_info); defsubr (&Sxwidget_resize); defsubr (&Sget_buffer_xwidgets); defsubr (&Sxwidget_view_model); defsubr (&Sxwidget_view_window); defsubr (&Sxwidget_view_lookup); defsubr (&Sxwidget_query_on_exit_flag); defsubr (&Sset_xwidget_query_on_exit_flag); #ifdef HAVE_WEBKIT_OSR defsubr (&Sxwidget_webkit_goto_uri); defsubr (&Sxwidget_webkit_execute_script); defsubr (&Sxwidget_webkit_get_title); DEFSYM (Qwebkit_osr, "webkit-osr"); #endif defsubr (&Sxwidget_size_request); defsubr (&Sdelete_xwidget_view); defsubr (&Sxwidget_plist); defsubr (&Sxwidget_buffer); defsubr (&Sset_xwidget_plist); defsubr (&Sxwidget_set_adjustment); DEFSYM (Qxwidget, "xwidget"); DEFSYM (QCxwidget, ":xwidget"); DEFSYM (QCtitle, ":title"); /* Do not forget to update the docstring of make-xwidget if you add new types. */ DEFSYM (Qvertical, "vertical"); DEFSYM (Qhorizontal, "horizontal"); DEFSYM (QCplist, ":plist"); DEFVAR_LISP ("xwidget-list", Vxwidget_list, doc: /* xwidgets list. */); Vxwidget_list = Qnil; DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list, doc: /* xwidget views list. */); Vxwidget_view_list = Qnil; Fprovide (intern ("xwidget-internal"), Qnil); } /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A valid xwidget specification is a list whose car is the symbol `xwidget', and whose rest is a property list. The property list must contain a value for key `:type'. That value must be the name of a supported xwidget type. The rest of the property list depends on the xwidget type. */ bool valid_xwidget_spec_p (Lisp_Object object) { int valid_p = false; if (CONSP (object) && EQ (XCAR (object), Qxwidget)) valid_p = true; return valid_p; } /* Find a value associated with key in spec. */ Lisp_Object xwidget_spec_value (Lisp_Object spec, Lisp_Object key, int *found) { Lisp_Object tail; eassert (valid_xwidget_spec_p (spec)); for (tail = XCDR (spec); CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail))) { if (EQ (XCAR (tail), key)) { if (found) *found = 1; return XCAR (XCDR (tail)); } } if (found) *found = 0; return Qnil; } void xwidget_view_delete_all_in_window (struct window *w) { struct xwidget_view *xv = NULL; for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { if (XWIDGET_VIEW_P (XCAR (tail))) { xv = XXWIDGET_VIEW (XCAR (tail)); if (XWINDOW (xv->w) == w) { Fdelete_xwidget_view (XCAR (tail)); } } } } struct xwidget_view * xwidget_view_lookup (struct xwidget *xw, struct window *w) { Lisp_Object xwidget, window, ret; XSETXWIDGET (xwidget, xw); XSETWINDOW (window, w); ret = Fxwidget_view_lookup (xwidget, window); return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret); } struct xwidget * lookup_xwidget (Lisp_Object spec) { /* When a xwidget lisp spec is found initialize the C struct that is used in the C code. This is done by redisplay so values change if the spec changes. So, take special care of one-shot events. */ int found = 0; Lisp_Object value; struct xwidget *xw; value = xwidget_spec_value (spec, QCxwidget, &found); xw = XXWIDGET (value); return xw; } /* Set up detection of touched xwidget*/ void xwidget_start_redisplay (void) { for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { if (XWIDGET_VIEW_P (XCAR (tail))) XXWIDGET_VIEW (XCAR (tail))->redisplayed = 0; } } /* The xwidget was touched during redisplay, so it isn't a candidate for hiding. */ void xwidget_touch (struct xwidget_view *xv) { xv->redisplayed = 1; } static int xwidget_touched (struct xwidget_view *xv) { return xv->redisplayed; } /* Redisplay has ended, now we should hide untouched xwidgets */ void xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) { int i; int area; xwidget_start_redisplay (); // Iterate desired glyph matrix of window here, hide gtk widgets // not in the desired matrix. // This only takes care of xwidgets in active windows. if a window // goes away from screen xwidget views wust be deleted // dump_glyph_matrix (matrix, 2); for (i = 0; i < matrix->nrows; ++i) { // dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); struct glyph_row *row; row = MATRIX_ROW (matrix, i); if (row->enabled_p != 0) { for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area) { struct glyph *glyph = row->glyphs[area]; struct glyph *glyph_end = glyph + row->used[area]; for (; glyph < glyph_end; ++glyph) { if (glyph->type == XWIDGET_GLYPH) { /* The only call to xwidget_end_redisplay is in dispnew xwidget_end_redisplay (w->current_matrix); */ xwidget_touch (xwidget_view_lookup (glyph->u.xwidget, w)); } } } } } for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { if (XWIDGET_VIEW_P (XCAR (tail))) { struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail)); // "touched" is only meaningful for the current window, so // disregard other views. if (XWINDOW (xv->w) == w) { if (xwidget_touched (xv)) xwidget_show_view (xv); else xwidget_hide_view (xv); } } } } /* Kill all xwidget in BUFFER. */ void kill_buffer_xwidgets (Lisp_Object buffer) { Lisp_Object tail, xwidget; for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail)) { xwidget = XCAR (tail); Vxwidget_list = Fdelq (xwidget, Vxwidget_list); /* TODO free the GTK things in xw */ { CHECK_XWIDGET (xwidget); struct xwidget *xw = XXWIDGET (xwidget); if (xw->widget_osr && xw->widgetwindow_osr) { gtk_widget_destroy (xw->widget_osr); gtk_widget_destroy (xw->widgetwindow_osr); } } } }