winex11: Track async GL drawable changes.
[wine] / server / atom.c
1 /*
2  * Server-side atom management
3  *
4  * Copyright (C) 1999, 2000 Alexandre Julliard
5  * Copyright (C) 2000 Turchanov Sergei
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32
33 #include "unicode.h"
34 #include "request.h"
35 #include "object.h"
36 #include "process.h"
37 #include "handle.h"
38 #include "user.h"
39 #include "winuser.h"
40 #include "winternl.h"
41
42 #define HASH_SIZE     37
43 #define MIN_HASH_SIZE 4
44 #define MAX_HASH_SIZE 0x200
45
46 #define MAX_ATOM_LEN  255
47 #define MIN_STR_ATOM  0xc000
48 #define MAX_ATOMS     0x4000
49
50 struct atom_entry
51 {
52     struct atom_entry *next;   /* hash table list */
53     struct atom_entry *prev;   /* hash table list */
54     int                count;  /* reference count */
55     short              pinned; /* whether the atom is pinned or not */
56     atom_t             atom;   /* atom handle */
57     unsigned short     hash;   /* string hash */
58     unsigned short     len;    /* string len */
59     WCHAR              str[1]; /* atom string */
60 };
61
62 struct atom_table
63 {
64     struct object       obj;                 /* object header */
65     int                 count;               /* count of atom handles */
66     int                 last;                /* last handle in-use */
67     struct atom_entry **handles;             /* atom handles */
68     int                 entries_count;       /* humber of hash entries */
69     struct atom_entry **entries;             /* hash table entries */
70 };
71
72 static void atom_table_dump( struct object *obj, int verbose );
73 static void atom_table_destroy( struct object *obj );
74
75 static const struct object_ops atom_table_ops =
76 {
77     sizeof(struct atom_table),    /* size */
78     atom_table_dump,              /* dump */
79     no_add_queue,                 /* add_queue */
80     NULL,                         /* remove_queue */
81     NULL,                         /* signaled */
82     NULL,                         /* satisfied */
83     no_signal,                    /* signal */
84     no_get_fd,                    /* get_fd */
85     no_map_access,                /* map_access */
86     no_lookup_name,               /* lookup_name */
87     no_open_file,                 /* open_file */
88     no_close_handle,              /* close_handle */
89     atom_table_destroy            /* destroy */
90 };
91
92 static struct atom_table *global_table;
93
94 /* create an atom table */
95 static struct atom_table *create_table(int entries_count)
96 {
97     struct atom_table *table;
98
99     if ((table = alloc_object( &atom_table_ops )))
100     {
101         if ((entries_count < MIN_HASH_SIZE) ||
102             (entries_count > MAX_HASH_SIZE)) entries_count = HASH_SIZE;
103         table->entries_count = entries_count;
104         if (!(table->entries = malloc( sizeof(*table->entries) * table->entries_count )))
105         {
106             set_error( STATUS_NO_MEMORY );
107             goto fail;
108         }
109         memset( table->entries, 0, sizeof(*table->entries) * table->entries_count );
110         table->count = 64;
111         table->last  = -1;
112         if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count )))
113             return table;
114 fail:
115         release_object( table );
116         table = NULL;
117     }
118     return table;
119 }
120
121 /* retrieve an entry pointer from its atom */
122 static struct atom_entry *get_atom_entry( struct atom_table *table, atom_t atom )
123 {
124     struct atom_entry *entry = NULL;
125     if (table && (atom >= MIN_STR_ATOM) && (atom <= MIN_STR_ATOM + table->last))
126         entry = table->handles[atom - MIN_STR_ATOM];
127     if (!entry) set_error( STATUS_INVALID_HANDLE );
128     return entry;
129 }
130
131 /* add an atom entry in the table and return its handle */
132 static atom_t add_atom_entry( struct atom_table *table, struct atom_entry *entry )
133 {
134     int i;
135     for (i = 0; i <= table->last; i++)
136         if (!table->handles[i]) goto found;
137     if (i == table->count)
138     {
139         struct atom_entry **new_table = NULL;
140         int new_size = table->count + table->count / 2;
141         if (new_size > MAX_ATOMS) new_size = MAX_ATOMS;
142         if (new_size > table->count)
143             new_table = realloc( table->handles, sizeof(*table->handles) * new_size );
144         if (!new_table)
145         {
146             set_error( STATUS_NO_MEMORY );
147             return 0;
148         }
149         table->count = new_size;
150         table->handles = new_table;
151     }
152     table->last = i;
153  found:
154     table->handles[i] = entry;
155     entry->atom = i + MIN_STR_ATOM;
156     return entry->atom;
157 }
158
159 /* compute the hash code for a string */
160 static unsigned short atom_hash( struct atom_table *table, const WCHAR *str, data_size_t len )
161 {
162     unsigned int i;
163     unsigned short hash = 0;
164     for (i = 0; i < len; i++) hash ^= toupperW(str[i]) + i;
165     return hash % table->entries_count;
166 }
167
168 /* dump an atom table */
169 static void atom_table_dump( struct object *obj, int verbose )
170 {
171     int i;
172     struct atom_table *table = (struct atom_table *)obj;
173     assert( obj->ops == &atom_table_ops );
174
175     fprintf( stderr, "Atom table size=%d entries=%d\n",
176              table->last + 1, table->entries_count );
177     if (!verbose) return;
178     for (i = 0; i <= table->last; i++)
179     {
180         struct atom_entry *entry = table->handles[i];
181         if (!entry) continue;
182         fprintf( stderr, "  %04x: ref=%d pinned=%c hash=%d \"",
183                  entry->atom, entry->count, entry->pinned ? 'Y' : 'N', entry->hash );
184         dump_strW( entry->str, entry->len, stderr, "\"\"");
185         fprintf( stderr, "\"\n" );
186     }
187 }
188
189 /* destroy the atom table */
190 static void atom_table_destroy( struct object *obj )
191 {
192     int i;
193     struct atom_table *table = (struct atom_table *)obj;
194     assert( obj->ops == &atom_table_ops );
195     if (table->handles)
196     {
197         for (i = 0; i <= table->last; i++) free( table->handles[i] );
198         free( table->handles );
199     }
200     free( table->entries );
201 }
202
203 /* find an atom entry in its hash list */
204 static struct atom_entry *find_atom_entry( struct atom_table *table, const WCHAR *str,
205                                            data_size_t len, unsigned short hash )
206 {
207     struct atom_entry *entry = table->entries[hash];
208     while (entry)
209     {
210         if (entry->len == len && !memicmpW( entry->str, str, len )) break;
211         entry = entry->next;
212     }
213     return entry;
214 }
215
216 /* add an atom to the table */
217 static atom_t add_atom( struct atom_table *table, const WCHAR *str, data_size_t len )
218 {
219     struct atom_entry *entry;
220     unsigned short hash = atom_hash( table, str, len );
221     atom_t atom = 0;
222
223     if (!len)
224     {
225         set_error( STATUS_OBJECT_NAME_INVALID );
226         return 0;
227     }
228     if (len > MAX_ATOM_LEN)
229     {
230         set_error( STATUS_INVALID_PARAMETER );
231         return 0;
232     }
233     if ((entry = find_atom_entry( table, str, len, hash )))  /* exists already */
234     {
235         entry->count++;
236         return entry->atom;
237     }
238
239     if ((entry = mem_alloc( sizeof(*entry) + (len - 1) * sizeof(WCHAR) )))
240     {
241         if ((atom = add_atom_entry( table, entry )))
242         {
243             entry->prev  = NULL;
244             if ((entry->next = table->entries[hash])) entry->next->prev = entry;
245             table->entries[hash] = entry;
246             entry->count  = 1;
247             entry->pinned = 0;
248             entry->hash   = hash;
249             entry->len    = len;
250             memcpy( entry->str, str, len * sizeof(WCHAR) );
251         }
252         else free( entry );
253     }
254     else set_error( STATUS_NO_MEMORY );
255     return atom;
256 }
257
258 /* delete an atom from the table */
259 static void delete_atom( struct atom_table *table, atom_t atom, int if_pinned )
260 {
261     struct atom_entry *entry = get_atom_entry( table, atom );
262     if (!entry) return;
263     if (entry->pinned && !if_pinned) set_error( STATUS_WAS_LOCKED );
264     else if (!--entry->count)
265     {
266         if (entry->next) entry->next->prev = entry->prev;
267         if (entry->prev) entry->prev->next = entry->next;
268         else table->entries[entry->hash] = entry->next;
269         table->handles[atom - MIN_STR_ATOM] = NULL;
270         free( entry );
271     }
272 }
273
274 /* find an atom in the table */
275 static atom_t find_atom( struct atom_table *table, const WCHAR *str, data_size_t len )
276 {
277     struct atom_entry *entry;
278
279     if (!len)
280     {
281         set_error( STATUS_OBJECT_NAME_INVALID );
282         return 0;
283     }
284     if (len > MAX_ATOM_LEN)
285     {
286         set_error( STATUS_INVALID_PARAMETER );
287         return 0;
288     }
289     if (table && (entry = find_atom_entry( table, str, len, atom_hash(table, str, len) )))
290         return entry->atom;
291     set_error( STATUS_OBJECT_NAME_NOT_FOUND );
292     return 0;
293 }
294
295 static struct atom_table *get_global_table( struct winstation *winstation, int create )
296 {
297     struct atom_table *table = winstation ? winstation->atom_table : global_table;
298     if (!table)
299     {
300         if (create)
301         {
302             table = create_table( HASH_SIZE );
303             if (winstation) winstation->atom_table = table;
304             else
305             {
306                 global_table = table;
307                 make_object_static( &global_table->obj );
308             }
309         }
310         else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
311     }
312     return table;
313 }
314
315 static struct atom_table *get_table( obj_handle_t h, int create )
316 {
317     struct atom_table *table = NULL;
318
319     if (h)
320     {
321         table = (struct atom_table *)get_handle_obj( current->process, h, 0, &atom_table_ops );
322     }
323     else
324     {
325         table = get_global_table( NULL, 1 );
326         if (table) grab_object( table );
327     }
328     return table;
329 }
330
331 /* add an atom in the global table; used for window properties */
332 atom_t add_global_atom( struct winstation *winstation, const WCHAR *str, data_size_t len )
333 {
334     struct atom_table *global_table = get_global_table( winstation, 1 );
335     if (!global_table) return 0;
336     return add_atom( global_table, str, len );
337 }
338
339 /* find an atom in the global table; used for window properties */
340 atom_t find_global_atom( struct winstation *winstation, const WCHAR *str, data_size_t len )
341 {
342     struct atom_table *global_table = get_global_table( winstation, 0 );
343     struct atom_entry *entry;
344
345     if (!len || len > MAX_ATOM_LEN || !global_table) return 0;
346     if ((entry = find_atom_entry( global_table, str, len, atom_hash(global_table, str, len) )))
347         return entry->atom;
348     return 0;
349 }
350
351 /* increment the ref count of a global atom; used for window properties */
352 int grab_global_atom( struct winstation *winstation, atom_t atom )
353 {
354     if (atom >= MIN_STR_ATOM)
355     {
356         struct atom_table *global_table = get_global_table( winstation, 0 );
357         if (global_table)
358         {
359             struct atom_entry *entry = get_atom_entry( global_table, atom );
360             if (entry) entry->count++;
361             return (entry != NULL);
362         }
363         else return 0;
364     }
365     else return 1;
366 }
367
368 /* decrement the ref count of a global atom; used for window properties */
369 void release_global_atom( struct winstation *winstation, atom_t atom )
370 {
371     if (atom >= MIN_STR_ATOM)
372     {
373         struct atom_table *global_table = get_global_table( winstation, 0 );
374         if (global_table) delete_atom( global_table, atom, 1 );
375     }
376 }
377
378 /* add a global atom */
379 DECL_HANDLER(add_atom)
380 {
381     struct atom_table *table = get_table( req->table, 1 );
382     if (table)
383     {
384         reply->atom = add_atom( table, get_req_data(), get_req_data_size() / sizeof(WCHAR) );
385         release_object( table );
386     }
387 }
388
389 /* delete a global atom */
390 DECL_HANDLER(delete_atom)
391 {
392     struct atom_table *table = get_table( req->table, 0 );
393     if (table)
394     {
395         delete_atom( table, req->atom, 0 );
396         release_object( table );
397     }
398 }
399
400 /* find a global atom */
401 DECL_HANDLER(find_atom)
402 {
403     struct atom_table *table = get_table( req->table, 0 );
404     if (table)
405     {
406         reply->atom = find_atom( table, get_req_data(), get_req_data_size() / sizeof(WCHAR) );
407         release_object( table );
408     }
409 }
410
411 /* get global atom name */
412 DECL_HANDLER(get_atom_information)
413 {
414     struct atom_table *table = get_table( req->table, 0 );
415     if (table)
416     {
417         struct atom_entry *entry;
418
419         if ((entry = get_atom_entry( table, req->atom )))
420         {
421             data_size_t len = entry->len * sizeof(WCHAR);
422             if (get_reply_max_size())
423                 set_reply_data( entry->str, min( len, get_reply_max_size()));
424             reply->count = entry->count;
425             reply->pinned = entry->pinned;
426             reply->total = len;
427         }
428         else reply->count = -1;
429         release_object( table );
430     }
431 }
432
433 /* set global atom name */
434 DECL_HANDLER(set_atom_information)
435 {
436     struct atom_table *table = get_table( req->table, 0 );
437     if (table)
438     {
439         struct atom_entry *entry;
440
441         if ((entry = get_atom_entry( table, req->atom )))
442         {
443             if (req->pinned) entry->pinned = 1;
444         }
445         release_object( table );
446     }
447 }
448
449 /* init a (local) atom table */
450 DECL_HANDLER(init_atom_table)
451 {
452     struct atom_table* table = create_table( req->entries );
453
454     if (table)
455     {
456         reply->table = alloc_handle( current->process, table, 0, 0 );
457         release_object( table );
458     }
459 }
460
461 /* set global atom name */
462 DECL_HANDLER(empty_atom_table)
463 {
464     struct atom_table *table = get_table( req->table, 1 );
465     if (table)
466     {
467         int i;
468         struct atom_entry *entry;
469
470         for (i = 0; i <= table->last; i++)
471         {
472             entry = table->handles[i];
473             if (entry && (!entry->pinned || req->if_pinned))
474             {
475                 if (entry->next) entry->next->prev = entry->prev;
476                 if (entry->prev) entry->prev->next = entry->next;
477                 else table->entries[entry->hash] = entry->next;
478                 table->handles[i] = NULL;
479                 free( entry );
480             }
481         }
482         release_object( table );
483     }
484 }