Move initialisation code into its own section, if supported by the linker.
[gwm.git] / window-table.c
blobfb71d867f9750933032379f9881d9be22fda20eb
1 /*
2 * window-table.c
4 * Part of gwm, the Gratuitous Window Manager,
5 * by Gary Wong, <gtw@gnu.org>.
7 * Copyright (C) 2009 Gary Wong
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of version 3 of the GNU General Public License as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * $Id$
24 #include <config.h>
26 #include <assert.h>
27 #include <limits.h>
28 #if HAVE_STDINT_H
29 #include <stdint.h>
30 #endif
31 #include <stdlib.h>
32 #include <xcb/xcb.h>
34 #include "gwm.h"
36 #include "window-table.h"
38 /* Cuckoo hashed window table: see _Cuckoo Hashing_, R. Pagh and F. Rodler,
39 Journal of Algorithms 51 (2004), pp. 122-144. */
41 struct window_table windows, update_windows;
43 extern INIT void table_init( struct window_table *table ) {
45 table->used = 0;
46 table->n = 32;
47 table->max = 24;
48 table->hash = 0xBA97D5B1UL;
50 table->t[ 0 ] = xcalloc( table->n << 1, sizeof table->t[ 0 ][ 0 ] );
51 table->t[ 1 ] = table->t[ 0 ] + table->n;
52 table->values = xmalloc( table->n * sizeof *table->values );
55 #if DEBUG
56 extern void table_destroy( struct window_table *table ) {
58 free( table->t[ 0 ] );
59 free( table->values );
61 #endif
63 static CONST int table_hash( unsigned long hash, int n, int half,
64 xcb_window_t key ) {
66 unsigned long h0, h1;
67 #if defined( UINT64_MAX ) || defined( uint64_t )
68 uint64_t l;
69 #else
70 unsigned long l;
71 #endif
73 if( half ) {
74 h0 = hash ^ 0x79D9C6D4UL;
75 h1 = hash * 0x6A391E85UL;
76 } else {
77 h0 = hash;
78 h1 = hash ^ 0x52C694B6UL;
81 #if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL
82 l = key * h0;
83 l ^= l >> 32;
84 l *= h1;
85 l ^= l >> 29;
86 #else
87 l = key * h0;
88 l ^= l >> 16;
89 l *= h1;
90 l ^= l >> 13;
91 l *= h0;
92 l ^= l >> 15;
93 #endif
95 return l & ( n - 1 );
98 static PURE int table_lookup_index( struct window_table *table,
99 xcb_window_t key ) {
101 int h;
103 assert( key );
105 h = table_hash( table->hash, table->n, 0, key );
106 if( table->t[ 0 ][ h ].key == key )
107 return table->t[ 0 ][ h ].index;
109 h = table_hash( table->hash, table->n, 1, key );
110 if( table->t[ 1 ][ h ].key == key )
111 return table->t[ 1 ][ h ].index;
113 return -1;
116 extern PURE struct gwm_window *table_lookup( struct window_table *table,
117 xcb_window_t key ) {
119 int index;
121 if( !key )
122 return NULL;
124 return ( index = table_lookup_index( table, key ) ) >= 0 ?
125 table->values[ index ] : NULL;
128 static void table_insert_index( struct window_table *table, xcb_window_t key,
129 int index );
131 static void table_rehash( struct window_table *table, unsigned long hash,
132 unsigned long n, unsigned long max ) {
134 int old_n = table->n;
135 struct half_table *old_t[ 2 ];
136 int i;
138 old_t[ 0 ] = table->t[ 0 ];
139 old_t[ 1 ] = table->t[ 1 ];
141 table->hash = hash;
142 table->n = n;
143 table->max = max;
144 table->used = 0;
146 table->t[ 0 ] = xcalloc( table->n << 1, sizeof table->t[ 0 ][ 0 ] );
147 table->t[ 1 ] = table->t[ 0 ] + table->n;
148 table->values = xrealloc( table->values, table->n * sizeof *table->values );
150 /* We've now gotten everything we want out of the old table. Although
151 table_insert might possibly cause inefficient re-entrancy, that case
152 should be both harmless and exceedingly rare. */
153 for( i = 0; i < old_n; i++ ) {
154 if( old_t[ 0 ][ i ].key )
155 table_insert_index( table, old_t[ 0 ][ i ].key,
156 old_t[ 0 ][ i ].index );
157 if( old_t[ 1 ][ i ].key )
158 table_insert_index( table, old_t[ 1 ][ i ].key,
159 old_t[ 1 ][ i ].index );
162 free( old_t[ 0 ] );
165 static void table_insert_index( struct window_table *table, xcb_window_t key,
166 int index ) {
168 table->used++;
170 for(;;) {
171 int i;
173 for( i = 0; i < table->max; i++ ) {
174 int h = table_hash( table->hash, table->n, i & 1, key );
175 xcb_window_t old_key = table->t[ i & 1 ][ h ].key;
176 int old_index = table->t[ i & 1 ][ h ].index;
178 table->t[ i & 1 ][ h ].key = key;
179 table->t[ i & 1 ][ h ].index = index;
181 if( !old_key )
182 return;
184 key = old_key;
185 index = old_index;
188 /* Failure to insert: rehash. */
189 table_rehash( table, table->hash + 0x5DB1BA96UL,
190 table->n, table->max );
194 static void table_insert( struct window_table *table, xcb_window_t key,
195 struct gwm_window *value ) {
197 if( !key )
198 return;
200 assert( !table_lookup( table, key ) );
202 table->values[ table->used ] = value;
204 if( table->used > ( ( table->n * 3 ) >> 2 ) )
205 /* Load becoming high: resize. */
206 table_rehash( table, table->hash, table->n << 1, table->max + 4 );
208 table_insert_index( table, key, table->used );
211 static void table_delete( struct window_table *table, xcb_window_t key ) {
213 int h;
214 int i;
216 if( !key )
217 return;
219 for( i = 0; i < 2; i++ ) {
220 h = table_hash( table->hash, table->n, i, key );
221 if( table->t[ i ][ h ].key == key ) {
222 int index;
223 xcb_window_t replacement_key;
224 struct gwm_window *replacement_value;
225 int replacement_hash;
227 table->used--;
229 if( ( index = table->t[ i ][ h ].index ) < table->used ) {
230 /* Must replace our old index, to keep the values array
231 compact. */
232 table->values[ index ] = replacement_value =
233 table->values[ table->used ];
234 replacement_key = replacement_value->w;
236 replacement_hash = table_hash( table->hash, table->n, 0,
237 replacement_key );
238 if( table->t[ 0 ][ replacement_hash ].key == replacement_key )
239 table->t[ 0 ][ replacement_hash ].index = index;
240 else {
241 replacement_hash = table_hash( table->hash, table->n, 1,
242 replacement_key );
243 assert( table->t[ 1 ][ replacement_hash ].key ==
244 replacement_key );
245 table->t[ 1 ][ replacement_hash ].index = index;
249 table->t[ i ][ h ].key = 0;
251 if( table->used < ( ( table->n * 3 ) >> 4 ) && table->n > 32 )
252 /* Load becoming low: resize. */
253 table_rehash( table, table->hash, table->n >> 1,
254 table->max - 4 );
255 return;
260 extern void queue_window_update( struct gwm_window *window,
261 int x, int y, int width, int height,
262 int cleared ) {
264 /* FIXME Consider computing the region instead of the bounding box. */
266 if( !window->update.width || !window->update.height ) {
267 /* New update region. */
268 window->update.x = x;
269 window->update.y = y;
270 window->update.width = width;
271 window->update.height = height;
273 if( !cleared )
274 window->cleared = FALSE;
275 } else {
276 /* Compute the bounding box of the union of the old and new regions.
277 If the bounding box is enlarged, we reset the cleared flag,
278 because it is very likely that it now includes areas which
279 were not in either of the two original regions (and therefore
280 not guaranteed to be cleared). */
281 if( x < window->update.x ) {
282 window->update.width += window->update.x - x;
283 window->update.x = x;
284 window->cleared = FALSE;
287 if( y < window->update.y ) {
288 window->update.height += window->update.y - y;
289 window->update.y = y;
290 window->cleared = FALSE;
293 if( x + width > window->update.x + window->update.width ) {
294 window->update.width = x + width - window->update.x;
295 window->cleared = FALSE;
298 if( y + height > window->update.y + window->update.height ) {
299 window->update.height = y + height - window->update.y;
300 window->cleared = FALSE;
304 if( table_lookup( &update_windows, window->w ) )
305 return;
307 table_insert( &update_windows, window->w, window );
310 extern void window_update_done( struct gwm_window *window ) {
312 window->update.x = 0;
313 window->update.y = 0;
314 window->update.width = 0;
315 window->update.height = 0;
316 window->cleared = TRUE;
318 table_delete( &update_windows, window->w );
321 extern MALLOC struct gwm_window *add_window( xcb_window_t w ) {
323 struct gwm_window *window;
325 if( table_lookup( &windows, w ) )
326 return NULL; /* window already exists */
328 window = xmalloc( sizeof *window );
329 window->w = w;
330 window_update_done( window );
332 table_insert( &windows, window->w, window );
334 return window;
337 extern void forget_window( struct gwm_window *window ) {
339 if( pointer_demux == window->w )
340 pointer_demux = XCB_NONE;
342 table_delete( &windows, window->w );
343 table_delete( &update_windows, window->w );
344 free( window );
347 extern struct gwm_window *lookup_window( xcb_window_t w ) {
349 struct gwm_window *window;
351 for(;;) {
352 window = table_lookup( &windows, w );
354 if( window && window->type == WINDOW_INCOMPLETE )
355 /* Whoops... all this asynchronicity has gotten us ahead
356 of ourselves. Catch up on what we deferred, by waiting
357 until the window is complete before we continue. */
358 sync_with_callback( window->u.incomplete.sequence );
359 else
360 return window;
364 struct window_stack window_stack;
366 extern INIT void stack_init( struct window_stack *stack ) {
368 stack->used = 0;
369 stack->n = 32;
370 stack->max = 24;
371 stack->hash = 0xBA97D5B1UL;
373 stack->t[ 0 ] = xcalloc( stack->n << 1, sizeof stack->t[ 0 ][ 0 ] );
374 stack->t[ 1 ] = stack->t[ 0 ] + stack->n;
377 #if DEBUG
378 extern void stack_destroy( struct window_stack *stack ) {
380 free( stack->t[ 0 ] );
382 #endif
384 extern PURE struct stacking_order *stack_lookup( struct window_stack *stack,
385 xcb_window_t key ) {
387 int h;
389 if( !key )
390 return NULL;
392 h = table_hash( stack->hash, stack->n, 0, key );
393 if( stack->t[ 0 ][ h ].window == key )
394 return &stack->t[ 0 ][ h ];
396 h = table_hash( stack->hash, stack->n, 1, key );
397 if( stack->t[ 1 ][ h ].window == key )
398 return &stack->t[ 1 ][ h ];
400 return NULL;
403 static void stack_insert( struct window_stack *stack, xcb_window_t window,
404 xcb_window_t lower_window,
405 xcb_window_t higher_window );
407 static void stack_rehash( struct window_stack *stack, unsigned long hash,
408 unsigned long n, unsigned long max ) {
410 int old_n = stack->n;
411 struct stacking_order *old_t[ 2 ];
412 int i;
414 old_t[ 0 ] = stack->t[ 0 ];
415 old_t[ 1 ] = stack->t[ 1 ];
417 stack->hash = hash;
418 stack->n = n;
419 stack->max = max;
420 stack->used = 0;
422 stack->t[ 0 ] = xcalloc( stack->n << 1, sizeof stack->t[ 0 ][ 0 ] );
423 stack->t[ 1 ] = stack->t[ 0 ] + stack->n;
425 /* We've now gotten everything we want out of the old stack. Although
426 stack_insert might possibly cause inefficient re-entrancy, that case
427 should be both harmless and exceedingly rare. */
428 for( i = 0; i < old_n; i++ ) {
429 if( old_t[ 0 ][ i ].window )
430 stack_insert( stack, old_t[ 0 ][ i ].window,
431 old_t[ 0 ][ i ].lower_window,
432 old_t[ 0 ][ i ].higher_window );
433 if( old_t[ 1 ][ i ].window )
434 stack_insert( stack, old_t[ 1 ][ i ].window,
435 old_t[ 1 ][ i ].lower_window,
436 old_t[ 1 ][ i ].higher_window );
439 free( old_t[ 0 ] );
442 static void stack_insert( struct window_stack *stack, xcb_window_t window,
443 xcb_window_t lower_window,
444 xcb_window_t higher_window ) {
446 stack->used++;
448 for(;;) {
449 int i;
451 for( i = 0; i < stack->max; i++ ) {
452 int h = table_hash( stack->hash, stack->n, i & 1, window );
453 xcb_window_t old_window = stack->t[ i & 1 ][ h ].window;
454 xcb_window_t old_lower, old_higher;
456 if( old_window ) {
457 old_lower = stack->t[ i & 1 ][ h ].lower_window;
458 old_higher = stack->t[ i & 1 ][ h ].higher_window;
461 stack->t[ i & 1 ][ h ].window = window;
462 stack->t[ i & 1 ][ h ].lower_window = lower_window;
463 stack->t[ i & 1 ][ h ].higher_window = higher_window;
465 if( !old_window )
466 return;
468 window = old_window;
469 lower_window = old_lower;
470 higher_window = old_higher;
473 /* Failure to insert: rehash. */
474 stack_rehash( stack, stack->hash + 0x5DB1BA96UL,
475 stack->n, stack->max );
479 static void stack_insert_new( struct window_stack *stack, xcb_window_t window,
480 xcb_window_t lower_window,
481 xcb_window_t higher_window ) {
483 if( !window )
484 return;
486 assert( !stack_lookup( stack, window ) );
488 if( stack->used > ( ( stack->n * 3 ) >> 2 ) )
489 /* Load becoming high: resize. */
490 stack_rehash( stack, stack->hash, stack->n << 1, stack->max + 4 );
492 stack_insert( stack, window, lower_window, higher_window );
495 extern void stack_insert_singleton( struct window_stack *stack,
496 xcb_window_t key ) {
498 stack_insert_new( stack, key, key, key );
501 extern void stack_insert_above( struct window_stack *stack,
502 xcb_window_t key, xcb_window_t lower_window ) {
504 struct stacking_order *l, *h;
505 xcb_window_t higher_window;
507 l = stack_lookup( stack, lower_window );
508 assert( l );
509 higher_window = l->higher_window;
511 stack_insert_new( stack, key, lower_window, higher_window );
512 /* It's essential to repeat the lookup of lower_window, because inserting
513 the new entry might have disturbed the existing ones. */
514 l = stack_lookup( stack, lower_window );
515 h = stack_lookup( stack, higher_window );
516 h->lower_window = key;
517 l->higher_window = key;
520 extern void stack_remove( struct window_stack *stack, xcb_window_t window ) {
522 struct stacking_order *l, *h, *entry;
523 int hash, i;
525 if( !window )
526 return;
528 entry = stack_lookup( stack, window );
529 assert( entry );
531 l = stack_lookup( stack, entry->lower_window );
532 assert( l );
534 h = stack_lookup( stack, entry->higher_window );
535 assert( h );
537 l->higher_window = entry->higher_window;
538 h->lower_window = entry->lower_window;
540 for( i = 0; i < 2; i++ ) {
541 hash = table_hash( stack->hash, stack->n, i, window );
542 if( stack->t[ i ][ hash ].window == window ) {
543 stack->t[ i ][ hash ].window = 0;
545 if( stack->used < ( ( stack->n * 3 ) >> 4 ) && stack->n > 32 )
546 /* Load becoming low: resize. */
547 stack_rehash( stack, stack->hash, stack->n >> 1,
548 stack->max - 4 );
549 return;
554 extern void stack_move_above( struct window_stack *stack,
555 xcb_window_t window,
556 xcb_window_t lower_window ) {
558 struct stacking_order *l, *h, *entry;
560 if( !window )
561 return;
563 entry = stack_lookup( stack, window );
564 assert( entry );
566 if( entry->lower_window == lower_window )
567 return;
569 l = stack_lookup( stack, entry->lower_window );
570 assert( l );
572 h = stack_lookup( stack, entry->higher_window );
573 assert( h );
575 l->higher_window = entry->higher_window;
576 h->lower_window = entry->lower_window;
578 l = stack_lookup( stack, lower_window );
579 assert( l );
581 h = stack_lookup( stack, l->higher_window );
582 assert( h );
584 h->lower_window = window;
585 l->higher_window = window;
586 entry->lower_window = lower_window;
587 entry->higher_window = h->window;
590 extern xcb_window_t stack_real_below( struct window_stack *stack,
591 xcb_window_t window ) {
593 xcb_window_t search;
595 if( !window )
596 return 0;
598 for( search = stack_lookup( stack, window )->lower_window;
599 search != window && ( search & STACK_MASK ) != STACK_END;
600 search = stack_lookup( stack, search )->lower_window )
601 if( !( search & STACK_MASK ) )
602 return search;
604 return 0;