make an event queue for X events. the queue's min size is 16 XEvents (~3k)
authorDana Jansens <danakj@orodu.net>
Wed, 28 Apr 2010 16:57:51 +0000 (12:57 -0400)
committerDana Jansens <danakj@orodu.net>
Wed, 28 Apr 2010 16:58:42 +0000 (12:58 -0400)
Makefile.am
obt/display.c
obt/mainloop.c
obt/xqueue.c [new file with mode: 0644]
obt/xqueue.h [new file with mode: 0644]
openbox/client.c
openbox/event.c
openbox/frame.c
openbox/moveresize.c
openbox/screen.c
openbox/window.c

index fb61494..4c670bc 100644 (file)
@@ -148,7 +148,9 @@ obt_libobt_la_SOURCES = \
        obt/prop.c \
        obt/util.h \
        obt/xevent.h \
-       obt/xevent.c
+       obt/xevent.c \
+       obt/xqueue.h \
+       obt/xqueue.c
 
 ## openbox ##
 
@@ -441,7 +443,8 @@ obtpubinclude_HEADERS = \
        obt/prop.h \
        obt/util.h \
        obt/version.h \
-       obt/xevent.h
+       obt/xevent.h \
+       obt/xqueue.h
 
 nodist_pkgconfig_DATA = \
        obrender/obrender-3.5.pc \
index 37b1215..8b06cbf 100644 (file)
@@ -20,6 +20,7 @@
 #include "obt/prop.h"
 #include "obt/internal.h"
 #include "obt/keyboard.h"
+#include "obt/xqueue.h"
 
 #ifdef HAVE_STRING_H
 #  include <string.h>
 #  include <unistd.h>
 #endif
 
+/* from xqueue.c */
+extern void xqueue_init(void);
+extern void xqueue_destroy(void);
+
 Display* obt_display = NULL;
 
 gboolean obt_display_error_occured = FALSE;
@@ -116,13 +121,19 @@ gboolean obt_display_open(const char *display_name)
     }
     g_free(n);
 
+    if (obt_display)
+        xqueue_init();
+
     return obt_display != NULL;
 }
 
 void obt_display_close(void)
 {
     obt_keyboard_shutdown();
-    if (obt_display) XCloseDisplay(obt_display);
+    if (obt_display) {
+        xqueue_destroy();
+        XCloseDisplay(obt_display);
+    }
 }
 
 static gint xerror_handler(Display *d, XErrorEvent *e)
index ecdd7f7..7536625 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "obt/mainloop.h"
 #include "obt/display.h"
+#include "obt/xqueue.h"
 #include "obt/util.h"
 
 #ifdef HAVE_STDIO_H
@@ -296,10 +297,8 @@ void obt_main_loop_run(ObtMainLoop *loop)
             loop->signal_fired = FALSE;
 
             sigprocmask(SIG_SETMASK, &oldset, NULL);
-        } else if (loop->display && XPending(loop->display)) {
-            do {
-                XNextEvent(loop->display, &e);
-
+        } else if (loop->display && xqueue_pending_local()) {
+            while (xqueue_next_local(&e) && loop->run) {
                 if (e.type == MappingNotify)
                     XRefreshKeyboardMapping(&e.xmapping);
 
@@ -307,10 +306,9 @@ void obt_main_loop_run(ObtMainLoop *loop)
                     ObtMainLoopXHandlerType *h = it->data;
                     h->func(&e, h->data);
                 }
-            } while (XPending(loop->display) && loop->run);
+            }
         } else {
             /* this only runs if there were no x events received */
-
             timer_dispatch(loop, (GTimeVal**)&wait);
 
             selset = loop->fd_set;
diff --git a/obt/xqueue.c b/obt/xqueue.c
new file mode 100644 (file)
index 0000000..2304ea4
--- /dev/null
@@ -0,0 +1,320 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   obt/display.c for the Openbox window manager
+   Copyright (c) 2007        Dana Jansens
+
+   This program 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 2 of the License, or
+   (at your option) any later version.
+
+   This program 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.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "obt/xqueue.h"
+#include "obt/display.h"
+
+#define MINSZ 16
+
+static XEvent *q = NULL;
+static gulong qsz = 0;
+static gulong qstart; /* the first event in the queue */
+static gulong qend; /* the last event in the queue */
+static gulong qnum = 0;
+
+static inline void shrink(void) {
+    if (qsz > MINSZ && qnum < qsz / 4) {
+        const gulong newsz = qsz/2;
+        gulong i;
+
+        if (qnum == 0) {
+            qstart = 0;
+            qend = -1;
+        }
+
+        /* all in the shinking part, move it to pos 0 */
+        else if (qstart >= newsz && qend >= newsz) {
+            for (i = 0; i < qnum; ++i)
+                q[i] = q[qstart+i];
+            qstart = 0;
+            qend = qnum - 1;
+        }
+
+        /* it wraps around to 0 right now, move the part between newsz and qsz
+           to be before newsz */
+        else if (qstart >= newsz) {
+            const gulong n = qsz - qstart;
+            for (i = 0; i < n; ++i)
+                q[newsz-n+i] = q[qstart+i];
+            qstart = newsz-n;
+        }
+
+        /* it needs to wrap around to 0, move the stuff after newsz to pos 0 */
+        else if (qend >= newsz) {
+            const gulong n = qend + 1 - newsz;
+            for (i = 0; i < n; ++i)
+                q[i] = q[newsz+i];
+            qend = n - 1;
+        }
+
+        q = g_renew(XEvent, q, newsz);
+        qsz = newsz;
+    }
+}
+
+static inline void grow(void) {
+    if (qnum == qsz) {
+        const gulong newsz = qsz*2;
+        gulong i;
+        q = g_renew(XEvent, q, newsz);
+
+        g_assert(qnum > 0);
+
+        if (qend < qstart) { /* it wraps around to 0 right now */
+            for (i = 0; i <= qend; ++i)
+                q[newsz+i] = q[i];
+            qend = newsz + qend;
+        }
+
+        qsz = newsz;
+    }
+}
+
+/* Grab all pending X events */
+static gboolean read_events(gboolean block)
+{
+    gint sth, n;
+
+    n = XEventsQueued(obt_display, QueuedAfterFlush) > 0;
+    sth = FALSE;
+
+    while ((block && !sth) || n > 0) {
+        XEvent e;
+
+        if (XNextEvent(obt_display, &e) != Success)
+            return FALSE;
+
+        grow(); /* make sure there is room */
+
+        ++qnum;
+        qend = (qend + 1) % qsz; /* move the end */
+        q[qend] = e; /* stick the event at the end */
+
+        --n;
+        sth = TRUE;
+    }
+
+    return sth; /* return if we read anything */
+}
+
+static void pop(gulong p)
+{
+    /* remove the event */
+    --qnum;
+    if (qnum == 0) {
+        qstart = 0;
+        qend = -1;
+    }
+    else if (p == qstart)
+        qstart = (qstart + 1) % qsz;
+    else {
+        gulong pi;
+
+        /* is it cheaper to move the start or the end ? */
+        if ((p >= qstart && p < qstart + qnum/2) ||
+            (p < qstart && p < (qstart + qnum/2) % qsz))
+        {
+            /* move the start */
+            pi = p;
+            while (pi != qstart) {
+                const gulong pi_next = (pi == 0 ? qsz-1 : pi-1);
+
+                q[pi] = q[pi_next];
+                pi = pi_next;
+            }
+            qstart = (qstart + 1) % qsz;
+        }
+        else {
+            /* move the end */
+            pi = p;
+            while (pi != qend) {
+                const gulong pi_next = (pi + 1) % qsz;
+
+                q[pi] = q[pi_next];
+                pi = pi_next;
+            }
+            qend = (qend == 0 ? qsz-1 : qend-1);
+        }
+    }
+
+    shrink(); /* shrink the q if too little in it */
+}
+
+void xqueue_init(void)
+{
+    if (q != NULL) return;
+    qsz = MINSZ;
+    q = g_new(XEvent, qsz);
+    qstart = 0;
+    qend = -1;
+}
+
+void xqueue_destroy(void)
+{
+    if (q == NULL) return;
+    g_free(q);
+    q = NULL;
+    qsz = 0;
+}
+
+gboolean xqueue_match_window(XEvent *e, gpointer data)
+{
+    const Window w = *(Window*)data;
+    return e->xany.window == w;
+}
+
+gboolean xqueue_match_type(XEvent *e, gpointer data)
+{
+    return e->type == GPOINTER_TO_INT(data);
+}
+
+gboolean xqueue_match_window_type(XEvent *e, gpointer data)
+{
+    const ObtXQueueWindowType x = *(ObtXQueueWindowType*)data;
+    return e->xany.window == x.window && e->type == x.type;
+}
+
+gboolean xqueue_match_window_message(XEvent *e, gpointer data)
+{
+    const ObtXQueueWindowMessage x = *(ObtXQueueWindowMessage*)data;
+    return e->xany.window == x.window && e->type == ClientMessage &&
+        e->xclient.message_type == x.message;
+}
+
+gboolean xqueue_peek(XEvent *event_return)
+{
+    g_return_val_if_fail(q != NULL, FALSE);
+    g_return_val_if_fail(event_return != NULL, FALSE);
+
+    if (!qnum) read_events(TRUE);
+    if (!qnum) return FALSE;
+    *event_return = q[qstart]; /* get the head */
+    return TRUE;
+}
+
+gboolean xqueue_peek_local(XEvent *event_return)
+{
+    g_return_val_if_fail(q != NULL, FALSE);
+    g_return_val_if_fail(event_return != NULL, FALSE);
+
+    if (!qnum) read_events(FALSE);
+    if (!qnum) return FALSE;
+    *event_return = q[qstart]; /* get the head */
+    return TRUE;
+}
+
+gboolean xqueue_next(XEvent *event_return)
+{
+    g_return_val_if_fail(q != NULL, FALSE);
+    g_return_val_if_fail(event_return != NULL, FALSE);
+
+    if (!qnum) read_events(TRUE);
+    if (qnum) {
+        *event_return = q[qstart]; /* get the head */
+        pop(qstart);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+gboolean xqueue_next_local(XEvent *event_return)
+{
+    g_return_val_if_fail(q != NULL, FALSE);
+    g_return_val_if_fail(event_return != NULL, FALSE);
+
+    if (!qnum) read_events(FALSE);
+    if (qnum) {
+        *event_return = q[qstart]; /* get the head */
+        pop(qstart);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+gboolean xqueue_exists(xqueue_match_func match, gpointer data)
+{
+    gulong i, checked;
+
+    g_return_val_if_fail(q != NULL, FALSE);
+    g_return_val_if_fail(match != NULL, FALSE);
+
+    checked = 0;
+    while (TRUE) {
+        for (i = checked; i < qnum; ++i, ++checked) {
+            const gulong p = (qstart + i) % qsz;
+            if (match(&q[p], data))
+                return TRUE;
+        }
+        if (!read_events(TRUE)) break; /* error */
+    }
+    return FALSE;
+}
+
+gboolean xqueue_exists_local(xqueue_match_func match, gpointer data)
+{
+    gulong i, checked;
+
+    g_return_val_if_fail(q != NULL, FALSE);
+    g_return_val_if_fail(match != NULL, FALSE);
+
+    checked = 0;
+    while (TRUE) {
+        for (i = checked; i < qnum; ++i, ++checked) {
+            const gulong p = (qstart + i) % qsz;
+            if (match(&q[p], data))
+                return TRUE;
+        }
+        if (!read_events(FALSE)) break;
+    }
+    return FALSE;
+}
+
+gboolean xqueue_remove_local(XEvent *event_return,
+                             xqueue_match_func match, gpointer data)
+{
+    gulong i, checked;
+
+    g_return_val_if_fail(q != NULL, FALSE);
+    g_return_val_if_fail(event_return != NULL, FALSE);
+    g_return_val_if_fail(match != NULL, FALSE);
+
+    checked = 0;
+    while (TRUE) {
+        for (i = checked; i < qnum; ++i, ++checked) {
+            const gulong p = (qstart + i) % qsz;
+            if (match(&q[p], data)) {
+                *event_return = q[p];
+                pop(p);
+                return TRUE;
+            }
+        }
+        if (!read_events(FALSE)) break;
+    }
+    return FALSE;
+}
+
+gboolean xqueue_pending_local(void)
+{
+    g_return_val_if_fail(q != NULL, FALSE);
+    
+    if (!qnum) read_events(FALSE);
+    return qnum != 0;
+}
diff --git a/obt/xqueue.h b/obt/xqueue.h
new file mode 100644 (file)
index 0000000..8b31278
--- /dev/null
@@ -0,0 +1,92 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   obt/xqueue.h for the Openbox window manager
+   Copyright (c) 2010        Dana Jansens
+
+   This program 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 2 of the License, or
+   (at your option) any later version.
+
+   This program 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.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_xqueue_h
+#define __obt_xqueue_h
+
+#include <glib.h>
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ObtXQueueWindowType {
+    Window window;
+    int type;
+} ObtXQueueWindowType;
+
+typedef struct _ObtXQueueWindowMessage {
+    Window window;
+    Atom message;
+} ObtXQueueWindowMessage;
+
+typedef gboolean (*xqueue_match_func)(XEvent *e, gpointer data);
+
+/*! Returns TRUE if the event matches the window pointed to by @data */
+gboolean xqueue_match_window(XEvent *e, gpointer data);
+
+/*! Returns TRUE if the event matches the type contained in the value of @data */
+gboolean xqueue_match_type(XEvent *e, gpointer data);
+
+/*! Returns TRUE if the event matches the type and window in the
+  ObtXQueueWindowType pointed to by @data */
+gboolean xqueue_match_window_type(XEvent *e, gpointer data);
+
+/*! Returns TRUE if a ClientMessage event matches the message and window in the
+  ObtXQueueWindowMessage pointed to by @data */
+gboolean xqueue_match_window_message(XEvent *e, gpointer data);
+
+/*! Returns TRUE and passes the next event in the queue and removes it from
+  the queue.  On error, returns FALSE */
+gboolean xqueue_next(XEvent *event_return);
+
+/*! Returns TRUE and passes the next event in the local queue and removes it
+  from the queue.  If no event is in the local queue, it returns FALSE. */
+gboolean xqueue_next_local(XEvent *event_return);
+
+/*! Returns TRUE if there is anything in the local event queue, and FALSE
+  otherwise. */
+gboolean xqueue_pending_local(void);
+
+/*! Returns TRUE and passes the next event in the queue, or FALSE if there
+  is an error */
+gboolean xqueue_peek(XEvent *event_return);
+
+/*! Returns TRUE and passes the next event in the queue, if there is one,
+  and returns FALSE otherwise. */
+gboolean xqueue_peek_local(XEvent *event_return);
+
+/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the
+  current event queue or in the stream of events from the server,
+  and passes the matching event without removing it from the queue.
+  This blocks until an event is found or an error occurs. */
+gboolean xqueue_exists(xqueue_match_func match, gpointer data);
+
+/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the
+  current event queue, and passes the matching event without removing it
+  from the queue. */
+gboolean xqueue_exists_local(xqueue_match_func match, gpointer data);
+
+/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the
+  current event queue, and passes the matching event while removing it
+  from the queue. */
+gboolean xqueue_remove_local(XEvent *event_return,
+                             xqueue_match_func match, gpointer data);
+
+G_END_DECLS
+
+#endif
index cc69171..0a32621 100644 (file)
@@ -42,6 +42,7 @@
 #include "obrender/render.h"
 #include "gettext.h"
 #include "obt/display.h"
+#include "obt/xqueue.h"
 #include "obt/prop.h"
 
 #ifdef HAVE_UNISTD_H
@@ -3620,36 +3621,31 @@ ObClient *client_search_modal_child(ObClient *self)
     return NULL;
 }
 
-static gboolean client_validate_unmap(ObClient *self, int n)
-{
-    XEvent e;
-    gboolean ret = TRUE;
-
-    if (XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e)) {
-        if (n < self->ignore_unmaps) // ignore this one, but look for more
-            ret = client_validate_unmap(self, n+1);
-        else
-            ret = FALSE; // the window is going to become unmanaged
+struct ObClientFindDestroyUnmap {
+    Window window;
+    gint ignore_unmaps;
+};
 
-        /* put them back on the event stack so they end up in the same order */
-        XPutBackEvent(obt_display, &e);
-    }
-
-    return ret;
+static gboolean find_destroy_unmap(XEvent *e, gpointer data)
+{
+    struct ObClientFindDestroyUnmap *find = data;
+    if (e->type == DestroyNotify)
+        return e->xdestroywindow.window == find->window;
+    if (e->type == UnmapNotify && e->xunmap.window == find->window)
+        /* ignore the first $find->ignore_unmaps$ many unmap events */
+        return --find->ignore_unmaps < 0;
+    return FALSE;
 }
 
 gboolean client_validate(ObClient *self)
 {
-    XEvent e;
+    struct ObClientFindDestroyUnmap find;
 
     XSync(obt_display, FALSE); /* get all events on the server */
 
-    if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e)) {
-        XPutBackEvent(obt_display, &e);
-        return FALSE;
-    }
-
-    if (!client_validate_unmap(self, 0))
+    find.window = self->window;
+    find.ignore_unmaps = self->ignore_unmaps;
+    if (xqueue_exists_local(find_destroy_unmap, &find))
         return FALSE;
 
     return TRUE;
@@ -3841,6 +3837,8 @@ gboolean client_can_focus(ObClient *self)
 
 gboolean client_focus(ObClient *self)
 {
+    if (!client_validate(self)) return FALSE;
+
     /* we might not focus this window, so if we have modal children which would
        be focused instead, bring them to this desktop */
     client_bring_modal_windows(self);
index 5e526d2..9905d97 100644 (file)
@@ -40,6 +40,7 @@
 #include "stacking.h"
 #include "ping.h"
 #include "obt/display.h"
+#include "obt/xqueue.h"
 #include "obt/prop.h"
 #include "obt/keyboard.h"
 
@@ -108,6 +109,7 @@ static Time event_sourcetime;
 /*! The serial of the current X event */
 static gulong event_curserial;
 static gboolean focus_left_screen = FALSE;
+static gboolean waiting_for_focusin = FALSE;
 /*! A list of ObSerialRanges which are to be ignored for mouse enter events */
 static GSList *ignore_serials = NULL;
 
@@ -288,8 +290,11 @@ static void event_hack_mods(XEvent *e)
         /* compress events */
         {
             XEvent ce;
-            while (XCheckTypedWindowEvent(obt_display, e->xmotion.window,
-                                          e->type, &ce)) {
+            ObtXQueueWindowType wt;
+
+            wt.window = e->xmotion.window;
+            wt.type = MotionNotify;
+            while (xqueue_remove_local(&ce, xqueue_match_window_type, &wt)) {
                 e->xmotion.x = ce.xmotion.x;
                 e->xmotion.y = ce.xmotion.y;
                 e->xmotion.x_root = ce.xmotion.x_root;
@@ -389,12 +394,12 @@ static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
     }
 }
 
-static Bool event_look_for_focusin(Display *d, XEvent *e, XPointer arg)
+static gboolean event_look_for_focusin(XEvent *e, gpointer data)
 {
     return e->type == FocusIn && wanted_focusevent(e, FALSE);
 }
 
-static Bool event_look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
+static gboolean event_look_for_focusin_client(XEvent *e, gpointer data)
 {
     return e->type == FocusIn && wanted_focusevent(e, TRUE);
 }
@@ -437,28 +442,9 @@ static void print_focusevent(XEvent *e)
 
 }
 
-static gboolean event_ignore(XEvent *e, ObClient *client)
-{
-    switch(e->type) {
-    case FocusIn:
-        print_focusevent(e);
-        if (!wanted_focusevent(e, FALSE))
-            return TRUE;
-        break;
-    case FocusOut:
-        print_focusevent(e);
-        if (!wanted_focusevent(e, FALSE))
-            return TRUE;
-        break;
-    }
-    return FALSE;
-}
-
 static void event_process(const XEvent *ec, gpointer data)
 {
     XEvent ee, *e;
-    ObEventData *ed = data;
-
     Window window;
     ObClient *client = NULL;
     ObDock *dock = NULL;
@@ -502,21 +488,23 @@ static void event_process(const XEvent *ec, gpointer data)
     event_set_curtime(e);
     event_curserial = e->xany.serial;
     event_hack_mods(e);
-    if (event_ignore(e, client)) {
-        if (ed)
-            ed->ignored = TRUE;
-        return;
-    } else if (ed)
-            ed->ignored = FALSE;
 
     /* deal with it in the kernel */
 
     if (e->type == FocusIn) {
-        if (client &&
-            e->xfocus.detail == NotifyInferior)
-        {
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Focus went to the frame window");
+        print_focusevent(e);
+        if (!wanted_focusevent(e, FALSE)) {
+            if (waiting_for_focusin) {
+                /* We were waiting for this FocusIn, since we got a FocusOut
+                   earlier, but it went to a window that isn't a client. */
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Focus went to an unmanaged window 0x%x !",
+                              e->xfocus.window);
+                focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
+            }
+        }
+        else if (client && e->xfocus.detail == NotifyInferior) {
+            ob_debug_type(OB_DEBUG_FOCUS, "Focus went to the frame window");
 
             focus_left_screen = FALSE;
 
@@ -533,8 +521,6 @@ static void event_process(const XEvent *ec, gpointer data)
                  e->xfocus.detail == NotifyInferior ||
                  e->xfocus.detail == NotifyNonlinear)
         {
-            XEvent ce;
-
             ob_debug_type(OB_DEBUG_FOCUS,
                           "Focus went to root or pointer root/none");
 
@@ -557,10 +543,7 @@ static void event_process(const XEvent *ec, gpointer data)
                But if the other focus in is something like PointerRoot then we
                still want to fall back.
             */
-            if (XCheckIfEvent(obt_display, &ce, event_look_for_focusin_client,
-                              NULL))
-            {
-                XPutBackEvent(obt_display, &ce);
+            if (xqueue_exists_local(event_look_for_focusin_client, NULL)) {
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "  but another FocusIn is coming");
             } else {
@@ -593,11 +576,14 @@ static void event_process(const XEvent *ec, gpointer data)
             client_calc_layer(client);
             client_bring_helper_windows(client);
         }
-    } else if (e->type == FocusOut) {
-        XEvent ce;
 
+        waiting_for_focusin = FALSE;
+    } else if (e->type == FocusOut) {
+        print_focusevent(e);
+        if (!wanted_focusevent(e, FALSE))
+            ; /* skip this one */
         /* Look for the followup FocusIn */
-        if (!XCheckIfEvent(obt_display, &ce, event_look_for_focusin, NULL)) {
+        else if (!xqueue_exists_local(event_look_for_focusin, NULL)) {
             /* There is no FocusIn, this means focus went to a window that
                is not being managed, or a window on another screen. */
             Window win, root;
@@ -619,24 +605,16 @@ static void event_process(const XEvent *ec, gpointer data)
             /* nothing is focused */
             focus_set_client(NULL);
         } else {
-            /* Focus moved, so process the FocusIn event */
-            ObEventData ed = { .ignored = FALSE };
-            event_process(&ce, &ed);
-            if (ed.ignored) {
-                /* The FocusIn was ignored, this means it was on a window
-                   that isn't a client. */
-                ob_debug_type(OB_DEBUG_FOCUS,
-                              "Focus went to an unmanaged window 0x%x !",
-                              ce.xfocus.window);
-                focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
-            }
+            /* Focus moved, so mark that we are waiting to process that
+               FocusIn */
+            waiting_for_focusin = TRUE;
+
+            /* nothing is focused right now, but will be again shortly */
+            focus_set_client(NULL);
         }
 
-        if (client && client != focus_client) {
+        if (client && client != focus_client)
             frame_adjust_focus(client->frame, FALSE);
-            /* focus_set_client(NULL) has already been called in this
-               section or by focus_fallback */
-        }
     }
     else if (client)
         event_handle_client(client, e);
@@ -739,13 +717,15 @@ static void event_process(const XEvent *ec, gpointer data)
     }
     else if (e->type == KeyPress || e->type == KeyRelease ||
              e->type == MotionNotify)
+    {
         used = event_handle_user_input(client, e);
 
-    if (prompt && !used)
-        used = event_handle_prompt(prompt, e);
+        if (prompt && !used)
+            used = event_handle_prompt(prompt, e);
+    }
 
     /* if something happens and it's not from an XEvent, then we don't know
-       the time */
+       the time, so clear it here until the next event is handled */
     event_curtime = event_sourcetime = CurrentTime;
     event_curserial = 0;
 }
@@ -918,25 +898,48 @@ static gboolean *context_to_button(ObFrame *f, ObFrameContext con, gboolean pres
     }
 }
 
-static void compress_client_message_event(XEvent *e, XEvent *ce, Window window,
-                                          Atom msgtype)
+static gboolean more_client_message_event(Window window, Atom msgtype)
 {
-    /* compress changes into a single change */
-    while (XCheckTypedWindowEvent(obt_display, window, e->type, ce)) {
-        /* XXX: it would be nice to compress ALL messages of a
-           type, not just messages in a row without other
-           message types between. */
-        if (ce->xclient.message_type != msgtype) {
-            XPutBackEvent(obt_display, ce);
-            break;
+    ObtXQueueWindowMessage wm;
+    wm.window = window;
+    wm.message = msgtype;
+    return xqueue_exists_local(xqueue_match_window_message, &wm);
+}
+
+struct ObSkipPropertyChange {
+    Window window;
+    Atom prop;
+};
+
+static gboolean skip_property_change(XEvent *e, gpointer data)
+{
+    const struct ObSkipPropertyChange s = *(struct ObSkipPropertyChange*)data;
+
+    if (e->type == PropertyNotify && e->xproperty.window == s.window) {
+        const Atom a = e->xproperty.atom;
+        const Atom b = s.prop;
+
+        /* these are all updated together */
+        if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
+             a == OBT_PROP_ATOM(WM_NAME) ||
+             a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+             a == OBT_PROP_ATOM(WM_ICON_NAME))
+            &&
+            (b == OBT_PROP_ATOM(NET_WM_NAME) ||
+             b == OBT_PROP_ATOM(WM_NAME) ||
+             b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+             b == OBT_PROP_ATOM(WM_ICON_NAME)))
+        {
+            return TRUE;
         }
-        e->xclient = ce->xclient;
+        else if (a == b && a == OBT_PROP_ATOM(NET_WM_ICON))
+            return TRUE;
     }
+    return FALSE;
 }
 
 static void event_handle_client(ObClient *client, XEvent *e)
 {
-    XEvent ce;
     Atom msgtype;
     ObFrameContext con;
     gboolean *but;
@@ -1372,14 +1375,16 @@ static void event_handle_client(ObClient *client, XEvent *e)
 
         msgtype = e->xclient.message_type;
         if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) {
-            compress_client_message_event(e, &ce, client->window, msgtype);
-            client_set_wm_state(client, e->xclient.data.l[0]);
+            if (!more_client_message_event(client->window, msgtype))
+                client_set_wm_state(client, e->xclient.data.l[0]);
         } else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) {
-            compress_client_message_event(e, &ce, client->window, msgtype);
-            if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
-                (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
+            if (!more_client_message_event(client->window, msgtype) &&
+                ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
+                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL))
+            {
                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
                                    FALSE, FALSE);
+            }
         } else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) {
             gulong ignore_start;
 
@@ -1566,36 +1571,16 @@ static void event_handle_client(ObClient *client, XEvent *e)
         /* validate cuz we query stuff off the client here */
         if (!client_validate(client)) break;
 
-        /* compress changes to a single property into a single change */
-        while (XCheckTypedWindowEvent(obt_display, client->window,
-                                      e->type, &ce)) {
-            Atom a, b;
-
-            /* XXX: it would be nice to compress ALL changes to a property,
-               not just changes in a row without other props between. */
-
-            a = ce.xproperty.atom;
-            b = e->xproperty.atom;
-
-            if (a == b)
-                continue;
-            if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
-                 a == OBT_PROP_ATOM(WM_NAME) ||
-                 a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
-                 a == OBT_PROP_ATOM(WM_ICON_NAME))
-                &&
-                (b == OBT_PROP_ATOM(NET_WM_NAME) ||
-                 b == OBT_PROP_ATOM(WM_NAME) ||
-                 b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
-                 b == OBT_PROP_ATOM(WM_ICON_NAME))) {
-                continue;
-            }
-            if (a == OBT_PROP_ATOM(NET_WM_ICON) &&
-                b == OBT_PROP_ATOM(NET_WM_ICON))
-                continue;
+        msgtype = e->xproperty.atom;
 
-            XPutBackEvent(obt_display, &ce);
-            break;
+        /* ignore changes to some properties if there is another change
+           coming in the queue */
+        {
+            struct ObSkipPropertyChange s;
+            s.window = client->window;
+            s.prop = msgtype;
+            if (xqueue_exists_local(skip_property_change, &s))
+                break;
         }
 
         msgtype = e->xproperty.atom;
@@ -1974,13 +1959,13 @@ static gboolean event_handle_menu_input(XEvent *ev)
     return ret;
 }
 
-static Bool event_look_for_menu_enter(Display *d, XEvent *ev, XPointer arg)
+static gboolean event_look_for_menu_enter(XEvent *ev, gpointer data)
 {
-    ObMenuFrame *f = (ObMenuFrame*)arg;
+    const ObMenuFrame *f = (ObMenuFrame*)data;
     ObMenuEntryFrame *e;
     return ev->type == EnterNotify &&
         (e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
-        !e->ignore_enters && e->frame == f;
+        e->frame == f && !e->ignore_enters;
 }
 
 static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
@@ -2005,16 +1990,10 @@ static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
         if (ev->xcrossing.detail == NotifyInferior)
             break;
 
-        if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)))
-        {
-            XEvent ce;
-
+        if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
             /* check if an EnterNotify event is coming, and if not, then select
                nothing in the menu */
-            if (XCheckIfEvent(obt_display, &ce, event_look_for_menu_enter,
-                              (XPointer)e->frame))
-                XPutBackEvent(obt_display, &ce);
-            else
+            if (!xqueue_exists_local(event_look_for_menu_enter, e->frame))
                 menu_frame_select(e->frame, NULL, FALSE);
         }
         break;
@@ -2218,16 +2197,19 @@ gboolean event_time_after(guint32 t1, guint32 t2)
         return t1 >= t2 && t1 < (t2 + TIME_HALF);
 }
 
-Bool find_timestamp(Display *d, XEvent *e, XPointer a)
+gboolean find_timestamp(XEvent *e, gpointer data)
 {
     const Time t = event_get_timestamp(e);
-    return t != CurrentTime;
+    if (t != CurrentTime) {
+        event_curtime = t;
+        return TRUE;
+    }
+    else
+        return FALSE;
 }
 
 Time event_time(void)
 {
-    XEvent event;
-
     if (event_curtime) return event_curtime;
 
     /* Some events don't come with timestamps :(
@@ -2240,10 +2222,12 @@ Time event_time(void)
                     8, PropModeAppend, NULL, 0);
 
     /* Grab the first timestamp available */
-    XPeekIfEvent(obt_display, &event, find_timestamp, NULL);
+    xqueue_exists(find_timestamp, NULL);
+
+    /*g_assert(event_curtime != CurrentTime);*/
 
     /* Save the time so we don't have to do this again for this event */
-    return event_curtime = event.xproperty.time;
+    return event_curtime;
 }
 
 Time event_source_time(void)
index 5e1351d..6c3ee6f 100644 (file)
@@ -30,6 +30,7 @@
 #include "screen.h"
 #include "obrender/theme.h"
 #include "obt/display.h"
+#include "obt/xqueue.h"
 #include "obt/prop.h"
 
 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
@@ -1042,33 +1043,24 @@ void frame_grab_client(ObFrame *self)
     window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client));
 }
 
-void frame_release_client(ObFrame *self)
+static gboolean find_reparent(XEvent *e, gpointer data)
 {
-    XEvent ev;
-    gboolean reparent = TRUE;
+    const ObFrame *self = data;
+
+    /* Find ReparentNotify events for the window that aren't being reparented into the
+       frame, thus the client reparenting itself off the frame. */
+    return e->type == ReparentNotify && e->xreparent.window == self->client->window &&
+        e->xreparent.parent != self->window;
+}
 
+void frame_release_client(ObFrame *self)
+{
     /* if there was any animation going on, kill it */
     obt_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
                                       self, FALSE);
 
     /* check if the app has already reparented its window away */
-    while (XCheckTypedWindowEvent(obt_display, self->client->window,
-                                  ReparentNotify, &ev))
-    {
-        /* This check makes sure we don't catch our own reparent action to
-           our frame window. This doesn't count as the app reparenting itself
-           away of course.
-
-           Reparent events that are generated by us are just discarded here.
-           They are of no consequence to us anyhow.
-        */
-        if (ev.xreparent.parent != self->window) {
-            reparent = FALSE;
-            break;
-        }
-    }
-
-    if (reparent) {
+    if (!xqueue_exists_local(find_reparent, self)) {
         /* according to the ICCCM - if the client doesn't reparent itself,
            then we will reparent the window to root for them */
         XReparentWindow(obt_display, self->client->window, obt_root(ob_screen),
index cb0d210..8ee88fc 100644 (file)
@@ -32,6 +32,7 @@
 #include "obrender/render.h"
 #include "obrender/theme.h"
 #include "obt/display.h"
+#include "obt/xqueue.h"
 #include "obt/prop.h"
 #include "obt/keyboard.h"
 
@@ -672,7 +673,8 @@ static void move_with_keys(KeySym sym, guint state)
     XSync(obt_display, FALSE);
     {
         XEvent ce;
-        while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
+        while (xqueue_remove_local(&ce, xqueue_match_type,
+                                   GINT_TO_POINTER(MotionNotify)));
     }
     screen_pointer_pos(&px, &py);
 
@@ -831,7 +833,8 @@ static void resize_with_keys(KeySym sym, guint state)
     XSync(obt_display, FALSE);
     {
         XEvent ce;
-        while (XCheckTypedEvent(obt_display, MotionNotify, &ce));
+        while (xqueue_remove_local(&ce, xqueue_match_type,
+                                   GINT_TO_POINTER(MotionNotify)));
     }
     screen_pointer_pos(&px, &py);
 
index 2ff950a..5246d34 100644 (file)
@@ -36,6 +36,7 @@
 #include "obrender/render.h"
 #include "gettext.h"
 #include "obt/display.h"
+#include "obt/xqueue.h"
 #include "obt/prop.h"
 #include "obt/mainloop.h"
 
@@ -129,14 +130,16 @@ static gboolean replace_wm(void)
 
     /* Wait for old window manager to go away */
     if (current_wm_sn_owner) {
-      XEvent event;
       gulong wait = 0;
       const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
+      ObtXQueueWindowType wt;
+
+      wt.window = current_wm_sn_owner;
+      wt.type = DestroyNotify;
 
       while (wait < timeout) {
-          if (XCheckWindowEvent(obt_display, current_wm_sn_owner,
-                                StructureNotifyMask, &event) &&
-              event.type == DestroyNotify)
+          /* Checks the local queue and incoming events for this event */
+          if (xqueue_exists_local(xqueue_match_window_type, &wt))
               break;
           g_usleep(G_USEC_PER_SEC / 10);
           wait += G_USEC_PER_SEC / 10;
index c8cb348..ad61294 100644 (file)
@@ -26,6 +26,7 @@
 #include "prompt.h"
 #include "debug.h"
 #include "grab.h"
+#include "obt/xqueue.h"
 
 static GHashTable *window_map;
 
@@ -146,16 +147,15 @@ void window_manage_all(void)
     if (children) XFree(children);
 }
 
-static Bool check_unmap(Display *d, XEvent *e, XPointer arg)
+static gboolean check_unmap(XEvent *e, gpointer data)
 {
-    const Window win = *(Window*)arg;
+    const Window win = *(Window*)data;
     return ((e->type == DestroyNotify && e->xdestroywindow.window == win) ||
             (e->type == UnmapNotify && e->xunmap.window == win));
 }
 
 void window_manage(Window win)
 {
-    XEvent e;
     XWindowAttributes attrib;
     gboolean no_manage = FALSE;
     gboolean is_dockapp = FALSE;
@@ -165,12 +165,11 @@ void window_manage(Window win)
 
     /* check if it has already been unmapped by the time we started
        mapping. the grab does a sync so we don't have to here */
-    if (XCheckIfEvent(obt_display, &e, check_unmap, (XPointer)&win)) {
+    if (xqueue_exists_local(check_unmap, &win)) {
         ob_debug("Trying to manage unmapped window. Aborting that.");
         no_manage = TRUE;
     }
-
-    if (!XGetWindowAttributes(obt_display, win, &attrib))
+    else if (!XGetWindowAttributes(obt_display, win, &attrib))
         no_manage = TRUE;
     else {
         XWMHints *wmhints;