make an event queue for X events. the queue's min size is 16 XEvents (~3k)
This commit is contained in:
parent
029628087f
commit
55b84316bb
11 changed files with 590 additions and 187 deletions
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
320
obt/xqueue.c
Normal file
320
obt/xqueue.c
Normal file
|
@ -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;
|
||||
}
|
92
obt/xqueue.h
Normal file
92
obt/xqueue.h
Normal file
|
@ -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
|
|
@ -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)
|
||||
struct ObClientFindDestroyUnmap {
|
||||
Window window;
|
||||
gint ignore_unmaps;
|
||||
};
|
||||
|
||||
static gboolean find_destroy_unmap(XEvent *e, gpointer data)
|
||||
{
|
||||
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
|
||||
|
||||
/* put them back on the event stack so they end up in the same order */
|
||||
XPutBackEvent(obt_display, &e);
|
||||
}
|
||||
|
||||
return ret;
|
||||
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);
|
||||
|
|
222
openbox/event.c
222
openbox/event.c
|
@ -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)
|
||||
{
|
||||
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 the frame window");
|
||||
"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 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);
|
||||
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,35 +1571,15 @@ 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;
|
||||
msgtype = e->xproperty.atom;
|
||||
|
||||
/* 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;
|
||||
|
||||
XPutBackEvent(obt_display, &ce);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
static gboolean find_reparent(XEvent *e, gpointer data)
|
||||
{
|
||||
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)
|
||||
{
|
||||
XEvent ev;
|
||||
gboolean reparent = TRUE;
|
||||
|
||||
/* 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),
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue