[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 case otSMWP:
181 pi = GetW32ProcessInfo();
182 break;
183
184 case otMonitor:
185 pi = NULL; /* System */
186 break;
187
188 default:
189 pi = NULL;
190 break;
191 }
192
193 return pi;
194 }
195
196 /* allocate a user handle for a given object */
197 HANDLE UserAllocHandle(PUSER_HANDLE_TABLE ht, PVOID object, USER_OBJECT_TYPE type )
198 {
199 PUSER_HANDLE_ENTRY entry = alloc_user_entry(ht);
200 if (!entry)
201 return 0;
202 entry->ptr = object;
203 entry->type = type;
204 entry->flags = 0;
205 entry->pi = UserHandleOwnerByType(type);
206 if (++entry->generation >= 0xffff)
207 entry->generation = 1;
208
209 /* We have created a handle, which is a reference! */
210 UserReferenceObject(object);
211
212 return entry_to_handle(ht, entry );
213 }
214
215 /* return a pointer to a user object from its handle */
216 PVOID UserGetObject(PUSER_HANDLE_TABLE ht, HANDLE handle, USER_OBJECT_TYPE type )
217 {
218 PUSER_HANDLE_ENTRY entry;
219
220 ASSERT(ht);
221
222 if (!(entry = handle_to_entry(ht, handle )) || entry->type != type)
223 {
224 EngSetLastError(ERROR_INVALID_HANDLE);
225 return NULL;
226 }
227 return entry->ptr;
228 }
229
230
231 /* get the full handle (32bit) for a possibly truncated (16bit) handle */
232 HANDLE get_user_full_handle(PUSER_HANDLE_TABLE ht, HANDLE handle )
233 {
234 PUSER_HANDLE_ENTRY entry;
235
236 if ((unsigned int)handle >> 16)
237 return handle;
238 if (!(entry = handle_to_entry(ht, handle )))
239 return handle;
240 return entry_to_handle( ht, entry );
241 }
242
243
244 /* same as get_user_object plus set the handle to the full 32-bit value */
245 void *get_user_object_handle(PUSER_HANDLE_TABLE ht, HANDLE* handle, USER_OBJECT_TYPE type )
246 {
247 PUSER_HANDLE_ENTRY entry;
248
249 if (!(entry = handle_to_entry(ht, *handle )) || entry->type != type)
250 return NULL;
251 *handle = entry_to_handle( ht, entry );
252 return entry->ptr;
253 }
254
255 /* return the next user handle after 'handle' that is of a given type */
256 PVOID UserGetNextHandle(PUSER_HANDLE_TABLE ht, HANDLE* handle, USER_OBJECT_TYPE type )
257 {
258 PUSER_HANDLE_ENTRY entry;
259
260 if (!*handle)
261 entry = ht->handles;
262 else
263 {
264 int index = (((unsigned int)*handle & 0xffff) - FIRST_USER_HANDLE) >> 1;
265 if (index < 0 || index >= ht->nb_handles)
266 return NULL;
267 entry = ht->handles + index + 1; /* start from the next one */
268 }
269 while (entry < ht->handles + ht->nb_handles)
270 {
271 if (!type || entry->type == type)
272 {
273 *handle = entry_to_handle(ht, entry );
274 return entry->ptr;
275 }
276 entry++;
277 }
278 return NULL;
279 }
280
281 BOOL FASTCALL UserCreateHandleTable(VOID)
282 {
283
284 PVOID mem;
285
286 //FIXME: dont alloc all at once! must be mapped into umode also...
287 mem = UserHeapAlloc(sizeof(USER_HANDLE_ENTRY) * 1024*2);
288 if (!mem)
289 {
290 DPRINT1("Failed creating handle table\n");
291 return FALSE;
292 }
293
294 gHandleTable = UserHeapAlloc(sizeof(USER_HANDLE_TABLE));
295 if (gHandleTable == NULL)
296 {
297 UserHeapFree(mem);
298 DPRINT1("Failed creating handle table\n");
299 return FALSE;
300 }
301
302 //FIXME: make auto growable
303 UserInitHandleTable(gHandleTable, mem, sizeof(USER_HANDLE_ENTRY) * 1024*2);
304
305 return TRUE;
306 }
307
308 //
309 // New
310 //
311 PVOID
312 FASTCALL
313 UserCreateObject( PUSER_HANDLE_TABLE ht,
314 PDESKTOP pDesktop,
315 HANDLE* h,
316 USER_OBJECT_TYPE type,
317 ULONG size)
318 {
319 HANDLE hi;
320 PVOID Object;
321 PTHREADINFO pti;
322 PPROCESSINFO ppi;
323 BOOL dt;
324 PDESKTOP rpdesk = pDesktop;
325
326 pti = GetW32ThreadInfo();
327 ppi = pti->ppi;
328 if (!pDesktop) rpdesk = pti->rpdesk;
329
330 switch (type)
331 {
332 case otWindow:
333 // case otMenu:
334 case otHook:
335 case otCallProc:
336 case otInputContext:
337 Object = DesktopHeapAlloc(rpdesk, size);
338 dt = TRUE;
339 break;
340
341 default:
342 Object = UserHeapAlloc(size);
343 dt = FALSE;
344 break;
345 }
346
347 if (!Object)
348 return NULL;
349
350
351 hi = UserAllocHandle(ht, Object, type );
352 if (!hi)
353 {
354 if (dt)
355 DesktopHeapFree(rpdesk, Object);
356 else
357 UserHeapFree(Object);
358 return NULL;
359 }
360
361 RtlZeroMemory(Object, size);
362
363 switch (type)
364 {
365 case otWindow:
366 case otHook:
367 case otInputContext:
368 ((PTHRDESKHEAD)Object)->rpdesk = rpdesk;
369 ((PTHRDESKHEAD)Object)->pSelf = Object;
370 case otEvent:
371 ((PTHROBJHEAD)Object)->pti = pti;
372 break;
373
374 case otMenu:
375 case otCallProc:
376 ((PPROCDESKHEAD)Object)->rpdesk = rpdesk;
377 ((PPROCDESKHEAD)Object)->pSelf = Object;
378 break;
379
380 case otCursorIcon:
381 ((PPROCMARKHEAD)Object)->ppi = ppi;
382 break;
383
384 default:
385 break;
386 }
387 /* Now set default headers. */
388 ((PHEAD)Object)->h = hi;
389 ((PHEAD)Object)->cLockObj = 2; // we need this, because we create 2 refs: handle and pointer!
390
391 if (h)
392 *h = hi;
393 return Object;
394 }
395
396
397 BOOL
398 FASTCALL
399 UserDereferenceObject(PVOID object)
400 {
401 PUSER_HANDLE_ENTRY entry;
402 USER_OBJECT_TYPE type;
403
404 ASSERT(((PHEAD)object)->cLockObj >= 1);
405
406 if ((INT)--((PHEAD)object)->cLockObj <= 0)
407 {
408 entry = handle_to_entry(gHandleTable, ((PHEAD)object)->h );
409
410 DPRINT("warning! Dereference to zero! Obj -> 0x%x\n", object);
411
412 ((PHEAD)object)->cLockObj = 0;
413
414 if (!(entry->flags & HANDLEENTRY_INDESTROY))
415 return TRUE;
416
417 type = entry->type;
418 free_user_entry(gHandleTable, entry );
419
420 switch (type)
421 {
422 case otWindow:
423 // case otMenu:
424 case otHook:
425 case otCallProc:
426 case otInputContext:
427 return DesktopHeapFree(((PTHRDESKHEAD)object)->rpdesk, object);
428
429 default:
430 return UserHeapFree(object);
431 }
432 }
433 return FALSE;
434 }
435
436 BOOL
437 FASTCALL
438 UserFreeHandle(PUSER_HANDLE_TABLE ht, HANDLE handle )
439 {
440 PUSER_HANDLE_ENTRY entry;
441
442 if (!(entry = handle_to_entry( ht, handle )))
443 {
444 SetLastNtError( STATUS_INVALID_HANDLE );
445 return FALSE;
446 }
447
448 entry->flags = HANDLEENTRY_INDESTROY;
449
450 return UserDereferenceObject(entry->ptr);
451 }
452
453 BOOL
454 FASTCALL
455 UserDeleteObject(HANDLE h, USER_OBJECT_TYPE type )
456 {
457 PVOID body = UserGetObject(gHandleTable, h, type);
458
459 if (!body) return FALSE;
460
461 ASSERT( ((PHEAD)body)->cLockObj >= 1);
462
463 return UserFreeHandle(gHandleTable, h);
464 }
465
466 VOID
467 FASTCALL
468 UserReferenceObject(PVOID obj)
469 {
470 ASSERT(((PHEAD)obj)->cLockObj >= 0);
471
472 ((PHEAD)obj)->cLockObj++;
473 }
474
475 PVOID
476 FASTCALL
477 UserReferenceObjectByHandle(HANDLE handle, USER_OBJECT_TYPE type)
478 {
479 PVOID object;
480
481 object = UserGetObject(gHandleTable, handle, type);
482 if (object)
483 {
484 UserReferenceObject(object);
485 }
486 return object;
487 }