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/>.
32 #include "window-table.h"
34 /* Cuckoo hashed window table: see _Cuckoo Hashing_, R. Pagh and F. Rodler,
35 Journal of Algorithms 51 (2004), pp. 122-144. */
37 struct window_table windows
, update_windows
;
39 extern void table_init( struct window_table
*table
) {
44 table
->hash
= 0xBA97D5B1UL
;
46 table
->t
[ 0 ] = xcalloc( table
->n
<< 1, sizeof table
->t
[ 0 ][ 0 ] );
47 table
->t
[ 1 ] = table
->t
[ 0 ] + table
->n
;
48 table
->values
= xmalloc( table
->n
* sizeof *table
->values
);
52 extern void table_destroy( struct window_table
*table
) {
54 free( table
->t
[ 0 ] );
55 free( table
->values
);
59 static CONST
int table_hash( struct window_table
*table
, int half
,
66 h0
= table
->hash
^ 0x79D9C6D4UL
;
67 h1
= table
->hash
* 0x6A391E85UL
;
70 h1
= table
->hash
^ 0x52C694B6UL
;
78 return l
& ( table
->n
- 1 );
81 static PURE
int table_lookup_index( struct window_table
*table
,
88 h
= table_hash( table
, 0, key
);
89 if( table
->t
[ 0 ][ h
].key
== key
)
90 return table
->t
[ 0 ][ h
].index
;
92 h
= table_hash( table
, 1, key
);
93 if( table
->t
[ 1 ][ h
].key
== key
)
94 return table
->t
[ 1 ][ h
].index
;
99 extern PURE
struct gwm_window
*table_lookup( struct window_table
*table
,
107 return ( index
= table_lookup_index( table
, key
) ) >= 0 ?
108 table
->values
[ index
] : NULL
;
111 static void table_insert_index( struct window_table
*table
, xcb_window_t key
,
114 static void table_rehash( struct window_table
*table
, unsigned long hash
,
115 unsigned long n
, unsigned long max
) {
117 int old_n
= table
->n
;
118 struct half_table
*old_t
[ 2 ];
121 old_t
[ 0 ] = table
->t
[ 0 ];
122 old_t
[ 1 ] = table
->t
[ 1 ];
129 table
->t
[ 0 ] = xcalloc( table
->n
<< 1, sizeof table
->t
[ 0 ][ 0 ] );
130 table
->t
[ 1 ] = table
->t
[ 0 ] + table
->n
;
131 table
->values
= xrealloc( table
->values
, table
->n
* sizeof *table
->values
);
133 /* We've now gotten everything we want out of the old table. Although
134 table_insert might possibly cause inefficient re-entrancy, that case
135 should be both harmless and exceedingly rare. */
136 for( i
= 0; i
< old_n
; i
++ ) {
137 if( old_t
[ 0 ][ i
].key
)
138 table_insert_index( table
, old_t
[ 0 ][ i
].key
,
139 old_t
[ 0 ][ i
].index
);
140 if( old_t
[ 1 ][ i
].key
)
141 table_insert_index( table
, old_t
[ 1 ][ i
].key
,
142 old_t
[ 1 ][ i
].index
);
148 static void table_insert_index( struct window_table
*table
, xcb_window_t key
,
156 for( i
= 0; i
< table
->max
; i
++ ) {
157 int h
= table_hash( table
, i
& 1, key
);
158 xcb_window_t old_key
= table
->t
[ i
& 1 ][ h
].key
;
159 int old_index
= table
->t
[ i
& 1 ][ h
].index
;
161 table
->t
[ i
& 1 ][ h
].key
= key
;
162 table
->t
[ i
& 1 ][ h
].index
= index
;
171 /* Failure to insert: rehash. */
172 table_rehash( table
, table
->hash
+ 0x5DB1BA96UL
,
173 table
->n
, table
->max
);
177 static void table_insert( struct window_table
*table
, xcb_window_t key
,
178 struct gwm_window
*value
) {
183 assert( !table_lookup( table
, key
) );
185 table
->values
[ table
->used
] = value
;
187 if( table
->used
> ( ( table
->n
* 3 ) >> 2 ) )
188 /* Load becoming high: resize. */
189 table_rehash( table
, table
->hash
, table
->n
<< 1, table
->max
+ 4 );
191 table_insert_index( table
, key
, table
->used
);
194 static void table_delete( struct window_table
*table
, xcb_window_t key
) {
202 for( i
= 0; i
< 2; i
++ ) {
203 h
= table_hash( table
, i
, key
);
204 if( table
->t
[ i
][ h
].key
== key
) {
206 xcb_window_t replacement_key
;
207 struct gwm_window
*replacement_value
;
208 int replacement_hash
;
212 if( ( index
= table
->t
[ i
][ h
].index
) < table
->used
) {
213 /* Must replace our old index, to keep the values array
215 table
->values
[ index
] = replacement_value
=
216 table
->values
[ table
->used
];
217 replacement_key
= replacement_value
->w
;
219 replacement_hash
= table_hash( table
, 0, replacement_key
);
220 if( table
->t
[ 0 ][ replacement_hash
].key
== replacement_key
)
221 table
->t
[ 0 ][ replacement_hash
].index
= index
;
223 replacement_hash
= table_hash( table
, 1, replacement_key
);
224 assert( table
->t
[ 1 ][ replacement_hash
].key
==
226 table
->t
[ 1 ][ replacement_hash
].index
= index
;
230 table
->t
[ i
][ h
].key
= 0;
232 if( table
->used
< ( ( table
->n
* 3 ) >> 4 ) && table
->n
> 32 )
233 /* Load becoming low: resize. */
234 table_rehash( table
, table
->hash
, table
->n
>> 1,
241 extern void queue_window_update( struct gwm_window
*window
,
242 int x
, int y
, int width
, int height
,
245 /* FIXME Consider computing the region instead of the bounding box. */
247 if( !window
->update
.width
|| !window
->update
.height
) {
248 /* New update region. */
249 window
->update
.x
= x
;
250 window
->update
.y
= y
;
251 window
->update
.width
= width
;
252 window
->update
.height
= height
;
254 /* Compute the bounding box of the union of the old and new regions. */
255 if( x
< window
->update
.x
) {
256 window
->update
.width
+= window
->update
.x
- x
;
257 window
->update
.x
= x
;
260 if( y
< window
->update
.y
) {
261 window
->update
.height
+= window
->update
.y
- y
;
262 window
->update
.y
= y
;
265 if( x
+ width
> window
->update
.x
+ window
->update
.width
)
266 window
->update
.width
= x
+ width
- window
->update
.x
;
268 if( y
+ height
> window
->update
.y
+ window
->update
.height
)
269 window
->update
.height
= y
+ height
- window
->update
.y
;
273 window
->cleared
= FALSE
;
275 if( table_lookup( &update_windows
, window
->w
) )
278 table_insert( &update_windows
, window
->w
, window
);
281 extern void window_update_done( struct gwm_window
*window
) {
283 window
->update
.x
= 0;
284 window
->update
.y
= 0;
285 window
->update
.width
= 0;
286 window
->update
.height
= 0;
287 window
->cleared
= TRUE
;
289 table_delete( &update_windows
, window
->w
);
292 extern MALLOC
struct gwm_window
*add_window( xcb_window_t w
) {
294 struct gwm_window
*window
;
296 if( table_lookup( &windows
, w
) )
297 return NULL
; /* window already exists */
299 window
= xmalloc( sizeof *window
);
301 window_update_done( window
);
303 table_insert( &windows
, window
->w
, window
);
308 extern void forget_window( struct gwm_window
*window
) {
310 table_delete( &windows
, window
->w
);
311 table_delete( &update_windows
, window
->w
);
315 extern struct gwm_window
*lookup_window( xcb_window_t w
) {
317 struct gwm_window
*window
;
320 window
= table_lookup( &windows
, w
);
322 if( window
&& window
->type
== WINDOW_INCOMPLETE
)
323 /* Whoops... all this asynchronicity has gotten us ahead
324 of ourselves. Catch up on what we deferred, by waiting
325 until the window is complete before we continue. */
326 sync_with_callback( window
->u
.incomplete
.sequence
);