e5d2da317de43419a677e592d110f7b598dcf5b6
[mikachu/openbox.git] / obt / xqueue.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    obt/display.c for the Openbox window manager
4    Copyright (c) 2007        Dana Jansens
5
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.
10
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.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "obt/xqueue.h"
20 #include "obt/display.h"
21
22 #define MINSZ 16
23
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;
29
30 static inline void shrink(void) {
31     if (qsz > MINSZ && qnum < qsz / 4) {
32         const gulong newsz = qsz/2;
33         gulong i;
34
35         if (qnum == 0) {
36             qstart = 0;
37             qend = -1;
38         }
39
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;
46         }
47
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;
55         }
56
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;
63         }
64
65         q = g_renew(XEvent, q, newsz);
66         qsz = newsz;
67     }
68 }
69
70 static inline void grow(void) {
71     if (qnum == qsz) {
72         const gulong newsz = qsz*2;
73         gulong i;
74  
75         q = g_renew(XEvent, q, newsz);
76
77         g_assert(qnum > 0);
78
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;
83         }
84
85         qsz = newsz;
86     }
87 }
88
89 /* Grab all pending X events */
90 static gboolean read_events(gboolean block)
91 {
92     gint sth, n;
93
94     n = XEventsQueued(obt_display, QueuedAfterFlush) > 0;
95     sth = FALSE;
96
97     while ((block && !sth) || n > 0) {
98         XEvent e;
99
100         if (XNextEvent(obt_display, &e) != Success)
101             return FALSE;
102
103         grow(); /* make sure there is room */
104
105         ++qnum;
106         qend = (qend + 1) % qsz; /* move the end */
107         q[qend] = e; /* stick the event at the end */
108
109         --n;
110         sth = TRUE;
111     }
112
113     return sth; /* return if we read anything */
114 }
115
116 static void pop(const gulong p)
117 {
118     /* remove the event */
119     --qnum;
120     if (qnum == 0) {
121         qstart = 0;
122         qend = -1;
123     }
124     else if (p == qstart)
125         qstart = (qstart + 1) % qsz;
126     else {
127         gulong pi;
128
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))
132         {
133             /* move the start */
134             pi = p;
135             while (pi != qstart) {
136                 const gulong pi_next = (pi == 0 ? qsz-1 : pi-1);
137
138                 q[pi] = q[pi_next];
139                 pi = pi_next;
140             }
141             qstart = (qstart + 1) % qsz;
142         }
143         else {
144             /* move the end */
145             pi = p;
146             while (pi != qend) {
147                 const gulong pi_next = (pi + 1) % qsz;
148
149                 q[pi] = q[pi_next];
150                 pi = pi_next;
151             }
152             qend = (qend == 0 ? qsz-1 : qend-1);
153         }
154     }
155
156     shrink(); /* shrink the q if too little in it */
157 }
158
159 void xqueue_init(void)
160 {
161     if (q != NULL) return;
162     qsz = MINSZ;
163     q = g_new(XEvent, qsz);
164     qstart = 0;
165     qend = -1;
166 }
167
168 void xqueue_destroy(void)
169 {
170     if (q == NULL) return;
171     g_free(q);
172     q = NULL;
173     qsz = 0;
174 }
175
176 gboolean xqueue_match_window(XEvent *e, gpointer data)
177 {
178     const Window w = *(Window*)data;
179     return e->xany.window == w;
180 }
181
182 gboolean xqueue_match_type(XEvent *e, gpointer data)
183 {
184     return e->type == GPOINTER_TO_INT(data);
185 }
186
187 gboolean xqueue_match_window_type(XEvent *e, gpointer data)
188 {
189     const ObtXQueueWindowType x = *(ObtXQueueWindowType*)data;
190     return e->xany.window == x.window && e->type == x.type;
191 }
192
193 gboolean xqueue_match_window_message(XEvent *e, gpointer data)
194 {
195     const ObtXQueueWindowMessage x = *(ObtXQueueWindowMessage*)data;
196     return e->xany.window == x.window && e->type == ClientMessage &&
197         e->xclient.message_type == x.message;
198 }
199
200 gboolean xqueue_peek(XEvent *event_return)
201 {
202     g_return_val_if_fail(q != NULL, FALSE);
203     g_return_val_if_fail(event_return != NULL, FALSE);
204
205     if (!qnum) read_events(TRUE);
206     if (!qnum) return FALSE;
207     *event_return = q[qstart]; /* get the head */
208     return TRUE;
209 }
210
211 gboolean xqueue_peek_local(XEvent *event_return)
212 {
213     g_return_val_if_fail(q != NULL, FALSE);
214     g_return_val_if_fail(event_return != NULL, FALSE);
215
216     if (!qnum) read_events(FALSE);
217     if (!qnum) return FALSE;
218     *event_return = q[qstart]; /* get the head */
219     return TRUE;
220 }
221
222 gboolean xqueue_next(XEvent *event_return)
223 {
224     g_return_val_if_fail(q != NULL, FALSE);
225     g_return_val_if_fail(event_return != NULL, FALSE);
226
227     if (!qnum) read_events(TRUE);
228     if (qnum) {
229         *event_return = q[qstart]; /* get the head */
230         pop(qstart);
231         return TRUE;
232     }
233
234     return FALSE;
235 }
236
237 gboolean xqueue_next_local(XEvent *event_return)
238 {
239     g_return_val_if_fail(q != NULL, FALSE);
240     g_return_val_if_fail(event_return != NULL, FALSE);
241
242     if (!qnum) read_events(FALSE);
243     if (qnum) {
244         *event_return = q[qstart]; /* get the head */
245         pop(qstart);
246         return TRUE;
247     }
248
249     return FALSE;
250 }
251
252 gboolean xqueue_exists(xqueue_match_func match, gpointer data)
253 {
254     gulong i, checked;
255
256     g_return_val_if_fail(q != NULL, FALSE);
257     g_return_val_if_fail(match != NULL, FALSE);
258
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;
265         }
266         if (!read_events(TRUE)) break; /* error */
267     }
268     return FALSE;
269 }
270
271 gboolean xqueue_exists_local(xqueue_match_func match, gpointer data)
272 {
273     gulong i, checked;
274
275     g_return_val_if_fail(q != NULL, FALSE);
276     g_return_val_if_fail(match != NULL, FALSE);
277
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;
284         }
285         if (!read_events(FALSE)) break;
286     }
287     return FALSE;
288 }
289
290 gboolean xqueue_remove_local(XEvent *event_return,
291                              xqueue_match_func match, gpointer data)
292 {
293     gulong i, checked;
294
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);
298
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;
307             }
308         }
309         if (!read_events(FALSE)) break;
310     }
311     return FALSE;
312 }
313
314 gboolean xqueue_pending_local(void)
315 {
316     g_return_val_if_fail(q != NULL, FALSE);
317     
318     if (!qnum) read_events(FALSE);
319     return qnum != 0;
320 }