diff options
| author | Dana Jansens <danakj@orodu.net> | 2010-04-28 12:57:51 -0400 |
|---|---|---|
| committer | Dana Jansens <danakj@orodu.net> | 2010-04-28 12:58:42 -0400 |
| commit | 55b84316bb699fa530efe78d75ae8e1d57c1b57f (patch) | |
| tree | 4991974287c7adfdea0680b5d96d42a01c40a97a /obt | |
| parent | 029628087fa0090e7c3b1598786a1bf1712e0db9 (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.c | 13 | ||||
| -rw-r--r-- | obt/mainloop.c | 10 | ||||
| -rw-r--r-- | obt/xqueue.c | 320 | ||||
| -rw-r--r-- | obt/xqueue.h | 92 |
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 |
