summaryrefslogtreecommitdiff
path: root/obt
diff options
context:
space:
mode:
authorDana Jansens <danakj@orodu.net>2010-04-28 12:57:51 -0400
committerDana Jansens <danakj@orodu.net>2010-04-28 12:58:42 -0400
commit55b84316bb699fa530efe78d75ae8e1d57c1b57f (patch)
tree4991974287c7adfdea0680b5d96d42a01c40a97a /obt
parent029628087fa0090e7c3b1598786a1bf1712e0db9 (diff)
make an event queue for X events. the queue's min size is 16 XEvents (~3k)
Diffstat (limited to 'obt')
-rw-r--r--obt/display.c13
-rw-r--r--obt/mainloop.c10
-rw-r--r--obt/xqueue.c320
-rw-r--r--obt/xqueue.h92
4 files changed, 428 insertions, 7 deletions
diff --git a/obt/display.c b/obt/display.c
index 37b12157..8b06cbfc 100644
--- a/obt/display.c
+++ b/obt/display.c
@@ -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>
@@ -31,6 +32,10 @@
# 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)
diff --git a/obt/mainloop.c b/obt/mainloop.c
index ecdd7f7f..75366256 100644
--- a/obt/mainloop.c
+++ b/obt/mainloop.c
@@ -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
index 00000000..2304ea42
--- /dev/null
+++ b/obt/xqueue.c
@@ -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
index 00000000..8b312785
--- /dev/null
+++ b/obt/xqueue.h
@@ -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