- Support resursive locking of the GDI objects.
[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.70 2004/07/04 01:23:32 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 /* prevent APC delivery for the *FastMutexUnsafe calls */
198 const KIRQL PrevIrql = KfRaiseIrql(APC_LEVEL);
199 ExAcquireFastMutexUnsafe (&HandleTableMutex);
200 handleTable = ExAllocatePoolWithTag(PagedPool, MemSize, TAG_GDIHNDTBLE);
201 ASSERT( handleTable );
202 memset (handleTable, 0, MemSize);
203 #if GDI_COUNT_OBJECTS
204 handleTable->HandlesCount = 0;
205 #endif
206 handleTable->wTableSize = Size;
207 handleTable->AllocationHint = 1;
208 handleTable->LookasideLists = ExAllocatePoolWithTag(PagedPool,
209 OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
210 TAG_GDIHNDTBLE);
211 if (NULL == handleTable->LookasideLists)
212 {
213 ExFreePool(handleTable);
214 ExReleaseFastMutexUnsafe (&HandleTableMutex);
215 KfLowerIrql(PrevIrql);
216 return NULL;
217 }
218 for (ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
219 {
220 ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
221 ObjSizes[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
222 }
223 ExReleaseFastMutexUnsafe (&HandleTableMutex);
224 KfLowerIrql(PrevIrql);
225
226 return handleTable;
227 }
228
229 /*!
230 * Returns the entry into the handle table by index.
231 */
232 static PGDIOBJHDR FASTCALL
233 GDIOBJ_iGetObjectForIndex(WORD TableIndex)
234 {
235 if (0 == TableIndex || HandleTable->wTableSize < TableIndex)
236 {
237 DPRINT1("Invalid TableIndex %u\n", (unsigned) TableIndex);
238 return NULL;
239 }
240
241 return HandleTable->Handles[TableIndex];
242 }
243
244 /*!
245 * Finds next free entry in the GDI handle table.
246 * \return index into the table is successful, zero otherwise.
247 */
248 static WORD FASTCALL
249 GDIOBJ_iGetNextOpenHandleIndex (void)
250 {
251 WORD tableIndex;
252
253 for (tableIndex = HandleTable->AllocationHint;
254 tableIndex < HandleTable->wTableSize;
255 tableIndex++)
256 {
257 if (HandleTable->Handles[tableIndex] == NULL)
258 {
259 HandleTable->AllocationHint = tableIndex + 1;
260 return tableIndex;
261 }
262 }
263
264 for (tableIndex = 1;
265 tableIndex < HandleTable->AllocationHint;
266 tableIndex++)
267 {
268 if (HandleTable->Handles[tableIndex] == NULL)
269 {
270 HandleTable->AllocationHint = tableIndex + 1;
271 return tableIndex;
272 }
273 }
274
275 return 0;
276 }
277
278 static PPAGED_LOOKASIDE_LIST FASTCALL
279 FindLookasideList(DWORD ObjectType)
280 {
281 int Index;
282
283 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
284 {
285 if (ObjSizes[Index].Type == ObjectType)
286 {
287 return HandleTable->LookasideLists + Index;
288 }
289 }
290
291 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
292
293 return NULL;
294 }
295
296 /*!
297 * Allocate memory for GDI object and return handle to it.
298 *
299 * \param Size - size of the GDI object. This shouldn't to include the size of GDIOBJHDR.
300 * The actual amount of allocated memory is sizeof(GDIOBJHDR)+Size
301 * \param ObjectType - type of object \ref GDI object types
302 * \param CleanupProcPtr - Routine to be called on destruction of object
303 *
304 * \return Handle of the allocated object.
305 *
306 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
307 */
308 HGDIOBJ FASTCALL
309 GDIOBJ_AllocObj(WORD Size, DWORD ObjectType, GDICLEANUPPROC CleanupProc)
310 {
311 PW32PROCESS W32Process;
312 PGDIOBJHDR newObject;
313 WORD Index;
314 PPAGED_LOOKASIDE_LIST LookasideList;
315
316 ExAcquireFastMutex(&HandleTableMutex);
317 Index = GDIOBJ_iGetNextOpenHandleIndex ();
318 if (0 == Index)
319 {
320 ExReleaseFastMutex(&HandleTableMutex);
321 DPRINT1("Out of GDI handles\n");
322 return NULL;
323 }
324
325 LookasideList = FindLookasideList(ObjectType);
326 if (NULL == LookasideList)
327 {
328 ExReleaseFastMutex(&HandleTableMutex);
329 return NULL;
330 }
331 newObject = ExAllocateFromPagedLookasideList(LookasideList);
332 if (NULL == newObject)
333 {
334 ExReleaseFastMutex(&HandleTableMutex);
335 DPRINT1("Unable to allocate GDI object from lookaside list\n");
336 return NULL;
337 }
338 RtlZeroMemory (newObject, Size + sizeof(GDIOBJHDR));
339
340 newObject->wTableIndex = Index;
341
342 newObject->dwCount = 0;
343 newObject->hProcessId = PsGetCurrentProcessId ();
344 newObject->CleanupProc = CleanupProc;
345 newObject->Magic = GDI_TYPE_TO_MAGIC(ObjectType);
346 newObject->lockfile = NULL;
347 newObject->lockline = 0;
348 #if 0
349 ExInitializeFastMutex(&newObject->Lock);
350 #else
351 newObject->LockTid = 0;
352 newObject->LockCount = 0;
353 #endif
354 HandleTable->Handles[Index] = newObject;
355 #if GDI_COUNT_OBJECTS
356 HandleTable->HandlesCount++;
357 #endif
358 ExReleaseFastMutex(&HandleTableMutex);
359
360 W32Process = PsGetCurrentProcess()->Win32Process;
361 if(W32Process)
362 {
363 W32Process->GDIObjects++;
364 }
365
366 return GDI_HANDLE_CREATE(Index, ObjectType);
367 }
368
369 /*!
370 * Free memory allocated for the GDI object. For each object type this function calls the
371 * appropriate cleanup routine.
372 *
373 * \param hObj - handle of the object to be deleted.
374 * \param ObjectType - one of the \ref GDI object types
375 * or GDI_OBJECT_TYPE_DONTCARE.
376 * \param Flag - if set to GDIOBJFLAG_IGNOREPID then the routine doesn't check if the process that
377 * tries to delete the object is the same one that created it.
378 *
379 * \return Returns TRUE if succesful.
380 *
381 * \note You should only use GDIOBJFLAG_IGNOREPID if you are cleaning up after the process that terminated.
382 * \note This function deferres object deletion if it is still in use.
383 */
384 BOOL STDCALL
385 GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType, DWORD Flag)
386 {
387 PW32PROCESS W32Process;
388 PGDIOBJHDR objectHeader;
389 PGDIOBJ Obj;
390 PPAGED_LOOKASIDE_LIST LookasideList;
391 BOOL bRet = TRUE;
392
393 objectHeader = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
394 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x, object: %x\n", hObj, objectHeader);
395
396 if (! GDI_VALID_OBJECT(hObj, objectHeader, ObjectType, Flag)
397 || GDI_GLOBAL_PROCESS == objectHeader->hProcessId)
398
399 {
400 DPRINT1("Can't delete hObj:0x%08x, type:0x%08x, flag:%d\n", hObj, ObjectType, Flag);
401 return FALSE;
402 }
403
404 DPRINT("FreeObj: locks: %x\n", objectHeader->dwCount );
405 if (!(Flag & GDIOBJFLAG_IGNORELOCK))
406 {
407 /* check that the reference count is zero. if not then set flag
408 * and delete object when releaseobj is called */
409 ExAcquireFastMutex(&RefCountHandling);
410 if ((objectHeader->dwCount & ~0x80000000) > 0 )
411 {
412 DPRINT("GDIOBJ_FreeObj: delayed object deletion: count %d\n", objectHeader->dwCount);
413 objectHeader->dwCount |= 0x80000000;
414 ExReleaseFastMutex(&RefCountHandling);
415 return TRUE;
416 }
417 ExReleaseFastMutex(&RefCountHandling);
418 }
419
420 /* allow object to delete internal data */
421 if (NULL != objectHeader->CleanupProc)
422 {
423 Obj = (PGDIOBJ)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
424 bRet = (*(objectHeader->CleanupProc))(Obj);
425 }
426 LookasideList = FindLookasideList(GDI_MAGIC_TO_TYPE(objectHeader->Magic));
427 if (NULL != LookasideList)
428 {
429 ExFreeToPagedLookasideList(LookasideList, objectHeader);
430 }
431 ExAcquireFastMutexUnsafe (&HandleTableMutex);
432 HandleTable->Handles[GDI_HANDLE_GET_INDEX(hObj)] = NULL;
433 #if GDI_COUNT_OBJECTS
434 HandleTable->HandlesCount--;
435 #endif
436 ExReleaseFastMutexUnsafe (&HandleTableMutex);
437
438 W32Process = PsGetCurrentProcess()->Win32Process;
439 if(W32Process)
440 {
441 W32Process->GDIObjects--;
442 }
443
444 return bRet;
445 }
446
447 /*!
448 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
449 * duplicates. You should use this function to avoid trying to lock the same object twice!
450 *
451 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
452 * \param nObj number of objects to lock
453 * \return for each entry in pList this function sets pObj field to point to the object.
454 *
455 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
456 */
457 BOOL FASTCALL
458 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
459 {
460 INT i, j;
461 ASSERT( pList );
462 /* FIXME - check for "invalid" handles */
463 /* go through the list checking for duplicate objects */
464 for (i = 0; i < nObj; i++)
465 {
466 pList[i].pObj = NULL;
467 for (j = 0; j < i; j++)
468 {
469 if (pList[i].hObj == pList[j].hObj)
470 {
471 /* already locked, so just copy the pointer to the object */
472 pList[i].pObj = pList[j].pObj;
473 break;
474 }
475 }
476
477 if (NULL == pList[i].pObj)
478 {
479 /* object hasn't been locked, so lock it. */
480 if (NULL != pList[i].hObj)
481 {
482 pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
483 }
484 }
485 }
486
487 return TRUE;
488 }
489
490 /*!
491 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
492 * duplicates.
493 *
494 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
495 * \param nObj number of objects to lock
496 *
497 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
498 */
499 BOOL FASTCALL
500 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
501 {
502 INT i, j;
503 ASSERT(pList);
504
505 /* go through the list checking for duplicate objects */
506 for (i = 0; i < nObj; i++)
507 {
508 if (NULL != pList[i].pObj)
509 {
510 for (j = i + 1; j < nObj; j++)
511 {
512 if ((pList[i].pObj == pList[j].pObj))
513 {
514 /* set the pointer to zero for all duplicates */
515 pList[j].pObj = NULL;
516 }
517 }
518 GDIOBJ_UnlockObj(pList[i].hObj, pList[i].ObjectType);
519 pList[i].pObj = NULL;
520 }
521 }
522
523 return TRUE;
524 }
525
526 /*!
527 * Get the type of the object.
528 * \param ObjectHandle - handle of the object.
529 * \return One of the \ref GDI object types
530 */
531 DWORD FASTCALL
532 GDIOBJ_GetObjectType(HGDIOBJ ObjectHandle)
533 {
534 PGDIOBJHDR ObjHdr;
535
536 ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
537 if (NULL == ObjHdr
538 || ! GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_MAGIC_TO_TYPE(ObjHdr->Magic), 0))
539 {
540 DPRINT1("Invalid ObjectHandle 0x%08x\n", ObjectHandle);
541 return 0;
542 }
543 DPRINT("GDIOBJ_GetObjectType for handle 0x%08x returns 0x%08x\n", ObjectHandle,
544 GDI_MAGIC_TO_TYPE(ObjHdr->Magic));
545
546 return GDI_MAGIC_TO_TYPE(ObjHdr->Magic);
547 }
548
549 /*!
550 * Initialization of the GDI object engine.
551 */
552 VOID FASTCALL
553 InitGdiObjectHandleTable (VOID)
554 {
555 DPRINT("InitGdiObjectHandleTable\n");
556 ExInitializeFastMutex (&HandleTableMutex);
557 ExInitializeFastMutex (&RefCountHandling);
558
559 ShortDelay.QuadPart = -100;
560
561 HandleTable = GDIOBJ_iAllocHandleTable (GDI_HANDLE_COUNT);
562 DPRINT("HandleTable: %x\n", HandleTable );
563
564 InitEngHandleTable();
565 }
566
567 /*!
568 * Creates a bunch of stock objects: brushes, pens, fonts.
569 */
570 VOID FASTCALL
571 CreateStockObjects(void)
572 {
573 unsigned Object;
574
575 DPRINT("Beginning creation of stock objects\n");
576
577 /* Create GDI Stock Objects from the logical structures we've defined */
578
579 StockObjects[WHITE_BRUSH] = IntGdiCreateBrushIndirect(&WhiteBrush);
580 StockObjects[LTGRAY_BRUSH] = IntGdiCreateBrushIndirect(&LtGrayBrush);
581 StockObjects[GRAY_BRUSH] = IntGdiCreateBrushIndirect(&GrayBrush);
582 StockObjects[DKGRAY_BRUSH] = IntGdiCreateBrushIndirect(&DkGrayBrush);
583 StockObjects[BLACK_BRUSH] = IntGdiCreateBrushIndirect(&BlackBrush);
584 StockObjects[NULL_BRUSH] = IntGdiCreateBrushIndirect(&NullBrush);
585
586 StockObjects[WHITE_PEN] = IntGdiCreatePenIndirect(&WhitePen);
587 StockObjects[BLACK_PEN] = IntGdiCreatePenIndirect(&BlackPen);
588 StockObjects[NULL_PEN] = IntGdiCreatePenIndirect(&NullPen);
589
590 (void) TextIntCreateFontIndirect(&OEMFixedFont, (HFONT*)&StockObjects[OEM_FIXED_FONT]);
591 (void) TextIntCreateFontIndirect(&AnsiFixedFont, (HFONT*)&StockObjects[ANSI_FIXED_FONT]);
592 (void) TextIntCreateFontIndirect(&SystemFont, (HFONT*)&StockObjects[SYSTEM_FONT]);
593 (void) TextIntCreateFontIndirect(&DeviceDefaultFont, (HFONT*)&StockObjects[DEVICE_DEFAULT_FONT]);
594 (void) TextIntCreateFontIndirect(&SystemFixedFont, (HFONT*)&StockObjects[SYSTEM_FIXED_FONT]);
595 (void) TextIntCreateFontIndirect(&DefaultGuiFont, (HFONT*)&StockObjects[DEFAULT_GUI_FONT]);
596
597 StockObjects[DEFAULT_PALETTE] = (HGDIOBJ*)PALETTE_Init();
598
599 for (Object = 0; Object < NB_STOCK_OBJECTS; Object++)
600 {
601 if (NULL != StockObjects[Object])
602 {
603 GDIOBJ_SetOwnership(StockObjects[Object], NULL);
604 /* GDI_HANDLE_SET_STOCKOBJ(StockObjects[Object]);*/
605 }
606 }
607
608 DPRINT("Completed creation of stock objects\n");
609 }
610
611 /*!
612 * Return stock object.
613 * \param Object - stock object id.
614 * \return Handle to the object.
615 */
616 HGDIOBJ STDCALL
617 NtGdiGetStockObject(INT Object)
618 {
619 DPRINT("NtGdiGetStockObject index %d\n", Object);
620
621 return ((Object < 0) || (NB_STOCK_OBJECTS <= Object)) ? NULL : StockObjects[Object];
622 }
623
624 /*!
625 * Delete GDI object
626 * \param hObject object handle
627 * \return if the function fails the returned value is FALSE.
628 */
629 BOOL STDCALL
630 NtGdiDeleteObject(HGDIOBJ hObject)
631 {
632 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
633
634 return NULL != hObject
635 ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_DEFAULT) : FALSE;
636 }
637
638 /*!
639 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
640 * \param Process - PID of the process that will be destroyed.
641 */
642 BOOL FASTCALL
643 CleanupForProcess (struct _EPROCESS *Process, INT Pid)
644 {
645 DWORD i;
646 PGDIOBJHDR objectHeader;
647 PEPROCESS CurrentProcess;
648
649 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
650 CurrentProcess = PsGetCurrentProcess();
651 if (CurrentProcess != Process)
652 {
653 KeAttachProcess(Process);
654 }
655
656 for(i = 1; i < HandleTable->wTableSize; i++)
657 {
658 objectHeader = GDIOBJ_iGetObjectForIndex(i);
659 if (NULL != objectHeader &&
660 (INT) objectHeader->hProcessId == Pid)
661 {
662 DPRINT("CleanupForProcess: %d, process: %d, locks: %d, magic: 0x%x", i, objectHeader->hProcessId, objectHeader->dwCount, objectHeader->Magic);
663 GDIOBJ_FreeObj(GDI_HANDLE_CREATE(i, GDI_MAGIC_TO_TYPE(objectHeader->Magic)),
664 GDI_MAGIC_TO_TYPE(objectHeader->Magic),
665 GDIOBJFLAG_IGNOREPID | GDIOBJFLAG_IGNORELOCK);
666 }
667 }
668
669 if (CurrentProcess != Process)
670 {
671 KeDetachProcess();
672 }
673
674 DPRINT("Completed cleanup for process %d\n", Pid);
675
676 return TRUE;
677 }
678
679 #define GDIOBJ_TRACKLOCKS
680
681 #ifdef GDIOBJ_LockObj
682 #undef GDIOBJ_LockObj
683 PGDIOBJ FASTCALL
684 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
685 {
686 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
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 #if 0
716 #ifdef NDEBUG
717 ExAcquireFastMutex(&ObjHdr->Lock);
718 #else /* NDEBUG */
719 if (! ExTryToAcquireFastMutex(&ObjHdr->Lock))
720 {
721 DPRINT1("Caution! GDIOBJ_LockObj trying to lock object 0x%x second time\n", hObj);
722 DPRINT1(" called from: %s:%i (thread %x)\n", file, line, KeGetCurrentThread());
723 if (NULL != ObjHdr->lockfile)
724 {
725 DPRINT1(" previously locked from: %s:%i (thread %x)\n", ObjHdr->lockfile, ObjHdr->lockline, ObjHdr->Lock.Owner);
726 }
727 ExAcquireFastMutex(&ObjHdr->Lock);
728 DPRINT1(" Disregard previous message about object 0x%x, it's ok\n", hObj);
729 }
730 #endif /* NDEBUG */
731 #else
732 if (ObjHdr->LockTid == (DWORD)PsGetCurrentThreadId())
733 {
734 InterlockedIncrement(&ObjHdr->LockCount);
735 }
736 else
737 {
738 for (;;)
739 {
740 if (InterlockedCompareExchange(&ObjHdr->LockTid, (DWORD)PsGetCurrentThreadId(), 0))
741 {
742 InterlockedIncrement(&ObjHdr->LockCount);
743 break;
744 }
745 /* FIXME: KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay); */
746 }
747 }
748 #endif
749
750 ExAcquireFastMutex(&RefCountHandling);
751 ObjHdr->dwCount++;
752 ExReleaseFastMutex(&RefCountHandling);
753
754 if (NULL == ObjHdr->lockfile)
755 {
756 ObjHdr->lockfile = file;
757 ObjHdr->lockline = line;
758 }
759
760 return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
761 }
762 #endif//GDIOBJ_LockObj
763
764 #ifdef GDIOBJ_UnlockObj
765 #undef GDIOBJ_UnlockObj
766 BOOL FASTCALL
767 GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
768 {
769 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
770
771 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
772 {
773 DPRINT1("GDIBOJ_UnlockObj failed for 0x%08x, reqtype 0x%08x\n",
774 hObj, ObjectType);
775 DPRINT1("\tcalled from: %s:%i\n", file, line);
776 return FALSE;
777 }
778 DPRINT("(%s:%i) GDIOBJ_UnlockObj(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
779 ObjHdr->lockfile = NULL;
780 ObjHdr->lockline = 0;
781
782 return GDIOBJ_UnlockObj(hObj, ObjectType);
783 }
784 #endif//GDIOBJ_LockObj
785
786 /*!
787 * Return pointer to the object by handle.
788 *
789 * \param hObj Object handle
790 * \param ObjectType one of the object types defined in \ref GDI object types
791 * \return Pointer to the object.
792 *
793 * \note Process can only get pointer to the objects it created or global objects.
794 *
795 * \todo Don't allow to lock the objects twice! Synchronization!
796 */
797 PGDIOBJ FASTCALL
798 GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ObjectType)
799 {
800 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
801
802 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
803 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
804 {
805 DPRINT1("GDIBOJ_LockObj failed for 0x%08x, type 0x%08x\n",
806 hObj, ObjectType);
807 return NULL;
808 }
809
810 #if 0
811 ExAcquireFastMutex(&ObjHdr->Lock);
812 #else
813 if (ObjHdr->LockTid == (DWORD)PsGetCurrentThreadId())
814 {
815 InterlockedIncrement(&ObjHdr->LockCount);
816 }
817 else
818 {
819 for (;;)
820 {
821 if (InterlockedCompareExchange(&ObjHdr->LockTid, (DWORD)PsGetCurrentThreadId(), 0))
822 {
823 InterlockedIncrement(&ObjHdr->LockCount);
824 break;
825 }
826 /* FIXME: KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay); */
827 }
828 }
829 #endif
830
831 ExAcquireFastMutex(&RefCountHandling);
832 ObjHdr->dwCount++;
833 ExReleaseFastMutex(&RefCountHandling);
834 return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
835 }
836
837 /*!
838 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
839 * as soon as you don't need to have access to it's data.
840
841 * \param hObj Object handle
842 * \param ObjectType one of the object types defined in \ref GDI object types
843 *
844 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
845 * then \em this function frees the object when reference count is zero.
846 *
847 * \todo Change synchronization algorithm.
848 */
849 #undef GDIOBJ_UnlockObj
850 BOOL FASTCALL
851 GDIOBJ_UnlockObj(HGDIOBJ hObj, DWORD ObjectType)
852 {
853 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
854
855 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
856 if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
857 {
858 DPRINT1( "GDIOBJ_UnLockObj: failed\n");
859 return FALSE;
860 }
861
862 #if 0
863 ExReleaseFastMutex(&ObjHdr->Lock);
864 #else
865 if (InterlockedDecrement(&ObjHdr->LockCount) == 0)
866 {
867 InterlockedExchange(&ObjHdr->LockTid, 0);
868 }
869 #endif
870
871 ExAcquireFastMutex(&RefCountHandling);
872 if (0 == (ObjHdr->dwCount & ~0x80000000))
873 {
874 ExReleaseFastMutex(&RefCountHandling);
875 DPRINT1( "GDIOBJ_UnLockObj: unlock object (0x%x) that is not locked\n", hObj );
876 return FALSE;
877 }
878
879 ObjHdr->dwCount--;
880
881 if (ObjHdr->dwCount == 0x80000000)
882 {
883 //delayed object release
884 ObjHdr->dwCount = 0;
885 ExReleaseFastMutex(&RefCountHandling);
886 DPRINT("GDIOBJ_UnlockObj: delayed delete\n");
887 return GDIOBJ_FreeObj(hObj, ObjectType, GDIOBJFLAG_DEFAULT);
888 }
889 ExReleaseFastMutex(&RefCountHandling);
890
891 return TRUE;
892 }
893
894 BOOL FASTCALL
895 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
896 {
897 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
898
899 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
900 ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
901
902 return ObjHdr->hProcessId == PsGetCurrentProcessId();
903 }
904
905 void FASTCALL
906 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
907 {
908 PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
909 PEPROCESS OldProcess;
910 PW32PROCESS W32Process;
911 NTSTATUS Status;
912
913 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
914 ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
915
916 if ((NULL == NewOwner && GDI_GLOBAL_PROCESS != ObjHdr->hProcessId)
917 || (NULL != NewOwner && ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId))
918 {
919 Status = PsLookupProcessByProcessId((PVOID)ObjHdr->hProcessId, &OldProcess);
920 if (NT_SUCCESS(Status))
921 {
922 W32Process = OldProcess->Win32Process;
923 if (W32Process)
924 {
925 W32Process->GDIObjects--;
926 }
927 ObDereferenceObject(OldProcess);
928 }
929 }
930
931 if (NULL == NewOwner)
932 {
933 ObjHdr->hProcessId = GDI_GLOBAL_PROCESS;
934 }
935 else if (ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId)
936 {
937 ObjHdr->hProcessId = (HANDLE) NewOwner->UniqueProcessId;
938 W32Process = NewOwner->Win32Process;
939 if (W32Process)
940 {
941 W32Process->GDIObjects++;
942 }
943 }
944 }
945
946 void FASTCALL
947 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
948 {
949 PGDIOBJHDR ObjHdrFrom = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyFrom));
950 PGDIOBJHDR ObjHdrTo = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyTo));
951 NTSTATUS Status;
952 PEPROCESS ProcessFrom;
953 PEPROCESS CurrentProcess;
954
955 ASSERT(NULL != ObjHdrFrom && NULL != ObjHdrTo);
956 if (NULL != ObjHdrFrom && NULL != ObjHdrTo
957 && ObjHdrTo->hProcessId != ObjHdrFrom->hProcessId)
958 {
959 if (ObjHdrFrom->hProcessId == GDI_GLOBAL_PROCESS)
960 {
961 GDIOBJ_SetOwnership(CopyTo, NULL);
962 }
963 else
964 {
965 /* Warning: ugly hack ahead
966 *
967 * During process cleanup, we can't call PsLookupProcessByProcessId
968 * for the current process, 'cause that function will try to
969 * reference the process, and since the process is closing down
970 * that will result in a bugcheck.
971 * So, instead, we call PsGetCurrentProcess, which doesn't reference
972 * the process. If the current process is indeed the one we're
973 * looking for, we use it, otherwise we can (safely) call
974 * PsLookupProcessByProcessId
975 */
976 CurrentProcess = PsGetCurrentProcess();
977 if (ObjHdrFrom->hProcessId == (HANDLE) CurrentProcess->UniqueProcessId)
978 {
979 GDIOBJ_SetOwnership(CopyTo, CurrentProcess);
980 }
981 else
982 {
983 Status = PsLookupProcessByProcessId((PVOID) ObjHdrFrom->hProcessId, &ProcessFrom);
984 if (NT_SUCCESS(Status))
985 {
986 GDIOBJ_SetOwnership(CopyTo, ProcessFrom);
987 ObDereferenceObject(ProcessFrom);
988 }
989 }
990 }
991 }
992 }
993
994 /* EOF */