Allocate memory for the handle table from paged pool!
[reactos.git] / reactos / subsys / win32k / objects / gdiobj.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * GDIOBJ.C - GDI object manipulation routines
21 *
22 * $Id: gdiobj.c,v 1.73 2004/10/02 16:48:12 navaraf Exp $
23 *
24 */
25 #include <w32k.h>
26
27 /* count all gdi objects */
28 #define GDI_COUNT_OBJECTS 1
29
30 /*! Size of the GDI handle table
31 * http://www.windevnet.com/documents/s=7290/wdj9902b/9902b.htm
32 * gdi handle table can hold 0x4000 handles
33 */
34 #define GDI_HANDLE_COUNT 0x4000
35
36 #define GDI_GLOBAL_PROCESS ((HANDLE) 0xffffffff)
37
38 #define GDI_HANDLE_INDEX_MASK (GDI_HANDLE_COUNT - 1)
39 #define GDI_HANDLE_TYPE_MASK 0x007f0000
40 #define GDI_HANDLE_STOCK_MASK 0x00800000
41
42 #define GDI_HANDLE_CREATE(i, t) ((HANDLE)(((i) & GDI_HANDLE_INDEX_MASK) | ((t) & GDI_HANDLE_TYPE_MASK)))
43 #define GDI_HANDLE_GET_INDEX(h) (((DWORD)(h)) & GDI_HANDLE_INDEX_MASK)
44 #define GDI_HANDLE_GET_TYPE(h) (((DWORD)(h)) & GDI_HANDLE_TYPE_MASK)
45 #define GDI_HANDLE_IS_TYPE(h, t) ((t) == (((DWORD)(h)) & GDI_HANDLE_TYPE_MASK))
46 #define GDI_HANDLE_IS_STOCKOBJ(h) (0 != (((DWORD)(h)) & GDI_HANDLE_STOCK_MASK))
47 #define GDI_HANDLE_SET_STOCKOBJ(h) ((h) = (HANDLE)(((DWORD)(h)) | GDI_HANDLE_STOCK_MASK))
48
49 #define GDI_TYPE_TO_MAGIC(t) ((WORD) ((t) >> 16))
50 #define GDI_MAGIC_TO_TYPE(m) ((DWORD)(m) << 16)
51
52 /* FIXME Ownership of GDI objects by processes not properly implemented yet */
53 #if 0
54 #define GDI_VALID_OBJECT(h, obj, t, f) \
55 (NULL != (obj) \
56 && (GDI_MAGIC_TO_TYPE((obj)->Magic) == (t) || GDI_OBJECT_TYPE_DONTCARE == (t)) \
57 && (GDI_HANDLE_GET_TYPE((h)) == GDI_MAGIC_TO_TYPE((obj)->Magic)) \
58 && (((obj)->hProcessId == PsGetCurrentProcessId()) \
59 || (GDI_GLOBAL_PROCESS == (obj)->hProcessId) \
60 || ((f) & GDIOBJFLAG_IGNOREPID)))
61 #else
62 #define GDI_VALID_OBJECT(h, obj, t, f) \
63 (NULL != (obj) \
64 && (GDI_MAGIC_TO_TYPE((obj)->Magic) == (t) || GDI_OBJECT_TYPE_DONTCARE == (t)) \
65 && (GDI_HANDLE_GET_TYPE((h)) == GDI_MAGIC_TO_TYPE((obj)->Magic)))
66 #endif
67
68 typedef struct _GDI_HANDLE_TABLE
69 {
70 WORD wTableSize;
71 WORD AllocationHint;
72 #if GDI_COUNT_OBJECTS
73 ULONG HandlesCount;
74 #endif
75 PPAGED_LOOKASIDE_LIST LookasideLists;
76 PGDIOBJHDR Handles[1];
77 } GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
78
79 typedef struct
80 {
81 ULONG Type;
82 ULONG Size;
83 } GDI_OBJ_SIZE;
84
85 const
86 GDI_OBJ_SIZE ObjSizes[] =
87 {
88 /* Testing shows that regions are the most used GDIObj type,
89 so put that one first for performance */
90 {GDI_OBJECT_TYPE_REGION, sizeof(ROSRGNDATA)},
91 {GDI_OBJECT_TYPE_BITMAP, sizeof(BITMAPOBJ)},
92 {GDI_OBJECT_TYPE_DC, sizeof(DC)},
93 {GDI_OBJECT_TYPE_PALETTE, sizeof(PALGDI)},
94 {GDI_OBJECT_TYPE_BRUSH, sizeof(GDIBRUSHOBJ)},
95 {GDI_OBJECT_TYPE_PEN, sizeof(GDIBRUSHOBJ)},
96 {GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ)},
97 {GDI_OBJECT_TYPE_DCE, sizeof(DCE)},
98 /*
99 {GDI_OBJECT_TYPE_DIRECTDRAW, sizeof(DD_DIRECTDRAW)},
100 {GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE)},
101 */
102 {GDI_OBJECT_TYPE_EXTPEN, 0},
103 {GDI_OBJECT_TYPE_METADC, 0},
104 {GDI_OBJECT_TYPE_METAFILE, 0},
105 {GDI_OBJECT_TYPE_ENHMETAFILE, 0},
106 {GDI_OBJECT_TYPE_ENHMETADC, 0},
107 {GDI_OBJECT_TYPE_MEMDC, 0},
108 {GDI_OBJECT_TYPE_EMF, 0}
109 };
110
111 #define OBJTYPE_COUNT (sizeof(ObjSizes) / sizeof(ObjSizes[0]))
112
113 /* GDI stock objects */
114
115 static LOGBRUSH WhiteBrush =
116 { BS_SOLID, RGB(255,255,255), 0 };
117
118 static LOGBRUSH LtGrayBrush =
119 /* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
120 { BS_SOLID, RGB(192,192,192), 0 };
121
122 static LOGBRUSH GrayBrush =
123 /* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
124 { BS_SOLID, RGB(128,128,128), 0 };
125
126 static LOGBRUSH DkGrayBrush =
127 /* This is BS_HATCHED, for 1 bitperpixel. This makes the spray work in pbrush */
128 /* NB_HATCH_STYLES is an index into HatchBrushes */
129 { BS_HATCHED, RGB(0,0,0), NB_HATCH_STYLES };
130
131 static LOGBRUSH BlackBrush =
132 { BS_SOLID, RGB(0,0,0), 0 };
133
134 static LOGBRUSH NullBrush =
135 { BS_NULL, 0, 0 };
136
137 static LOGPEN WhitePen =
138 { PS_SOLID, { 0, 0 }, RGB(255,255,255) };
139
140 static LOGPEN BlackPen =
141 { PS_SOLID, { 0, 0 }, RGB(0,0,0) };
142
143 static LOGPEN NullPen =
144 { PS_NULL, { 0, 0 }, 0 };
145
146 static LOGFONTW OEMFixedFont =
147 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, OEM_CHARSET,
148 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
149
150 static LOGFONTW AnsiFixedFont =
151 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
152 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
153
154 /*static LOGFONTW AnsiVarFont =
155 *{ 10, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
156 * 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" }; */
157
158 static LOGFONTW SystemFont =
159 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
160 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
161
162 static LOGFONTW DeviceDefaultFont =
163 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
164 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
165
166 static LOGFONTW SystemFixedFont =
167 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
168 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
169
170 /* FIXME: Is this correct? */
171 static LOGFONTW DefaultGuiFont =
172 { 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
173 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
174
175 #define NB_STOCK_OBJECTS (DEFAULT_GUI_FONT + 1)
176
177 static HGDIOBJ StockObjects[NB_STOCK_OBJECTS];
178 static PGDI_HANDLE_TABLE HandleTable = 0;
179 static FAST_MUTEX HandleTableMutex;
180 static FAST_MUTEX RefCountHandling;
181 static LARGE_INTEGER ShortDelay;
182
183 /*!
184 * Allocate GDI object table.
185 * \param Size - number of entries in the object table.
186 * Notes:: Must be called at IRQL < DISPATCH_LEVEL.
187 */
188 static PGDI_HANDLE_TABLE FASTCALL
189 GDIOBJ_iAllocHandleTable (WORD Size)
190 {
191 PGDI_HANDLE_TABLE handleTable;
192 ULONG MemSize;
193 UINT ObjType;
194
195 MemSize = sizeof(GDI_HANDLE_TABLE) + sizeof(PGDIOBJ) * Size;
196
197 ExAcquireFastMutex(&HandleTableMutex);
198 handleTable = ExAllocatePoolWithTag(PagedPool, MemSize, TAG_GDIHNDTBLE);
199 ASSERT( handleTable );
200 memset (handleTable, 0, MemSize);
201 #if GDI_COUNT_OBJECTS
202 handleTable->HandlesCount = 0;
203 #endif
204 handleTable->wTableSize = Size;
205 handleTable->AllocationHint = 1;
206 handleTable->LookasideLists = ExAllocatePoolWithTag(PagedPool,
207 OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
208 TAG_GDIHNDTBLE);
209 if (NULL == handleTable->LookasideLists)
210 {
211 ExFreePool(handleTable);
212 ExReleaseFastMutex(&HandleTableMutex);
213 return NULL;
214 }
215 for (ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
216 {
217 ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
218 ObjSizes[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
219 }
220 ExReleaseFastMutex(&HandleTableMutex);
221
222 return handleTable;
223 }
224
225 /*!
226 * Returns the entry into the handle table by index.
227 */
228 static PGDIOBJHDR FASTCALL
229 GDIOBJ_iGetObjectForIndex(WORD TableIndex)
230 {
231 if (0 == TableIndex || HandleTable->wTableSize < TableIndex)
232 {
233 DPRINT1("Invalid TableIndex %u\n", (unsigned) TableIndex);
234 return NULL;
235 }
236
237 return HandleTable->Handles[TableIndex];
238 }
239
240 /*!
241 * Finds next free entry in the GDI handle table.
242 * \return index into the table is successful, zero otherwise.
243 */
244 static WORD FASTCALL
245 GDIOBJ_iGetNextOpenHandleIndex (void)
246 {
247 WORD tableIndex;
248
249 for (tableIndex = HandleTable->AllocationHint;
250 tableIndex < HandleTable->wTableSize;
251 tableIndex++)
252 {
253 if (HandleTable->Handles[tableIndex] == NULL)
254 {
255 HandleTable->AllocationHint = tableIndex + 1;
256 return tableIndex;
257 }
258 }
259
260 for (tableIndex = 1;
261 tableIndex < HandleTable->AllocationHint;
262 tableIndex++)
263 {
264 if (HandleTable->Handles[tableIndex] == NULL)
265 {
266 HandleTable->AllocationHint = tableIndex + 1;
267 return tableIndex;
268 }
269 }
270
271 return 0;
272 }
273
274 static PPAGED_LOOKASIDE_LIST FASTCALL
275 FindLookasideList(DWORD ObjectType)
276 {
277 int Index;
278
279 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
280 {
281 if (ObjSizes[Index].Type == ObjectType)
282 {
283 return HandleTable->LookasideLists + Index;
284 }
285 }
286
287 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
288
289 return NULL;
290 }
291
292 /*!
293 * Allocate memory for GDI object and return handle to it.
294 *
295 * \param Size - size of the GDI object. This shouldn't to include the size of GDIOBJHDR.
296 * The actual amount of allocated memory is sizeof(GDIOBJHDR)+Size
297 * \param ObjectType - type of object \ref GDI object types
298 * \param CleanupProcPtr - Routine to be called on destruction of object
299 *
300 * \return Handle of the allocated object.
301 *
302 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
303 */
304 HGDIOBJ FASTCALL
305 GDIOBJ_AllocObj(WORD Size, DWORD ObjectType, GDICLEANUPPROC CleanupProc)
306 {
307 PW32PROCESS W32Process;
308 PGDIOBJHDR newObject;
309 WORD Index;
310 PPAGED_LOOKASIDE_LIST LookasideList;
311
312 ExAcquireFastMutex(&HandleTableMutex);
313 Index = GDIOBJ_iGetNextOpenHandleIndex ();
314 if (0 == Index)
315 {
316 ExReleaseFastMutex(&HandleTableMutex);
317 DPRINT1("Out of GDI handles\n");
318 return NULL;
319 }
320
321 LookasideList = FindLookasideList(ObjectType);
322 if (NULL == LookasideList)
323 {
324 ExReleaseFastMutex(&HandleTableMutex);
325 return NULL;
326 }
327 newObject = ExAllocateFromPagedLookasideList(LookasideList);
328 if (NULL == newObject)
329 {
330 ExReleaseFastMutex(&HandleTableMutex);
331 DPRINT1("Unable to allocate GDI object from lookaside list\n");
332 return NULL;
333 }
334 RtlZeroMemory (newObject, Size + sizeof(GDIOBJHDR));
335
336 newObject->wTableIndex = Index;
337
338 newObject->dwCount = 0;
339 newObject->hProcessId = PsGetCurrentProcessId ();
340 newObject->CleanupProc = CleanupProc;
341 newObject->Magic = GDI_TYPE_TO_MAGIC(ObjectType);
342 newObject->lockfile = NULL;
343 newObject->lockline = 0;
344 #ifdef GDIOBJ_USE_FASTMUTEX
345 ExInitializeFastMutex(&newObject->Lock);
346 newObject->RecursiveLockCount = 0;
347 #else
348 newObject->LockTid = 0;
349 newObject->LockCount = 0;
350 #endif
351 HandleTable->Handles[Index] = newObject;
352 #if GDI_COUNT_OBJECTS
353 HandleTable->HandlesCount++;
354 #endif
355 ExReleaseFastMutex(&HandleTableMutex);
356
357 W32Process = PsGetCurrentProcess()->Win32Process;
358 if(W32Process)
359 {
360 W32Process->GDIObjects++;
361 }
362
363 return GDI_HANDLE_CREATE(Index, ObjectType);
364 }
365
366 /*!
367 * Free memory allocated for the GDI object. For each object type this function calls the
368 * appropriate cleanup routine.
369 *
370 * \param hObj - handle of the object to be deleted.
371 * \param ObjectType - one of the \ref GDI object types
372 * or GDI_OBJECT_TYPE_DONTCARE.
373 * \param Flag - if set to GDIOBJFLAG_IGNOREPID then the routine doesn't check if the process that
374 * tries to delete the object is the same one that created it.
375 *
376 * \return Returns TRUE if succesful.
377 *
378 * \note You should only use GDIOBJFLAG_IGNOREPID if you are cleaning up after the process that terminated.
379 * \note This function deferres object deletion if it is still in use.
380 */
381 BOOL STDCALL
382 GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType, DWORD Flag)
383 {
384 PW32PROCESS W32Process;
385 PGDIOBJHDR objectHeader;
386 PGDIOBJ Obj;
387 PPAGED_LOOKASIDE_LIST LookasideList;
388 BOOL bRet = TRUE;
389
390 objectHeader = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
391 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x, object: %x\n", hObj, objectHeader);
392
393 if (! GDI_VALID_OBJECT(hObj, objectHeader, ObjectType, Flag)
394 || GDI_GLOBAL_PROCESS == objectHeader->hProcessId)
395
396 {
397 DPRINT1("Can't delete hObj:0x%08x, type:0x%08x, flag:%d\n", hObj, ObjectType, Flag);
398 return FALSE;
399 }
400
401 DPRINT("FreeObj: locks: %x\n", objectHeader->dwCount );
402 if (!(Flag & GDIOBJFLAG_IGNORELOCK))
403 {
404 /* check that the reference count is zero. if not then set flag
405 * and delete object when releaseobj is called */
406 ExAcquireFastMutex(&RefCountHandling);
407 if ((objectHeader->dwCount & ~0x80000000) > 0 )
408 {
409 DPRINT("GDIOBJ_FreeObj: delayed object deletion: count %d\n", objectHeader->dwCount);
410 objectHeader->dwCount |= 0x80000000;
411 ExReleaseFastMutex(&RefCountHandling);
412 return TRUE;
413 }
414 ExReleaseFastMutex(&RefCountHandling);
415 }
416
417 /* allow object to delete internal data */
418 if (NULL != objectHeader->CleanupProc)
419 {
420 Obj = (PGDIOBJ)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
421 bRet = (*(objectHeader->CleanupProc))(Obj);
422 }
423 LookasideList = FindLookasideList(GDI_MAGIC_TO_TYPE(objectHeader->Magic));
424 if (NULL != LookasideList)
425 {
426 ExFreeToPagedLookasideList(LookasideList, objectHeader);
427 }
428 ExAcquireFastMutexUnsafe (&HandleTableMutex);
429 HandleTable->Handles[GDI_HANDLE_GET_INDEX(hObj)] = NULL;
430 #if GDI_COUNT_OBJECTS
431 HandleTable->HandlesCount--;
432 #endif
433 ExReleaseFastMutexUnsafe (&HandleTableMutex);
434
435 W32Process = PsGetCurrentProcess()->Win32Process;
436 if(W32Process)
437 {
438 W32Process->GDIObjects--;
439 }
440
441 return bRet;
442 }
443
444 /*!
445 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
446 * duplicates. You should use this function to avoid trying to lock the same object twice!
447 *
448 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
449 * \param nObj number of objects to lock
450 * \return for each entry in pList this function sets pObj field to point to the object.
451 *
452 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
453 */
454 BOOL FASTCALL
455 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
456 {
457 INT i, j;
458 ASSERT( pList );
459 /* FIXME - check for "invalid" handles */
460 /* go through the list checking for duplicate objects */
461 for (i = 0; i < nObj; i++)
462 {
463 pList[i].pObj = NULL;
464 for (j = 0; j < i; j++)
465 {
466 if (pList[i].hObj == pList[j].hObj)
467 {
468 /* already locked, so just copy the pointer to the object */
469 pList[i].pObj = pList[j].pObj;
470 break;
471 }
472 }
473
474 if (NULL == pList[i].pObj)
475 {
476 /* object hasn't been locked, so lock it. */
477 if (NULL != pList[i].hObj)
478 {
479 pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
480 }
481 }
482 }
483
484 return TRUE;
485 }
486
487 /*!
488 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
489 * duplicates.
490 *
491 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
492 * \param nObj number of objects to lock
493 *
494 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
495 */
496 BOOL FASTCALL
497 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
498 {
499 INT i, j;
500 ASSERT(pList);
501
502 /* go through the list checking for duplicate objects */
503 for (i = 0; i < nObj; i++)
504 {
505 if (NULL != pList[i].pObj)
506 {
507 for (j = i + 1; j < nObj; j++)
508 {
509 if ((pList[i].pObj == pList[j].pObj))
510 {
511 /* set the pointer to zero for all duplicates */
512 pList[j].pObj = NULL;
513 }
514 }
515 GDIOBJ_UnlockObj(pList[i].hObj, pList[i].ObjectType);
516 pList[i].pObj = NULL;
517 }
518 }
519
520 return TRUE;
521 }
522
523 /*!
524 * Get the type of the object.
525 * \param ObjectHandle - handle of the object.
526 * \return One of the \ref GDI object types
527 */
528 DWORD FASTCALL
529 GDIOBJ_GetObjectType(HGDIOBJ ObjectHandle)
530 {
531 PGDIOBJHDR ObjHdr;
532
533 ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
534 if (NULL == ObjHdr
535 || ! GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_MAGIC_TO_TYPE(ObjHdr->Magic), 0))
536 {
537 DPRINT1("Invalid ObjectHandle 0x%08x\n", ObjectHandle);
538 return 0;
539 }
540 DPRINT("GDIOBJ_GetObjectType for handle 0x%08x returns 0x%08x\n", ObjectHandle,
541 GDI_MAGIC_TO_TYPE(ObjHdr->Magic));
542
543 return GDI_MAGIC_TO_TYPE(ObjHdr->Magic);
544 }
545
546 /*!
547 * Initialization of the GDI object engine.
548 */
549 VOID FASTCALL
550 InitGdiObjectHandleTable (VOID)
551 {
552 DPRINT("InitGdiObjectHandleTable\n");
553 ExInitializeFastMutex (&HandleTableMutex);
554 ExInitializeFastMutex (&RefCountHandling);
555
556 ShortDelay.QuadPart = -100;
557
558 HandleTable = GDIOBJ_iAllocHandleTable (GDI_HANDLE_COUNT);
559 DPRINT("HandleTable: %x\n", HandleTable );
560
561 InitEngHandleTable();
562 }
563
564 /*!
565 * Creates a bunch of stock objects: brushes, pens, fonts.
566 */
567 VOID FASTCALL
568 CreateStockObjects(void)
569 {
570 unsigned Object;
571
572 DPRINT("Beginning creation of stock objects\n");
573
574 /* Create GDI Stock Objects from the logical structures we've defined */
575
576 StockObjects[WHITE_BRUSH] = IntGdiCreateBrushIndirect(&WhiteBrush);
577 StockObjects[LTGRAY_BRUSH] = IntGdiCreateBrushIndirect(&LtGrayBrush);
578 StockObjects[GRAY_BRUSH] = IntGdiCreateBrushIndirect(&GrayBrush);
579 StockObjects[DKGRAY_BRUSH] = IntGdiCreateBrushIndirect(&DkGrayBrush);
580 StockObjects[BLACK_BRUSH] = IntGdiCreateBrushIndirect(&BlackBrush);
581 StockObjects[NULL_BRUSH] = IntGdiCreateBrushIndirect(&NullBrush);
582
583 StockObjects[WHITE_PEN] = IntGdiCreatePenIndirect(&WhitePen);
584 StockObjects[BLACK_PEN] = IntGdiCreatePenIndirect(&BlackPen);
585 StockObjects[NULL_PEN] = IntGdiCreatePenIndirect(&NullPen);
586
587 (void) TextIntCreateFontIndirect(&OEMFixedFont, (HFONT*)&StockObjects[OEM_FIXED_FONT]);
588 (void) TextIntCreateFontIndirect(&AnsiFixedFont, (HFONT*)&StockObjects[ANSI_FIXED_FONT]);
589 (void) TextIntCreateFontIndirect(&SystemFont, (HFONT*)&StockObjects[SYSTEM_FONT]);
590 (void) TextIntCreateFontIndirect(&DeviceDefaultFont, (HFONT*)&StockObjects[DEVICE_DEFAULT_FONT]);
591 (void) TextIntCreateFontIndirect(&SystemFixedFont, (HFONT*)&StockObjects[SYSTEM_FIXED_FONT]);
592 (void) TextIntCreateFontIndirect(&DefaultGuiFont, (HFONT*)&StockObjects[DEFAULT_GUI_FONT]);
593
594 StockObjects[DEFAULT_PALETTE] = (HGDIOBJ*)PALETTE_Init();
595
596 for (Object = 0; Object < NB_STOCK_OBJECTS; Object++)
597 {
598 if (NULL != StockObjects[Object])
599 {
600 GDIOBJ_SetOwnership(StockObjects[Object], NULL);
601 /* GDI_HANDLE_SET_STOCKOBJ(StockObjects[Object]);*/
602 }
603 }
604
605 DPRINT("Completed creation of stock objects\n");
606 }
607
608 /*!
609 * Return stock object.
610 * \param Object - stock object id.
611 * \return Handle to the object.
612 */
613 HGDIOBJ STDCALL
614 NtGdiGetStockObject(INT Object)
615 {
616 DPRINT("NtGdiGetStockObject index %d\n", Object);
617
618 return ((Object < 0) || (NB_STOCK_OBJECTS <= Object)) ? NULL : StockObjects[Object];
619 }
620
621 /*!
622 * Delete GDI object
623 * \param hObject object handle
624 * \return if the function fails the returned value is FALSE.
625 */
626 BOOL STDCALL
627 NtGdiDeleteObject(HGDIOBJ hObject)
628 {
629 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
630
631 return NULL != hObject
632 ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_DEFAULT) : FALSE;
633 }
634
635 /*!
636 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
637 * \param Process - PID of the process that will be destroyed.
638 */
639 BOOL FASTCALL
640 CleanupForProcess (struct _EPROCESS *Process, INT Pid)
641 {
642 DWORD i;
643 PGDIOBJHDR objectHeader;
644 PEPROCESS CurrentProcess;
645
646 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
647 CurrentProcess = PsGetCurrentProcess();
648 if (CurrentProcess != Process)
649 {
650 KeAttachProcess(Process);
651 }
652
653 for(i = 1; i < HandleTable->wTableSize; i++)
654 {
655 objectHeader = GDIOBJ_iGetObjectForIndex(i);
656 if (NULL != objectHeader &&
657 (INT) objectHeader->hProcessId == Pid)
658 {
659 DPRINT("CleanupForProcess: %d, process: %d, locks: %d, magic: 0x%x", i, objectHeader->hProcessId, objectHeader->dwCount, objectHeader->Magic);
660 GDIOBJ_FreeObj(GDI_HANDLE_CREATE(i, GDI_MAGIC_TO_TYPE(objectHeader->Magic)),
661 GDI_MAGIC_TO_TYPE(objectHeader->Magic),
662 GDIOBJFLAG_IGNOREPID | GDIOBJFLAG_IGNORELOCK);
663 }
664 }
665
666 if (CurrentProcess != Process)
667 {
668 KeDetachProcess();
669 }
670
671 DPRINT("Completed cleanup for process %d\n", Pid);
672
673 return TRUE;
674 }
675
676 #define GDIOBJ_TRACKLOCKS
677
678 #ifdef GDIOBJ_LockObj
679 #undef GDIOBJ_LockObj
680 PGDIOBJ FASTCALL
681 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
682 {
683 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
684 #ifndef GDIOBJ_USE_FASTMUTEX
685 DWORD CurrentTid = (DWORD)PsGetCurrentThreadId();
686 #endif
687
688 DPRINT("(%s:%i) GDIOBJ_LockObjDbg(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
689 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
690 {
691 int reason = 0;
692 if (NULL == ObjHdr)
693 {
694 reason = 1;
695 }
696 else if (GDI_MAGIC_TO_TYPE(ObjHdr->Magic) != ObjectType && ObjectType != GDI_OBJECT_TYPE_DONTCARE)
697 {
698 reason = 2;
699 }
700 else if (ObjHdr->hProcessId != GDI_GLOBAL_PROCESS
701 && ObjHdr->hProcessId != PsGetCurrentProcessId())
702 {
703 reason = 3;
704 }
705 else if (GDI_HANDLE_GET_TYPE(hObj) != ObjectType && ObjectType != GDI_OBJECT_TYPE_DONTCARE)
706 {
707 reason = 4;
708 }
709 DPRINT1("GDIOBJ_LockObj failed for 0x%08x, reqtype 0x%08x reason %d\n",
710 hObj, ObjectType, reason );
711 DPRINT1("\tcalled from: %s:%i\n", file, line );
712 return NULL;
713 }
714
715 #ifdef GDIOBJ_USE_FASTMUTEX
716 if (ObjHdr->Lock.Owner == KeGetCurrentThread())
717 {
718 ObjHdr->RecursiveLockCount++;
719 }
720 else
721 {
722 #ifdef NDEBUG
723 ExAcquireFastMutex(&ObjHdr->Lock);
724 #else /* NDEBUG */
725 if (! ExTryToAcquireFastMutex(&ObjHdr->Lock))
726 {
727 DPRINT1("Caution! GDIOBJ_LockObj trying to lock object 0x%x second time\n", hObj);
728 DPRINT1(" called from: %s:%i (thread %x)\n", file, line, KeGetCurrentThread());
729 if (NULL != ObjHdr->lockfile)
730 {
731 DPRINT1(" previously locked from: %s:%i (thread %x)\n", ObjHdr->lockfile, ObjHdr->lockline, ObjHdr->Lock.Owner);
732 }
733 ExAcquireFastMutex(&ObjHdr->Lock);
734 DPRINT1(" Disregard previous message about object 0x%x, it's ok\n", hObj);
735 }
736 #endif /* NDEBUG */
737 ObjHdr->RecursiveLockCount++;
738 }
739 #else
740 if (ObjHdr->LockTid == CurrentTid)
741 {
742 InterlockedIncrement(&ObjHdr->LockCount);
743 }
744 else
745 {
746 for (;;)
747 {
748 if (InterlockedCompareExchange(&ObjHdr->LockTid, CurrentTid, 0) == CurrentTid)
749 {
750 InterlockedIncrement(&ObjHdr->LockCount);
751 break;
752 }
753 /* FIXME: KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay); */
754 }
755 }
756 #endif
757
758 ExAcquireFastMutex(&RefCountHandling);
759 ObjHdr->dwCount++;
760 ExReleaseFastMutex(&RefCountHandling);
761
762 if (NULL == ObjHdr->lockfile)
763 {
764 ObjHdr->lockfile = file;
765 ObjHdr->lockline = line;
766 }
767
768 return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
769 }
770 #endif//GDIOBJ_LockObj
771
772 #ifdef GDIOBJ_UnlockObj
773 #undef GDIOBJ_UnlockObj
774 BOOL FASTCALL
775 GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
776 {
777 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
778
779 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
780 {
781 DPRINT1("GDIBOJ_UnlockObj failed for 0x%08x, reqtype 0x%08x\n",
782 hObj, ObjectType);
783 DPRINT1("\tcalled from: %s:%i\n", file, line);
784 return FALSE;
785 }
786 DPRINT("(%s:%i) GDIOBJ_UnlockObj(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
787 ObjHdr->lockfile = NULL;
788 ObjHdr->lockline = 0;
789
790 return GDIOBJ_UnlockObj(hObj, ObjectType);
791 }
792 #endif//GDIOBJ_LockObj
793
794 /*!
795 * Return pointer to the object by handle.
796 *
797 * \param hObj Object handle
798 * \param ObjectType one of the object types defined in \ref GDI object types
799 * \return Pointer to the object.
800 *
801 * \note Process can only get pointer to the objects it created or global objects.
802 *
803 * \todo Don't allow to lock the objects twice! Synchronization!
804 */
805 PGDIOBJ FASTCALL
806 GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ObjectType)
807 {
808 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
809 #ifndef GDIOBJ_USE_FASTMUTEX
810 DWORD CurrentTid = (DWORD)PsGetCurrentThreadId();
811 #endif
812
813 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
814 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
815 {
816 DPRINT1("GDIBOJ_LockObj failed for 0x%08x, type 0x%08x\n",
817 hObj, ObjectType);
818 return NULL;
819 }
820
821 #ifdef GDIOBJ_USE_FASTMUTEX
822 if (ObjHdr->Lock.Owner == KeGetCurrentThread())
823 {
824 ObjHdr->RecursiveLockCount++;
825 }
826 else
827 {
828 ExAcquireFastMutex(&ObjHdr->Lock);
829 ObjHdr->RecursiveLockCount++;
830 }
831 #else
832 if (ObjHdr->LockTid == CurrentTid)
833 {
834 InterlockedIncrement(&ObjHdr->LockCount);
835 }
836 else
837 {
838 for (;;)
839 {
840 if (InterlockedCompareExchange(&ObjHdr->LockTid, CurrentTid, 0) == CurrentTid)
841 {
842 InterlockedIncrement(&ObjHdr->LockCount);
843 break;
844 }
845 /* FIXME: KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay); */
846 }
847 }
848 #endif
849
850 ExAcquireFastMutex(&RefCountHandling);
851 ObjHdr->dwCount++;
852 ExReleaseFastMutex(&RefCountHandling);
853 return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
854 }
855
856 /*!
857 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
858 * as soon as you don't need to have access to it's data.
859
860 * \param hObj Object handle
861 * \param ObjectType one of the object types defined in \ref GDI object types
862 *
863 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
864 * then \em this function frees the object when reference count is zero.
865 *
866 * \todo Change synchronization algorithm.
867 */
868 #undef GDIOBJ_UnlockObj
869 BOOL FASTCALL
870 GDIOBJ_UnlockObj(HGDIOBJ hObj, DWORD ObjectType)
871 {
872 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
873
874 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
875 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
876 {
877 DPRINT1( "GDIOBJ_UnLockObj: failed\n");
878 return FALSE;
879 }
880
881 #ifdef GDIOBJ_USE_FASTMUTEX
882 if (--ObjHdr->RecursiveLockCount == 0)
883 ExReleaseFastMutex(&ObjHdr->Lock);
884 #else
885 if (InterlockedDecrement(&ObjHdr->LockCount) == 0)
886 {
887 InterlockedExchange(&ObjHdr->LockTid, 0);
888 }
889 #endif
890
891 ExAcquireFastMutex(&RefCountHandling);
892 if (0 == (ObjHdr->dwCount & ~0x80000000))
893 {
894 ExReleaseFastMutex(&RefCountHandling);
895 DPRINT1( "GDIOBJ_UnLockObj: unlock object (0x%x) that is not locked\n", hObj );
896 return FALSE;
897 }
898
899 ObjHdr->dwCount--;
900
901 if (ObjHdr->dwCount == 0x80000000)
902 {
903 //delayed object release
904 ObjHdr->dwCount = 0;
905 ExReleaseFastMutex(&RefCountHandling);
906 DPRINT("GDIOBJ_UnlockObj: delayed delete\n");
907 return GDIOBJ_FreeObj(hObj, ObjectType, GDIOBJFLAG_DEFAULT);
908 }
909 ExReleaseFastMutex(&RefCountHandling);
910
911 return TRUE;
912 }
913
914 BOOL FASTCALL
915 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
916 {
917 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
918
919 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
920 ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
921
922 return ObjHdr->hProcessId == PsGetCurrentProcessId();
923 }
924
925 void FASTCALL
926 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
927 {
928 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
929 PEPROCESS OldProcess;
930 PW32PROCESS W32Process;
931 NTSTATUS Status;
932
933 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
934 ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
935
936 if ((NULL == NewOwner && GDI_GLOBAL_PROCESS != ObjHdr->hProcessId)
937 || (NULL != NewOwner && ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId))
938 {
939 Status = PsLookupProcessByProcessId((PVOID)ObjHdr->hProcessId, &OldProcess);
940 if (NT_SUCCESS(Status))
941 {
942 W32Process = OldProcess->Win32Process;
943 if (W32Process)
944 {
945 W32Process->GDIObjects--;
946 }
947 ObDereferenceObject(OldProcess);
948 }
949 }
950
951 if (NULL == NewOwner)
952 {
953 ObjHdr->hProcessId = GDI_GLOBAL_PROCESS;
954 }
955 else if (ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId)
956 {
957 ObjHdr->hProcessId = (HANDLE) NewOwner->UniqueProcessId;
958 W32Process = NewOwner->Win32Process;
959 if (W32Process)
960 {
961 W32Process->GDIObjects++;
962 }
963 }
964 }
965
966 void FASTCALL
967 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
968 {
969 PGDIOBJHDR ObjHdrFrom = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyFrom));
970 PGDIOBJHDR ObjHdrTo = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyTo));
971 NTSTATUS Status;
972 PEPROCESS ProcessFrom;
973 PEPROCESS CurrentProcess;
974
975 ASSERT(NULL != ObjHdrFrom && NULL != ObjHdrTo);
976 if (NULL != ObjHdrFrom && NULL != ObjHdrTo
977 && ObjHdrTo->hProcessId != ObjHdrFrom->hProcessId)
978 {
979 if (ObjHdrFrom->hProcessId == GDI_GLOBAL_PROCESS)
980 {
981 GDIOBJ_SetOwnership(CopyTo, NULL);
982 }
983 else
984 {
985 /* Warning: ugly hack ahead
986 *
987 * During process cleanup, we can't call PsLookupProcessByProcessId
988 * for the current process, 'cause that function will try to
989 * reference the process, and since the process is closing down
990 * that will result in a bugcheck.
991 * So, instead, we call PsGetCurrentProcess, which doesn't reference
992 * the process. If the current process is indeed the one we're
993 * looking for, we use it, otherwise we can (safely) call
994 * PsLookupProcessByProcessId
995 */
996 CurrentProcess = PsGetCurrentProcess();
997 if (ObjHdrFrom->hProcessId == (HANDLE) CurrentProcess->UniqueProcessId)
998 {
999 GDIOBJ_SetOwnership(CopyTo, CurrentProcess);
1000 }
1001 else
1002 {
1003 Status = PsLookupProcessByProcessId((PVOID) ObjHdrFrom->hProcessId, &ProcessFrom);
1004 if (NT_SUCCESS(Status))
1005 {
1006 GDIOBJ_SetOwnership(CopyTo, ProcessFrom);
1007 ObDereferenceObject(ProcessFrom);
1008 }
1009 }
1010 }
1011 }
1012 }
1013
1014 /* EOF */