Allow moving fullscreen windows between monitors
[openbox.git] / obt / xqueue.c
blobc04b226b55ca1f9bcf2efca3e618a07226a63420
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/display.c for the Openbox window manager
4 Copyright (c) 2007 Dana Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
19 #include "obt/xqueue.h"
20 #include "obt/display.h"
22 #define MINSZ 16
24 static XEvent *q = NULL;
25 static gulong qsz = 0;
26 static gulong qstart; /* the first event in the queue */
27 static gulong qend; /* the last event in the queue */
28 static gulong qnum = 0;
30 static inline void shrink(void) {
31 if (qsz > MINSZ && qnum < qsz / 4) {
32 const gulong newsz = qsz/2;
33 gulong i;
35 if (qnum == 0) {
36 qstart = 0;
37 qend = -1;
40 /* all in the shinking part, move it to pos 0 */
41 else if (qstart >= newsz && qend >= newsz) {
42 for (i = 0; i < qnum; ++i)
43 q[i] = q[qstart+i];
44 qstart = 0;
45 qend = qnum - 1;
48 /* it wraps around to 0 right now, move the part between newsz and qsz
49 to be before newsz */
50 else if (qstart >= newsz) {
51 const gulong n = qsz - qstart;
52 for (i = 0; i < n; ++i)
53 q[newsz-n+i] = q[qstart+i];
54 qstart = newsz-n;
57 /* it needs to wrap around to 0, move the stuff after newsz to pos 0 */
58 else if (qend >= newsz) {
59 const gulong n = qend + 1 - newsz;
60 for (i = 0; i < n; ++i)
61 q[i] = q[newsz+i];
62 qend = n - 1;
65 q = g_renew(XEvent, q, newsz);
66 qsz = newsz;
70 static inline void grow(void) {
71 if (qnum == qsz) {
72 const gulong newsz = qsz*2;
73 gulong i;
75 q = g_renew(XEvent, q, newsz);
77 g_assert(qnum > 0);
79 if (qend < qstart) { /* it wraps around to 0 right now */
80 for (i = 0; i <= qend; ++i)
81 q[qsz+i] = q[i];
82 qend = qsz + qend;
85 qsz = newsz;
89 /* Grab all pending X events */
90 static gboolean read_events(gboolean block)
92 gint sth, n;
94 n = XEventsQueued(obt_display, QueuedAfterFlush) > 0;
95 sth = FALSE;
97 while ((block && !sth) || n > 0) {
98 XEvent e;
100 if (XNextEvent(obt_display, &e) != Success)
101 return FALSE;
103 grow(); /* make sure there is room */
105 ++qnum;
106 qend = (qend + 1) % qsz; /* move the end */
107 q[qend] = e; /* stick the event at the end */
109 --n;
110 sth = TRUE;
113 return sth; /* return if we read anything */
116 static void pop(const gulong p)
118 /* remove the event */
119 --qnum;
120 if (qnum == 0) {
121 qstart = 0;
122 qend = -1;
124 else if (p == qstart)
125 qstart = (qstart + 1) % qsz;
126 else {
127 gulong pi;
129 /* is it cheaper to move the start or the end ? */
130 if ((p >= qstart && p < qstart + qnum/2) ||
131 (p < qstart && p < (qstart + qnum/2) % qsz))
133 /* move the start */
134 pi = p;
135 while (pi != qstart) {
136 const gulong pi_next = (pi == 0 ? qsz-1 : pi-1);
138 q[pi] = q[pi_next];
139 pi = pi_next;
141 qstart = (qstart + 1) % qsz;
143 else {
144 /* move the end */
145 pi = p;
146 while (pi != qend) {
147 const gulong pi_next = (pi + 1) % qsz;
149 q[pi] = q[pi_next];
150 pi = pi_next;
152 qend = (qend == 0 ? qsz-1 : qend-1);
156 shrink(); /* shrink the q if too little in it */
159 void xqueue_init(void)
161 if (q != NULL) return;
162 qsz = MINSZ;
163 q = g_new(XEvent, qsz);
164 qstart = 0;
165 qend = -1;
168 void xqueue_destroy(void)
170 if (q == NULL) return;
171 g_free(q);
172 q = NULL;
173 qsz = 0;
176 gboolean xqueue_match_window(XEvent *e, gpointer data)
178 const Window w = *(Window*)data;
179 return e->xany.window == w;
182 gboolean xqueue_match_type(XEvent *e, gpointer data)
184 return e->type == GPOINTER_TO_INT(data);
187 gboolean xqueue_match_window_type(XEvent *e, gpointer data)
189 const ObtXQueueWindowType x = *(ObtXQueueWindowType*)data;
190 return e->xany.window == x.window && e->type == x.type;
193 gboolean xqueue_match_window_message(XEvent *e, gpointer data)
195 const ObtXQueueWindowMessage x = *(ObtXQueueWindowMessage*)data;
196 return e->xany.window == x.window && e->type == ClientMessage &&
197 e->xclient.message_type == x.message;
200 gboolean xqueue_peek(XEvent *event_return)
202 g_return_val_if_fail(q != NULL, FALSE);
203 g_return_val_if_fail(event_return != NULL, FALSE);
205 if (!qnum) read_events(TRUE);
206 if (!qnum) return FALSE;
207 *event_return = q[qstart]; /* get the head */
208 return TRUE;
211 gboolean xqueue_peek_local(XEvent *event_return)
213 g_return_val_if_fail(q != NULL, FALSE);
214 g_return_val_if_fail(event_return != NULL, FALSE);
216 if (!qnum) read_events(FALSE);
217 if (!qnum) return FALSE;
218 *event_return = q[qstart]; /* get the head */
219 return TRUE;
222 gboolean xqueue_next(XEvent *event_return)
224 g_return_val_if_fail(q != NULL, FALSE);
225 g_return_val_if_fail(event_return != NULL, FALSE);
227 if (!qnum) read_events(TRUE);
228 if (qnum) {
229 *event_return = q[qstart]; /* get the head */
230 pop(qstart);
231 return TRUE;
234 return FALSE;
237 gboolean xqueue_next_local(XEvent *event_return)
239 g_return_val_if_fail(q != NULL, FALSE);
240 g_return_val_if_fail(event_return != NULL, FALSE);
242 if (!qnum) read_events(FALSE);
243 if (qnum) {
244 *event_return = q[qstart]; /* get the head */
245 pop(qstart);
246 return TRUE;
249 return FALSE;
252 gboolean xqueue_exists(xqueue_match_func match, gpointer data)
254 gulong i, checked;
256 g_return_val_if_fail(q != NULL, FALSE);
257 g_return_val_if_fail(match != NULL, FALSE);
259 checked = 0;
260 while (TRUE) {
261 for (i = checked; i < qnum; ++i, ++checked) {
262 const gulong p = (qstart + i) % qsz;
263 if (match(&q[p], data))
264 return TRUE;
266 if (!read_events(TRUE)) break; /* error */
268 return FALSE;
271 gboolean xqueue_exists_local(xqueue_match_func match, gpointer data)
273 gulong i, checked;
275 g_return_val_if_fail(q != NULL, FALSE);
276 g_return_val_if_fail(match != NULL, FALSE);
278 checked = 0;
279 while (TRUE) {
280 for (i = checked; i < qnum; ++i, ++checked) {
281 const gulong p = (qstart + i) % qsz;
282 if (match(&q[p], data))
283 return TRUE;
285 if (!read_events(FALSE)) break;
287 return FALSE;
290 gboolean xqueue_remove_local(XEvent *event_return,
291 xqueue_match_func match, gpointer data)
293 gulong i, checked;
295 g_return_val_if_fail(q != NULL, FALSE);
296 g_return_val_if_fail(event_return != NULL, FALSE);
297 g_return_val_if_fail(match != NULL, FALSE);
299 checked = 0;
300 while (TRUE) {
301 for (i = checked; i < qnum; ++i, ++checked) {
302 const gulong p = (qstart + i) % qsz;
303 if (match(&q[p], data)) {
304 *event_return = q[p];
305 pop(p);
306 return TRUE;
309 if (!read_events(FALSE)) break;
311 return FALSE;
314 gboolean xqueue_pending_local(void)
316 g_return_val_if_fail(q != NULL, FALSE);
318 if (!qnum) read_events(FALSE);
319 return qnum != 0;
322 typedef struct _ObtXQueueCB {
323 ObtXQueueFunc func;
324 gpointer data;
325 } ObtXQueueCB;
327 static ObtXQueueCB *callbacks = NULL;
328 static guint n_callbacks = 0;
330 static gboolean event_read(GSource *source, GSourceFunc callback,
331 gpointer data)
333 XEvent ev;
335 while (xqueue_next_local(&ev)) {
336 guint i;
337 for (i = 0; i < n_callbacks; ++i)
338 callbacks[i].func(&ev, callbacks[i].data);
341 return TRUE; /* repeat */
344 static gboolean x_source_prepare(GSource *source, gint *timeout)
346 *timeout = -1;
347 return XPending(obt_display);
350 static gboolean x_source_check(GSource *source)
352 return XPending(obt_display);
355 struct x_source {
356 GSource source;
358 GPollFD pfd;
361 static GSourceFuncs x_source_funcs = {
362 x_source_prepare,
363 x_source_check,
364 event_read,
365 NULL
368 void xqueue_listen(void)
370 GSource *source = g_source_new(&x_source_funcs, sizeof(struct x_source));
371 struct x_source *x_source = (struct x_source *)source;
372 GPollFD *pfd = &x_source->pfd;
374 *pfd = (GPollFD){ ConnectionNumber(obt_display), G_IO_IN, G_IO_IN };
375 g_source_add_poll(source, pfd);
376 g_source_attach(source, NULL);
379 void xqueue_add_callback(ObtXQueueFunc f, gpointer data)
381 guint i;
383 g_return_if_fail(f != NULL);
385 for (i = 0; i < n_callbacks; ++i)
386 if (callbacks[i].func == f && callbacks[i].data == data)
387 return;
389 callbacks = g_renew(ObtXQueueCB, callbacks, n_callbacks + 1);
390 callbacks[n_callbacks].func = f;
391 callbacks[n_callbacks].data = data;
392 ++n_callbacks;
395 void xqueue_remove_callback(ObtXQueueFunc f, gpointer data)
397 guint i;
399 g_return_if_fail(f != NULL);
401 for (i = 0; i < n_callbacks; ++i) {
402 if (callbacks[i].func == f && callbacks[i].data == data) {
403 /* remove it */
404 for (; i < n_callbacks - 1; ++i)
405 callbacks[i] = callbacks[i+1];
406 callbacks = g_renew(ObtXQueueCB, callbacks, n_callbacks - 1);
407 --n_callbacks;
408 break;