[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / object.c
1 /*
2 * Server-side USER handles
3 *
4 * Copyright (C) 2001 Alexandre Julliard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21
22
23 /* INCLUDES ******************************************************************/
24
25 #include <win32k.h>
26
27 #define NDEBUG
28 #include <debug.h>
29
30 //int usedHandles=0;
31 PUSER_HANDLE_TABLE gHandleTable = NULL;
32
33
34 PUSER_HANDLE_ENTRY handle_to_entry(PUSER_HANDLE_TABLE ht, HANDLE handle )
35 {
36 unsigned short generation;
37 int index = (((unsigned int)handle & 0xffff) - FIRST_USER_HANDLE) >> 1;
38 if (index < 0 || index >= ht->nb_handles)
39 return NULL;
40 if (!ht->handles[index].type)
41 return NULL;
42 generation = (unsigned int)handle >> 16;
43 if (generation == ht->handles[index].generation || !generation || generation == 0xffff)
44 return &ht->handles[index];
45 return NULL;
46 }
47
48 __inline static HANDLE entry_to_handle(PUSER_HANDLE_TABLE ht, PUSER_HANDLE_ENTRY ptr )
49 {
50 int index = ptr - ht->handles;
51 return (HANDLE)(((index << 1) + FIRST_USER_HANDLE) + (ptr->generation << 16));
52 }
53
54 __inline static PUSER_HANDLE_ENTRY alloc_user_entry(PUSER_HANDLE_TABLE ht)
55 {
56 PUSER_HANDLE_ENTRY entry;
57
58 DPRINT("handles used %i\n",gpsi->cHandleEntries);
59
60 if (ht->freelist)
61 {
62 entry = ht->freelist;
63 ht->freelist = entry->ptr;
64
65 gpsi->cHandleEntries++;
66 return entry;
67 }
68
69 if (ht->nb_handles >= ht->allocated_handles) /* need to grow the array */
70 {
71 /**/
72 int i, iFree = 0, iWindow = 0, iMenu = 0, iCursorIcon = 0,
73 iHook = 0, iCallProc = 0, iAccel = 0, iMonitor = 0, iTimer = 0;
74 /**/
75 DPRINT1("Out of user handles! Used -> %i, NM_Handle -> %d\n", gpsi->cHandleEntries, ht->nb_handles);
76 //#if 0
77 for(i = 0; i < ht->nb_handles; i++)
78 {
79 switch (ht->handles[i].type)
80 {
81 case otFree: // Should be zero.
82 iFree++;
83 break;
84 case otWindow:
85 iWindow++;
86 break;
87 case otMenu:
88 iMenu++;
89 break;
90 case otCursorIcon:
91 iCursorIcon++;
92 break;
93 case otHook:
94 iHook++;
95 break;
96 case otCallProc:
97 iCallProc++;
98 break;
99 case otAccel:
100 iAccel++;
101 break;
102 case otMonitor:
103 iMonitor++;
104 break;
105 case otTimer:
106 iTimer++;
107 break;
108 default:
109 break;
110 }
111 }
112 DPRINT1("Handle Count by Type:\n Free = %d Window = %d Menu = %d CursorIcon = %d Hook = %d\n CallProc = %d Accel = %d Monitor = %d Timer = %d\n",
113 iFree, iWindow, iMenu, iCursorIcon, iHook, iCallProc, iAccel, iMonitor, iTimer );
114 //#endif
115 return NULL;
116 #if 0
117 PUSER_HANDLE_ENTRY new_handles;
118 /* grow array by 50% (but at minimum 32 entries) */
119 int growth = max( 32, ht->allocated_handles / 2 );
120 int new_size = min( ht->allocated_handles + growth, (LAST_USER_HANDLE-FIRST_USER_HANDLE+1) >> 1 );
121 if (new_size <= ht->allocated_handles)
122 return NULL;
123 if (!(new_handles = UserHeapReAlloc( ht->handles, new_size * sizeof(*ht->handles) )))
124 return NULL;
125 ht->handles = new_handles;
126 ht->allocated_handles = new_size;
127 #endif
128 }
129
130 entry = &ht->handles[ht->nb_handles++];
131
132 entry->generation = 1;
133
134 gpsi->cHandleEntries++;
135
136 return entry;
137 }
138
139 VOID UserInitHandleTable(PUSER_HANDLE_TABLE ht, PVOID mem, ULONG bytes)
140 {
141 ht->freelist = NULL;
142 ht->handles = mem;
143
144 ht->nb_handles = 0;
145 ht->allocated_handles = bytes / sizeof(USER_HANDLE_ENTRY);
146 }
147
148 __inline static void *free_user_entry(PUSER_HANDLE_TABLE ht, PUSER_HANDLE_ENTRY entry)
149 {
150 void *ret;
151 ret = entry->ptr;
152 entry->ptr = ht->freelist;
153 entry->type = 0;
154 entry->flags = 0;
155 entry->pi = NULL;
156 ht->freelist = entry;
157
158 gpsi->cHandleEntries--;
159
160 return ret;
161 }
162
163 static __inline PVOID
164 UserHandleOwnerByType(USER_OBJECT_TYPE type)
165 {
166 PVOID pi;
167
168 switch (type)
169 {
170 case otWindow:
171 case otInputContext:
172 pi = GetW32ThreadInfo();
173 break;
174
175 case otMenu:
176 case otCursorIcon:
177 case otHook:
178 case otCallProc:
179 case otAccel:
180 pi = GetW32ProcessInfo();
181 break;
182
183 case otMonitor:
184 pi = NULL; /* System */
185 break;
186
187 default:
188 pi = NULL;
189 break;
190 }
191
192 return pi;
193 }
194
195 /* allocate a user handle for a given object */
196 HANDLE UserAllocHandle(PUSER_HANDLE_TABLE ht, PVOID object, USER_OBJECT_TYPE type )
197 {
198 PUSER_HANDLE_ENTRY entry = alloc_user_entry(ht);
199 if (!entry)
200 return 0;
201 entry->ptr = object;
202 entry->type = type;
203 entry->flags = 0;
204 entry->pi = UserHandleOwnerByType(type);
205 if (++entry->generation >= 0xffff)
206 entry->generation = 1;
207
208 /* We have created a handle, which is a reference! */
209 UserReferenceObject(object);
210
211 return entry_to_handle(ht, entry );
212 }
213
214 /* return a pointer to a user object from its handle */
215 PVOID UserGetObject(PUSER_HANDLE_TABLE ht, HANDLE handle, USER_OBJECT_TYPE type )
216 {
217 PUSER_HANDLE_ENTRY entry;
218
219 ASSERT(ht);
220
221 if (!(entry = handle_to_entry(ht, handle )) || entry->type != type)
222 {
223 EngSetLastError(ERROR_INVALID_HANDLE);
224 return NULL;
225 }
226 return entry->ptr;
227 }
228
229
230 /* get the full handle (32bit) for a possibly truncated (16bit) handle */
231 HANDLE get_user_full_handle(PUSER_HANDLE_TABLE ht, HANDLE handle )
232 {
233 PUSER_HANDLE_ENTRY entry;
234
235 if ((unsigned int)handle >> 16)
236 return handle;
237 if (!(entry = handle_to_entry(ht, handle )))
238 return handle;
239 return entry_to_handle( ht, entry );
240 }
241
242
243 /* same as get_user_object plus set the handle to the full 32-bit value */
244 void *get_user_object_handle(PUSER_HANDLE_TABLE ht, HANDLE* handle, USER_OBJECT_TYPE type )
245 {
246 PUSER_HANDLE_ENTRY entry;
247
248 if (!(entry = handle_to_entry(ht, *handle )) || entry->type != type)
249 return NULL;
250 *handle = entry_to_handle( ht, entry );
251 return entry->ptr;
252 }
253
254 /* return the next user handle after 'handle' that is of a given type */
255 PVOID UserGetNextHandle(PUSER_HANDLE_TABLE ht, HANDLE* handle, USER_OBJECT_TYPE type )
256 {
257 PUSER_HANDLE_ENTRY entry;
258
259 if (!*handle)
260 entry = ht->handles;
261 else
262 {
263 int index = (((unsigned int)*handle & 0xffff) - FIRST_USER_HANDLE) >> 1;
264 if (index < 0 || index >= ht->nb_handles)
265 return NULL;
266 entry = ht->handles + index + 1; /* start from the next one */
267 }
268 while (entry < ht->handles + ht->nb_handles)
269 {
270 if (!type || entry->type == type)
271 {
272 *handle = entry_to_handle(ht, entry );
273 return entry->ptr;
274 }
275 entry++;
276 }
277 return NULL;
278 }
279
280 BOOL FASTCALL UserCreateHandleTable(VOID)
281 {
282
283 PVOID mem;
284
285 //FIXME: dont alloc all at once! must be mapped into umode also...
286 mem = UserHeapAlloc(sizeof(USER_HANDLE_ENTRY) * 1024*2);
287 if (!mem)
288 {
289 DPRINT1("Failed creating handle table\n");
290 return FALSE;
291 }
292
293 gHandleTable = UserHeapAlloc(sizeof(USER_HANDLE_TABLE));
294 if (gHandleTable == NULL)
295 {
296 UserHeapFree(mem);
297 DPRINT1("Failed creating handle table\n");
298 return FALSE;
299 }
300
301 //FIXME: make auto growable
302 UserInitHandleTable(gHandleTable, mem, sizeof(USER_HANDLE_ENTRY) * 1024*2);
303
304 return TRUE;
305 }
306
307 //
308 // New
309 //
310 PVOID
311 FASTCALL
312 UserCreateObject( PUSER_HANDLE_TABLE ht,
313 PDESKTOP pDesktop,
314 HANDLE* h,
315 USER_OBJECT_TYPE type,
316 ULONG size)
317 {
318 HANDLE hi;
319 PVOID Object;
320 PTHREADINFO pti;
321 PPROCESSINFO ppi;
322 BOOL dt;
323 PDESKTOP rpdesk = pDesktop;
324
325 pti = GetW32ThreadInfo();
326 ppi = pti->ppi;
327 if (!pDesktop) rpdesk = pti->rpdesk;
328
329 switch (type)
330 {
331 case otWindow:
332 // case otMenu:
333 case otHook:
334 case otCallProc:
335 case otInputContext:
336 Object = DesktopHeapAlloc(rpdesk, size);
337 dt = TRUE;
338 break;
339
340 default:
341 Object = UserHeapAlloc(size);
342 dt = FALSE;
343 break;
344 }
345
346 if (!Object)
347 return NULL;
348
349
350 hi = UserAllocHandle(ht, Object, type );
351 if (!hi)
352 {
353 if (dt)
354 DesktopHeapFree(rpdesk, Object);
355 else
356 UserHeapFree(Object);
357 return NULL;
358 }
359
360 RtlZeroMemory(Object, size);
361
362 switch (type)
363 {
364 case otWindow:
365 case otHook:
366 case otInputContext:
367 ((PTHRDESKHEAD)Object)->rpdesk = rpdesk;
368 ((PTHRDESKHEAD)Object)->pSelf = Object;
369 case otEvent:
370 ((PTHROBJHEAD)Object)->pti = pti;
371 break;
372
373 case otMenu:
374 case otCallProc:
375 ((PPROCDESKHEAD)Object)->rpdesk = rpdesk;
376 ((PPROCDESKHEAD)Object)->pSelf = Object;
377 break;
378
379 case otCursorIcon:
380 ((PPROCMARKHEAD)Object)->ppi = ppi;
381 break;
382
383 default:
384 break;
385 }
386 /* Now set default headers. */
387 ((PHEAD)Object)->h = hi;
388 ((PHEAD)Object)->cLockObj = 2; // we need this, because we create 2 refs: handle and pointer!
389
390 if (h)
391 *h = hi;
392 return Object;
393 }
394
395
396 BOOL
397 FASTCALL
398 UserDereferenceObject(PVOID object)
399 {
400 PUSER_HANDLE_ENTRY entry;
401 USER_OBJECT_TYPE type;
402
403 ASSERT(((PHEAD)object)->cLockObj >= 1);
404
405 if ((INT)--((PHEAD)object)->cLockObj <= 0)
406 {
407 entry = handle_to_entry(gHandleTable, ((PHEAD)object)->h );
408
409 DPRINT("warning! Dereference to zero! Obj -> 0x%x\n", object);
410
411 ((PHEAD)object)->cLockObj = 0;
412
413 if (!(entry->flags & HANDLEENTRY_INDESTROY))
414 return TRUE;
415
416 type = entry->type;
417 free_user_entry(gHandleTable, entry );
418
419 switch (type)
420 {
421 case otWindow:
422 // case otMenu:
423 case otHook:
424 case otCallProc:
425 case otInputContext:
426 return DesktopHeapFree(((PTHRDESKHEAD)object)->rpdesk, object);
427
428 default:
429 return UserHeapFree(object);
430 }
431 }
432 return FALSE;
433 }
434
435 BOOL
436 FASTCALL
437 UserFreeHandle(PUSER_HANDLE_TABLE ht, HANDLE handle )
438 {
439 PUSER_HANDLE_ENTRY entry;
440
441 if (!(entry = handle_to_entry( ht, handle )))
442 {
443 SetLastNtError( STATUS_INVALID_HANDLE );
444 return FALSE;
445 }
446
447 entry->flags = HANDLEENTRY_INDESTROY;
448
449 return UserDereferenceObject(entry->ptr);
450 }
451
452 BOOL
453 FASTCALL
454 UserDeleteObject(HANDLE h, USER_OBJECT_TYPE type )
455 {
456 PVOID body = UserGetObject(gHandleTable, h, type);
457
458 if (!body) return FALSE;
459
460 ASSERT( ((PHEAD)body)->cLockObj >= 1);
461
462 return UserFreeHandle(gHandleTable, h);
463 }
464
465 VOID
466 FASTCALL
467 UserReferenceObject(PVOID obj)
468 {
469 ASSERT(((PHEAD)obj)->cLockObj >= 0);
470
471 ((PHEAD)obj)->cLockObj++;
472 }
473
474 PVOID
475 FASTCALL
476 UserReferenceObjectByHandle(HANDLE handle, USER_OBJECT_TYPE type)
477 {
478 PVOID object;
479
480 object = UserGetObject(gHandleTable, handle, type);
481 if (object)
482 {
483 UserReferenceObject(object);
484 }
485 return object;
486 }