Re: Difficulties in using GTK from Emacs



Richard Stallman <rms gnu org> writes: 
> In order for GTK to do its job well, it needs to make this kind of use
> easy.  Making it easy to put GTK into existing X applications has to
> be easy, and should not require major redesign of the application.
> Developers may wish, for various reasons, to keep some of the code
> using X directly; GTK needs to support that.

For this purpose it's very important to use GTK 2, which explicitly
addresses this issue via changes in the main loop interface.

I believe Owen wrote a mail a long time ago to someone else trying to
port Emacs to GTK, about main loop integration issues. Maybe he still
has that or you still have it.

I'll append a mail I wrote to the Eclipse project about the same topic
which may be useful, though some context is no doubt missing.
 
There's also documentation here:
 http://developer.gnome.org/doc/API/2.0/glib/glib-the-main-event-loop.html

> GTK uses GdkEvents, so one would think this would work:
> 
>       while (gdk_events_pending ())
>         /* He tried gtk_events_pending too; it doesn't affect the issue.  */
>         {
>           GdkEvent* ev = gdk_event_get ();
>           if (ev)
>             {
>               gtk_main_do_event (ev);
>               gdk_event_free (ev);
>             }
>         }

A conceptual misunderstanding here is that the queue of GdkEvents
makes up the entire GTK+ main loop. The main loop is much more general
than that, it represents any kind of task, including tasks done at
certain intervals (timeouts) and tasks done when there are no other
tasks (idles). Tasks also have priorities and are
scheduled. Processing the queue of GdkEvents is just one task among
many, and all the tasks are needed for GTK to operate correctly,
especially in GTK 2.
 
> It is also hard to use GTK in programs that have event loops that use
> X events.

I wrote a window manager using GTK that uses X directly for most of
the window manager code. This is done using an event filter,
gdk_window_add_filter(). Somewhat obscurely, if you pass NULL here you
get events for all windows. I just added that point to the docs.
But it should make things much easier for you to know this.

> Alas, this didn't work either, because gtk_main_iteration does not use
> XPending to see if it should do any work, it looks at the filedescriptor
> to the X connection to see if there is something to read.  Since we did
> XPeekEvent, there isn't anythingto read from the file descriptor, but
> there is an event in the X input queue.  gtk_main_iteration ignores
> the X input queue.

This is no longer the case in GTK 2, GTK 2 will call XPending() always.
 
> GDK has a function that lets the programmer set a filter for
> ClientMessages and specific Atoms.  By default, gdk installs its own
> filter for WM protocols.  This filter turns wm_delete_window events to
> an GDK event of type GDK_DELETE and drops all others (like
> wm_take_focus, wm_save_yourself that Emacs acts upon).  Emacs would
> have to put in its own filter that would override the GDK default
> filter.  Since wm_save_yourself and so on are useful, it should be
> made easy for programs to handle them.

If you're using GtkWindow (as you are required to if you're going to
put GTK widgets in the window) then wm_take_focus and wm_delete_window
are handled by GTK and you're going to have to be careful not to get
GTK confused if you also try to deal with them. GTK will for example
get confused if you change its "input model" (see table of four input
models in the ICCCM) and no doubt Emacs code also assumes one of
these, so if they are different something has to give.

You also have the problem that you can't change what GTK sets for the
WM_PROTOCOLS hint, so you can't tell the WM that you support
wm_save_yourself or other protocols that GTK ignores.

Using gdk_window_add_filter() and some careful hacks you should be
able to work around all this - but you're going to need to understand
what GTK does internally, and rely on that information to an
uncomfortable degree.

(BTW, wm_save_yourself is an old deprecated feature as of the 1994 SM
specification, so Emacs should really be thinking about using the
"new" (8-year-old) spec instead...)

Havoc

Here are those mails to the Eclipse project:

From: Havoc Pennington <hp redhat com>
Subject: Re: [platform-swt-dev] GTK in CVS
To: platform-swt-dev eclipse org
Date: 05 Dec 2001 16:23:25 -0500


Steve_Northover oti com writes: 
> - positioning widgets such that they do not move when the parent is 
> resized (we are using a fixed + hacks)
> 
> - everything to do with redraw, damaged areas, forcing paints, etc.

Things don't quite compile for me here yet, so I can't look at the
actual problems - glancing through the code, a "mismatch" between the
way GTK wants to do things and the way SWT wants to do things is that
you're expecting certain things to be synchronous (resulting in calls
go gtk_widget_show_now(), forced gtk_widget_realize(), the size
request/allocation process, and so on), while GTK wants those things
to happen "lazily" in an idle handler. I would expect to have a lot of
problems with flicker/jumping, and plain old inefficiency, unless
we're careful about how this is set up.

> - making Display.sleep() work properly

Spent some time talking to Owen about this. GLib 2.0 has a revised
main loop that's intended to make this kind of thing easier to do.
There are some possible hacks for GLib 1.2.

As you know the GLib main loop is a general-purpose thing that can
contain any kind of "event source." Built-in sources are timeouts,
idles, and input handlers, for example.

In both versions the loop has four "steps":

 prepare - ask all sources a) do they already have events and
           b) when they definitely need to be asked again 
           (when do we need to wake up and ask again - e.g. 
           a timeout requires that we wake up after the remaining
           time elapses)

 poll - sleep on all file descriptors (descriptors are provided by
        certain kinds of source), with the min timeout from all
        prepared sources, until we get a descriptor event or a timeout

 check - ask all sources if they have events now, taking into account
         the results of the poll

 dispatch - dispatch the sources with events ready

In GTK 1.2, there is no way to break these apart. 
The available API is:

  g_main_pending(): prepare, if there are events return right away,
                    else poll with timeout of 0 and check, 
                    then return whether we have events.

                    (Different in 2.0 in that it always polls, even if
                     the prepare has events already, in order to
                     handle source priorities correctly.)
   
  g_main_iteration(): do the whole cycle including dispatch

> From looking at Display.sleep() what we want to do is 
prepare/poll/check, but not dispatch, and with the usual poll timeout,
not a poll timeout of 0 as g_main_pending() uses. So GTK 1.2 does not
give us the appropriate API.

In GLib 2.0, we have exported API to do each of the steps. So there
are g_main_context_prepare(), g_main_context_poll(),
g_main_context_check(), etc. functions. Display.sleep() needs to call
g_main_context_prepare(), then get poll info with
g_main_context_query(), do the g_main_context_poll() with that info,
and then g_main_context_check().  In readAndDispatch(), I'm not sure
if we want just the g_main_context_dispatch(), or a full
g_main_context_iteration(), but something along those lines.

Basically the idea is to copy the code out of the static function
g_main_context_iterate() in glib/glib/gmain.c, removing the places
where it uses private API, those are just efficiency hacks.
(The code copy really shouldn't be required, I filed this bug report on that:
  http://bugzilla.gnome.org/show_bug.cgi?id=66290
 but it's past 2.0 API freeze so for now we have to reimplement a 
 few lines, not a huge deal.)

Anyway, this should give us what I take to be the correct semantics
for SWT, which is that Display.sleep() never processes events, just
sleeps until some arrive. (From the XtAppPeekEvent() man page, current
Motif Display.sleep() processes timeouts without returning however -
so maybe it's supposed to sleep until widget events arrive, and just
process other kinds of event?)

For GLib 1.2, things are going to be more exciting. The hack
we came up with for Display.sleep() is pretty broken, but is:

   while (g_main_iteration (FALSE)) /* FALSE means don't block, just
                                     * run already-ready events
                                     */
     {
        /* Here we get the X display connection file descriptor,
         * and block until it has data using poll() system call
         * directly on that single descriptor
         */
     }

The problem is that timeout and input event sources are not going to
wake us up out of the sleep. I don't think GTK 1.2 itself relies on 
either of these, but if SWT uses timers, we'd need to do a custom
SWT-specific hack to track those and be sure we pass an appropriate 
timeout to poll().

Another possible hack is to _always_ pass say a 50 ms timeout to
poll(), so we never accidentally lock up forever, but of course that
will mean constantly eating CPU at some low level. No more than the
current Display.sleep() implementation, of course.

Docs for 2.0 main loop stuff can be found here:
 http://developer.gnome.org/doc/API/2.0/glib/glib-the-main-event-loop.html

It may not be clear that gtk_main_iteration(), gtk_main(), etc. are
just trivial wrappers around the GLib stuff - the main loop was moved
to GLib in the 1.0->1.2 transition, and gtk_main_* remains as a
compatibility layer.

Havoc

From: Havoc Pennington <hp redhat com>
Subject: Re: [platform-swt-dev] GTK in CVS
To: platform-swt-dev eclipse org
Date: 05 Dec 2001 16:31:49 -0500


Havoc Pennington <hp redhat com> writes: 
> For GLib 1.2, things are going to be more exciting. The hack
> we came up with for Display.sleep() is pretty broken, but is:
> 
>    while (g_main_iteration (FALSE)) /* FALSE means don't block, just
>                                      * run already-ready events
>                                      */
>      {
>         /* Here we get the X display connection file descriptor,
>          * and block until it has data using poll() system call
>          * directly on that single descriptor
>          */
>      }
> 

Oops - this was intended to be a conceptual description of the whole
"SWT main loop" including both sleep() and readAndDispatch(). sleep() 
would just be something like:

 if (g_main_pending())
   return true;
 else
   {
     /* first poll on X file descriptor */

     return g_main_pending ();
   }

while readAndDispatch() would do the "while (g_main_iteration (FALSE))" part.

Havoc









 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]