/* 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);
}
}
}
}