diff options
-rw-r--r-- | README.xwidget | 115 | ||||
-rw-r--r-- | lisp/xwidget-screencast.el | 32 | ||||
-rw-r--r-- | src/xwidget-attic.c | 229 | ||||
-rw-r--r-- | src/xwidget.c | 57 |
4 files changed, 400 insertions, 33 deletions
diff --git a/README.xwidget b/README.xwidget index 8c4998cc960..068ffa01bed 100644 --- a/README.xwidget +++ b/README.xwidget @@ -198,13 +198,62 @@ stuff that needs to work: - lookup xw-view for xwidget in emacs window(during redisplay) (- do something for all siblings of a xw-view. not atm) +*** TODO xwidget creation interface +xwidgets are a little bit like emacs processes but also a little bit +like emacs images. Therefore its not perfectly obvious how to handle +creation. Currently I just use hardcoded identifiers. the real scheme +needs to be something else. + +Heres a tentative approach: +- xwidget-create returns a xwidget object, like process creation + functions. the xwidget will be largely uninitialized until + discovered by redisplay. an xw belongs to a buffer +- xwidget-insert inserts the xwidget in a buffer. when discovered by + redisplay it will be initialized and a xwidget-view allocated +- an event will be emitted when initialization is finished when + relevant like for sockets + +the problem with this aproach is that its not really legal to reuse +xwidget objects by writing several display specs who reference the +same xwidget. It could presumably be done but it would just become +weird for no real benefit. the big preblem is that the display spec +decides the on-screen size, and its not sane to have xwidget views +with different sizes. therefore such display specs would need to be +catched and dissallowed. Except it is hard because AFAIK the specs +don't have an identity as such. A flag in the structure could be set +by lookup so the first display attempt would win. but then you can't +rewrite the spec to change the size. hmmm. A third approach would be +to just allow the 1st spec refering an xw during a redisplay to take +effect, the rest are signaled as errors. this wouldnt be too bad. + +the other aproach would be to work more like images: + +- write the display spec with all information needed to create the + xwidget. +- retrieve the xwidget objet from the spec with an xwidget-at-point function. It + can be uninitalized which client code must handle. Unlike + assynchronous process creation we dont get back a handle, because + there is none yet. +- emitted event on initialization, when needed. Many widgets don't + need this. for instance, a button sends an event when pressed. but + you can't press it unless its on screen, and then its initialized + properly. + +This approach seemed good, but how do I know which instance +generates an event if I cant set the id beforehand? + +so, therefore, the first two aproach is used. + + + ** TODO more documentation There should be user docs, and xwidget contributor docs. The current README is all contributor docs there is now, apart from the code. -** TODO look into more ways of displaying xwidgets, like binding them to a +** CANCELLED look into more ways of displaying xwidgets, like binding them to a + CLOSED: [2011-07-05 Tue 11:34] window rather than a point in a buffer. This was suggested by Chidong. This would be a useful addition to Emacs in itself, and would avoid nearly all display issues. I still think the general case is more interesting, but this @@ -212,6 +261,9 @@ special case should also be added. The xwidget would then be bound to replace the view of a particular window, and it would only show in that window. +I got the webkit xwidget to work well enough so I dont see the need +for this now, except for sockets and I think it can better be dealt +with at the lisp level. ** DONE MVC mode for xwidgets CLOSED: [2011-06-27 Mon 12:53] @@ -298,10 +350,9 @@ make ** TODO mvc code crashes after a while -seemingly only when compiling with optimizations +seemingly only when compiling with optimizations. +I have no idea why. -** TODO delete xwidgets belonging to an emacs window -when it closes ** TODO xwidget-resize-at currently it rewrites the display spec. then it resizes the xwidget views. maybe rewriting the spec should be sufficient, and changes to @@ -349,7 +400,7 @@ webkitViewportAttributesRecompute is the offender. maybe try gtk3 variants? -export CFLAGS="`pkg-config --cflags webkitgtk-3.0 ` -DHAVE_WEBKIT_OSR -g" +export CFLAGS="`pkg-config --cflags webkitgtk-3.0 ` -DHAVE_WEBKIT_OSR " export LDFLAGS=`pkg-config --libs webkitgtk-3.0 ` ./configure --with-x-toolkit=gtk3 make @@ -420,6 +471,8 @@ interesting than just forwarding keyboard events. webkit_web_view_get_dom_document () +this is hard it seems. an idea might be to hack elisp support for swig +to machine generate the bindings. **** DONE inject javascript CLOSED: [2011-07-03 Sun 22:50] webkit_web_view_execute_script () @@ -448,12 +501,32 @@ or this funny hack: <jave> lucian: that was a pretty cool idea! -**** webkit_web_view_load_string () +*** webkit_web_view_load_string () I would like preview of html in a buffer rather than from uri. -** TODO clipping of controllers +*** TODO simple xwidget-webkit wrapper +so that it could be used for actual browsing :) +I dont want to reinvent too many wheels so i'd like to use existing +emacs facilities here possible. use bindings similar to w3m(or info) + +- m-x xwidget-webkit starts a session +- 'g' goes to a url +- use bookmark-jump i suppose. I mostly use org for bookmarks myself +- browse-url support so webkit can be the default browser +- some way of getting around the quirky keyboard interaction since + xwidgets dont receive keyboard events because I hawe no idea how to + do that in a sane way + +... and one can of course go on bikeshedding forever. lets keep it +simple and extensible, and compatible with other Emacs packages. + +the really cool ideas would need Emacs DOM integration, which is not +easy. + +** DONE clipping of controllers + CLOSED: [2011-07-05 Tue 11:33] Emacs uses a big GTK window and does its own clipping against Emacs windows inside this area. So, in order to layout gtk widgets in emacs @@ -478,6 +551,7 @@ either. http://www.lanedo.com/~carlos/gtk3-doc/chap-drawing-model.html +anyway clipping is rather complicated but seems to finally work okay. *** DONE subclass my own clipping widget CLOSED: [2011-07-04 Mon 16:55] @@ -555,7 +629,8 @@ the needed data is private to the base class. to overcome this: JanD pointed out the GTK3 port already has its own subclass, so I modified that one. -*** TODO clip top +*** DONE clip top + CLOSED: [2011-07-05 Tue 11:30] there are four controller edges that potentialy need clipping. I begun with right and bottom edges. clipping them is just a matter of setting the right size of the widgetwindow and also ensure it gets the right @@ -569,9 +644,20 @@ makes it harder to use the allocation workarounds. see: - gtk_widget_set_size_request - gtkscrolledwindow -** TODO use FRAME_GTK_WIDGET (f) -rather than gwfixed +I returned to using a simple gtkfixed for the widgetwindow. with +allocation hack and set_has_window it works. Idea prefer not to have +the allocatien hack and it wasnt needed it gtk3 only gtk2. needs +furthi investigation, + +** TODO various code cleanups +There are many cleanups necessary before any hope of inclusion in +Emacs trunk. To begin with, the part of the patch that touches other +parts of emacs must be very clean. +*** TODO use FRAME_GTK_WIDGET (f) +rather than gwfixed. + +*** TODO support configure ** DONE translate clicks CLOSED: [2011-07-03 Sun 22:12] on onscreen webkit peer to offscreen @@ -584,18 +670,21 @@ forwarded them offscreen! ** TODO investigate gdk_window_redirect_to_drawable http://developer.gnome.org/gdk/stable/gdk-Windows.html#gdk-offscreen-window-set-embedder -maybe control be used in place of my own copy hacks? +maybe control be used in place of my own copy hacks? to work it must +support a chain of redirects, which seems unlikely. the benefit would +be that I dont have to spend time optimizing redrawing. -** TODO remove xwidget_views when emacs window is deleted +** DONE remove xwidget_views when emacs window is deleted + CLOSED: [2011-07-05 Tue 11:29] removing xwidget views when an Emacs window closes is not reliable. - switching buffers in a window seems to hide the corresponding xwidget-views properly, but they might as well be deleted. - patching delete-window-internal could be used to delete the xwidget-views - +this seems to work ** notes from x_draw_xwidget_glyph_string diff --git a/lisp/xwidget-screencast.el b/lisp/xwidget-screencast.el new file mode 100644 index 00000000000..f85dbe8f247 --- /dev/null +++ b/lisp/xwidget-screencast.el @@ -0,0 +1,32 @@ +;;(require 'screencast) +(require 'xwidget) +(defconst xwidget-screencast-webkit '("Hello, and welcome to a + short demo of the Emacs xwidget branch, and the Webkit + integration it provides." n + "Xwidgets are toolkit widgets that behave like images in an + Emacs buffer. Except they are actual widgets, so you can + interact with them." n + "There are several, but people seem to fancy the webkit the most so lets have a look!" + (insert "some text") + (xwidget-insert (point-min) 'webkit-osr "webkit-osr" 500 1000 5) + n + "Okay so thats an actual webkit instance in an Emacs buffer! " n + "Mouse-overs work" n + "Mouse-clicks work" n + (split-window-vertically) + "You can split the buffer and scroll the windows separately, as + usual in Emacs. This is however not so usual in the browser + world for some reason." n + "So, can you use the xwidget branch as your main Emacs instance?"n + "Not yet, its still not mature. There are many tricky issues + left. That being said, there are many simple tasks to help out + with also if you like!" )) + + +(defun xwidget-screencast(&optional arg) + "Displays the screencast for xwidgets." + (interactive "P") + (apply (if arg + 'screencast-record + 'screencast) + xwidget-screencast-webkit "xvidgets" 1 ())) diff --git a/src/xwidget-attic.c b/src/xwidget-attic.c new file mode 100644 index 00000000000..84844587051 --- /dev/null +++ b/src/xwidget-attic.c @@ -0,0 +1,229 @@ +static int once = 0; + +int +xwidget_has_composition(void){ //unused +int event_base, error_base; +Display* dpy = GDK_DISPLAY (); +int i; + if(xwidget_query_composition_called) + return hasNamePixmap; + xwidget_query_composition_called = 1; + + //do this once in an emacs session + + if(gdk_display_supports_composite(gdk_display_get_default ())){ + hasNamePixmap = 1; + }else{ + return 0; + } + return 1; +} + + + + + +void +xwidget_end_composition(struct xwidget* w){ //unused + //XCompositeUnredirectWindow(); stop redirecting, should be called when the socket is destroyed +} + + +static gboolean +xwidget_composite_draw_phantom(struct xwidget* xw, + int x, int y, + int clipx, int clipy) +{ + FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (xw->widget), XG_FRAME_DATA); + //////////////////////////////////////////////////////////////// + //Example 7. Composited windows + GdkRegion *region; + GtkWidget *child; + cairo_t *cr; + printf("xwidget_composite_draw_2 at:%d %d\n", x,y); + /* get our child (in this case, the event box) */ + child = xw->widget; //gtk_bin_get_child (GTK_BIN (widget)); + /* create a cairo context to draw to the emacs window */ + // cr = gdk_cairo_create (gtk_widget_get_window (f->gwfixed));//GTK_WIDGET(xw->emacswindow));//xw->widgetwindow));//widget->window); + cr = gdk_cairo_create (gtk_widget_get_window (f->gwfixed));//GTK_WIDGET(xw->emacswindow));//));//widget->window); + /* the source data is the (composited) xwidget */ + //cairo_move_to(cr, xw->x, xw->y); + + cairo_rectangle(cr, x,y, clipx, clipy); + cairo_clip(cr); + + cairo_set_source_rgb(cr,1.0,0,0); + cairo_rectangle(cr,x,y,xw->width,xw->height); + cairo_fill(cr); + + gdk_cairo_set_source_pixmap (cr, child->window, + x,//child->allocation.x, + y//child->allocation.y + ); + /* draw no more than our expose event intersects our child */ + /* region = gdk_region_rectangle (&child->allocation); + gdk_region_intersect (region, event->region); + gdk_cairo_region (cr, region); + cairo_clip (cr); */ + /* composite, with a 50% opacity */ + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + //cairo_paint_with_alpha (cr, phantom ? 0.5 : 0); + cairo_paint_with_alpha (cr, 0.9); + //cairo_paint(cr);//transparency); + /* we're done */ + cairo_destroy (cr); + return FALSE; +} + + + +/* +static gboolean +xwidget_composite_draw(GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ + struct xwidget* xw = (struct xwidget*) g_object_get_data (G_OBJECT (widget), XG_XWIDGET); + printf("xwidget_composite_draw %s\n", data); + xwidget_composite_draw_2(widget, + event, + data, + xw->x, xw->y, 0); + return FALSE; +} +*/ + + +static gboolean +xwidget_composite_draw_widgetwindow(GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ + struct xwidget* xw = (struct xwidget*) g_object_get_data (G_OBJECT (widget), XG_XWIDGET); + cairo_t *cr; + GdkPixmap *pixmap; + pixmap=xw->widget->window; + printf("xwidget_composite_draw_widgetwindow xw.id:%d xw.type:%d window:%d\n", xw->id,xw->type, gtk_widget_get_window (widget)); + //if(xw->type!=3)//TODO this is just trial and terror to see if i can draw the live socket anywhere at all + cr = gdk_cairo_create (gtk_widget_get_window (widget));//GTK_LAYOUT (xw->widgetwindow)->bin_window);// + //else cr = gdk_cairo_create (gtk_widget_get_window (xw->emacswindow)); + cairo_rectangle(cr, 0,0, xw->width, xw->height); + cairo_clip(cr); + + cairo_set_source_rgb(cr,0,1.0,0); + cairo_rectangle(cr, 0,0, xw->width, xw->height); + cairo_fill(cr); + gdk_cairo_set_source_pixmap (cr, pixmap, + 0,0); + + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha (cr, 0.9); + //cairo_paint(cr); + cairo_destroy (cr); + + return FALSE; +} + + +//type support nevermind for now + +/* /\* List of supported image types. Use define_image_type to add new */ +/* types. Use lookup_image_type to find a type for a given symbol. *\/ */ + +/* static struct wxidget_type *wxidget_types; */ + +/* /\* Look up xwidget type SYMBOL, and return a pointer to its xwidget_type */ +/* structure. Value is null if SYMBOL is not a known image type. *\/ */ + +/* static INLINE struct xwidget_type *lookup_xwidget_type (Lisp_Object symbol) */ +/* { */ +/* struct xwidget_type *type; */ + +/* for (type = xwidget_types; type; type = type->next) */ +/* if (EQ (symbol, *type->type)) */ +/* break; */ + +/* return type; */ +/* } */ + + +//////////////////////////////////////////////////////////////// + +/* delete the xwidget and its native widget peer(unused) */ +void xwidget_delete(struct xwidget* xw); + + +void +xwidget_delete (struct xwidget *xw) +{ + printf ("xwidget %d deleted\n", xw->id); + xw->initialized = 0; + gtk_widget_destroy (xw->widget); + +} + + +/* redraw all xwidgets(unused) */ +void +xwidget_invalidate (void) +{ + int i; + struct xwidget *xw; + printf ("invalidate "); + for (i = 0; i < MAX_XWIDGETS; i++) + { + xw = &xwidgets[i]; + if (xw->initialized) + { + printf ("%d,", i); + gtk_widget_queue_draw_area (xw->widget, 0, 0, xw->width, + xw->height); + } + } + printf ("\n"); +} + + +/* initializes the xwidget model */ +void +xwidget_init_model (struct xwidget *xw, + struct glyph_string *s, + int x, int y) +{ + xw->id = s->xwidget_id; + xw->initialized = 1; +} + + + +DEFUN ("xwidget-replug", Fxwidget_replug, Sxwidget_replug, 2, 2, 0, + doc: /* unplug from socket1 plug into socket2.*/ + ) + (Lisp_Object old_parent, Lisp_Object new_parent) +{ + + struct xwidget *xw1; + struct xwidget *xw2; + + + GtkWidget* widget; + CHECK_NUMBER (old_parent); + CHECK_NUMBER (new_parent); + + xw1 = &xwidgets[XFASTINT (old_parent)]; + xw2 = &xwidgets[XFASTINT (new_parent)]; + + + ///this wasnt thought through. we need the views rather than the model. + //so we need to map xw+w to xv + + widget = xw1->widget->gtk_socket_get_plug_window (); + + //the plug... + gtk_widget_ref(widget);//...gets an xtra ref to prevent garb, then it ... + gtk_container_remove(GTK_CONTAINER(xw1->widget), widget);//...is uplugged from old socket and... + gtk_container_add(GTK_CONTAINER(xw2->widget), widget);//...replugged in new socket... + gtk_widget_unref(widget);//...and lastly remove the ref + + return Qnil; +} diff --git a/src/xwidget.c b/src/xwidget.c index 57243832ce3..6c712d1d707 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -90,8 +90,11 @@ #endif +#include <wchar.h> + #ifdef HAVE_WEBKIT #include <webkitgtk.h> + #endif @@ -412,15 +415,10 @@ xwidget_osr_button_callback ( GtkWidget *widget, GdkEvent *event, gpointer user_data) { - gdouble x, y; struct xwidget* xw = (struct xwidget*) g_object_get_data (G_OBJECT (widget), XG_XWIDGET); - GdkEventButton* eventcopy = gdk_event_copy(event); - x = ((GdkEventButton*)event)->x; - y = ((GdkEventButton*)event)->y; - - printf ("button callback %d %d\n",x,y); + GdkEvent* eventcopy = gdk_event_copy(event); - eventcopy->window = gtk_widget_get_window(xw->widget_osr); + ((GdkEventButton*)eventcopy)->window = gtk_widget_get_window(xw->widget_osr); gtk_main_do_event(eventcopy); //TODO this will leak events. they should be deallocated later } @@ -620,7 +618,7 @@ 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 clpping + moving and clipping is done here. also view init. */ int box_line_hwidth = eabs (s->face->box_line_width); @@ -676,7 +674,7 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) || (xv->clip_top != clip_top) || (xv->clip_left != clip_left)){ gtk_widget_set_size_request (GTK_WIDGET (xv->widgetwindow), clip_right + clip_left, clip_bottom + clip_top); - gtk_fixed_put(GTK_FIXED(xv->widgetwindow), xv->widget, -clip_left, -clip_top); + gtk_fixed_move(GTK_FIXED(xv->widgetwindow), xv->widget, -clip_left, -clip_top); printf("reclip %d %d -> %d %d clip_top:%d clip_left:%d\n",xv->clip_right, xv->clip_bottom, clip_right, clip_bottom, clip_top , clip_left); @@ -684,7 +682,7 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) } //if emacs wants to repaint the area where the widget lives, queue a redraw if (!xwidget_hidden(xv)){ - gtk_widget_queue_draw (xv->widgetwindow); + gtk_widget_queue_draw (GTK_WIDGET(xv->widgetwindow)); gtk_widget_queue_draw (xv->widget); } } @@ -730,7 +728,7 @@ DEFUN ("xwidget-webkit-get-title", Fxwidget_webkit_get_title, Sxwidget_webkit_g { //TODO support multibyte strings const gchar* str=webkit_web_view_get_title( WEBKIT_WEB_VIEW(xid2xw(xwidget_id)->widget_osr)); - return make_string_from_bytes(str, wcslen(str), strlen(str)); + return make_string_from_bytes(str, wcslen((const wchar_t *)str), strlen(str)); } @@ -798,18 +796,37 @@ DEFUN("xwidget-info", Fxwidget_info , Sxwidget_info, 1,1,0, doc: /* get xwidget struct xwidget *xw = xid2xw(xwidget_id); Lisp_Object info; - info = Fmake_vector (make_number (7), Qnil); + info = Fmake_vector (make_number (4), Qnil); XVECTOR (info)->contents[0] = make_number(xw->id); - XVECTOR (info)->contents[1] = make_number(xw->type); - XVECTOR (info)->contents[2] = Qnil; //make_number(xw->x); - XVECTOR (info)->contents[3] = Qnil;//make_number(xw->y); - XVECTOR (info)->contents[4] = make_number(xw->width); - XVECTOR (info)->contents[5] = make_number(xw->height); - XVECTOR (info)->contents[6] = Qnil;//make_number(xw->hidden); + XVECTOR (info)->contents[1] = xw->type; + XVECTOR (info)->contents[2] = make_number(xw->width); + XVECTOR (info)->contents[3] = make_number(xw->height); + + + return info; +} + +DEFUN("xwidget-view-info", Fxwidget_view_info , Sxwidget_view_info, 2,2,0, doc: /* get xwidget view props */) + (Lisp_Object xwidget_id, Lisp_Object window) +{ + struct xwidget *xw = xid2xw(xwidget_id); + struct xwidget_view* xv = xwidget_view_lookup(xw, XWINDOW(window)); + + Lisp_Object info; + + info = Fmake_vector (make_number (6), Qnil); + XVECTOR (info)->contents[0] = make_number(xv->x); + XVECTOR (info)->contents[1] = make_number(xv->y); + XVECTOR (info)->contents[2] = make_number(xv->clip_right); + XVECTOR (info)->contents[3] = make_number(xv->clip_bottom); + XVECTOR (info)->contents[4] = make_number(xv->clip_top); + XVECTOR (info)->contents[5] = make_number(xv->clip_left); return info; } + + //xterm.c listens to xwidget_owns_kbd and tries to not eat events when its set int xwidget_owns_kbd = 0; DEFUN ("xwidget-set-keyboard-grab", Fxwidget_set_keyboard_grab, Sxwidget_set_keyboard_grab, 2, 2, 0, doc: /* set unset kbd grab for xwidget. */ @@ -938,6 +955,7 @@ syms_of_xwidget (void) defsubr (&Sxwidget_send_keyboard_event); defsubr (&Sxwidget_embed_steal_window); defsubr (&Sxwidget_info); + defsubr (&Sxwidget_view_info); defsubr (&Sxwidget_resize_internal); defsubr (&Sxwidget_embed_steal_window); @@ -1050,12 +1068,11 @@ xwidget_from_id (int id) void xwidget_view_delete_all_in_window( struct window *w ) { - //xxx struct xwidget_view* xv = NULL; for (int i = 0; i < MAX_XWIDGETS; i++){ xv = &xwidget_views[i]; if(xv->w == w){ - gtk_widget_destroy(xv->widgetwindow); + gtk_widget_destroy(GTK_WIDGET(xv->widgetwindow)); } } } |