HACK: hard-coded gdi handle quotas, excepting code paths I haven't found yet, reactos...
[reactos.git] / reactos / subsys / win32k / objects / gdiobj.c
index 187198b..7c5d364 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ReactOS W32 Subsystem
- *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
+ *  Copyright (C) 1998 - 2004 ReactOS Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 /*
  * GDIOBJ.C - GDI object manipulation routines
  *
- * $Id: gdiobj.c,v 1.27 2003/06/06 10:17:44 gvg Exp $
- *
+ * $Id: gdiobj.c,v 1.80 2004/12/19 00:03:56 royce Exp $
  */
+#include <w32k.h>
 
-#undef WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <ddk/ntddk.h>
-#include <include/dce.h>
-#include <include/object.h>
-#include <win32k/gdiobj.h>
-#include <win32k/brush.h>
-#include <win32k/pen.h>
-#include <win32k/text.h>
-#include <win32k/dc.h>
-#include <win32k/bitmaps.h>
-#include <win32k/region.h>
-#include <win32k/cursoricon.h>
-#include <include/palette.h>
 #define NDEBUG
-#include <win32k/debug1.h>
-
-//  GDI stock objects
-
-static LOGBRUSH WhiteBrush =
-{ BS_SOLID, RGB(255,255,255), 0 };
-
-static LOGBRUSH LtGrayBrush =
-/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
-{ BS_SOLID, RGB(192,192,192), 0 };
-
-static LOGBRUSH GrayBrush =
-/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
-{ BS_SOLID, RGB(128,128,128), 0 };
+#include <debug.h>
 
-static LOGBRUSH DkGrayBrush =
-/* This is BS_HATCHED, for 1 bitperpixel. This makes the spray work in pbrush */
-/* NB_HATCH_STYLES is an index into HatchBrushes */
-{ BS_HATCHED, RGB(0,0,0), NB_HATCH_STYLES };
+#ifdef __USE_W32API
+/* F*(&#$ header mess!!!! */
+HANDLE
+STDCALL PsGetProcessId(
+       PEPROCESS       Process
+       );
+#endif /* __USE_W32API */
 
-static LOGBRUSH BlackBrush =
-{ BS_SOLID, RGB(0,0,0), 0 };
 
-static LOGBRUSH NullBrush =
-{ BS_NULL, 0, 0 };
 
-static LOGPEN WhitePen =
-{ PS_SOLID, { 0, 0 }, RGB(255,255,255) };
 
-static LOGPEN BlackPen =
-{ PS_SOLID, { 0, 0 }, RGB(0,0,0) };
+#define GDI_ENTRY_TO_INDEX(ht, e)                                              \
+  (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
+#define GDI_HANDLE_GET_ENTRY(HandleTable, h)                                   \
+  (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
 
-static LOGPEN NullPen =
-{ PS_NULL, { 0, 0 }, 0 };
+#define GDIBdyToHdr(body)                                                      \
+  ((PGDIOBJHDR)(body) - 1)
+#define GDIHdrToBdy(hdr)                                                       \
+  (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
 
-static LOGFONTW OEMFixedFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, OEM_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
+/* apparently the first 10 entries are never used in windows as they are empty */
+#define RESERVE_ENTRIES_COUNT 10
 
-/* Filler to make the location counter dword aligned again.  This is necessary
-   since (a) LOGFONT is packed, (b) gcc places initialised variables in the code
-   segment, and (c) Solaris assembler is stupid.  */
-static UINT align_OEMFixedFont = 1;
-
-static LOGFONTW AnsiFixedFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
-
-static UINT align_AnsiFixedFont = 1;
+typedef struct _GDI_HANDLE_TABLE
+{
+  PPAGED_LOOKASIDE_LIST LookasideLists;
 
-static LOGFONTW AnsiVarFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" };
+  SLIST_HEADER FreeEntriesHead;
+  SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
+                          (sizeof(SLIST_ENTRY) << 3)];
 
-static UINT align_AnsiVarFont = 1;
+  GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
+} GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
 
-static LOGFONTW SystemFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"System" };
+typedef struct
+{
+  ULONG Type;
+  ULONG Size;
+  GDICLEANUPPROC CleanupProc;
+} GDI_OBJ_INFO, *PGDI_OBJ_INFO;
 
-static UINT align_SystemFont = 1;
+/*
+ * Dummy GDI Cleanup Callback
+ */
+BOOL INTERNAL_CALL
+GDI_CleanupDummy(PVOID ObjectBody)
+{
+  return TRUE;
+}
 
-static LOGFONTW DeviceDefaultFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"" };
+/* Testing shows that regions are the most used GDIObj type,
+   so put that one first for performance */
+const
+GDI_OBJ_INFO ObjInfo[] =
+{
+   /* Type */                   /* Size */             /* CleanupProc */
+  {GDI_OBJECT_TYPE_REGION,      sizeof(ROSRGNDATA),    RGNDATA_Cleanup},
+  {GDI_OBJECT_TYPE_BITMAP,      sizeof(BITMAPOBJ),     BITMAP_Cleanup},
+  {GDI_OBJECT_TYPE_DC,          sizeof(DC),            DC_Cleanup},
+  {GDI_OBJECT_TYPE_PALETTE,     sizeof(PALGDI),        PALETTE_Cleanup},
+  {GDI_OBJECT_TYPE_BRUSH,       sizeof(GDIBRUSHOBJ),   BRUSH_Cleanup},
+  {GDI_OBJECT_TYPE_PEN,         sizeof(GDIBRUSHOBJ),   GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_FONT,        sizeof(TEXTOBJ),       GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_DCE,         sizeof(DCE),           DCE_Cleanup},
+/*{GDI_OBJECT_TYPE_DIRECTDRAW,  sizeof(DD_DIRECTDRAW), DD_Cleanup},
+  {GDI_OBJECT_TYPE_DD_SURFACE,  sizeof(DD_SURFACE),    DDSURF_Cleanup},*/
+  {GDI_OBJECT_TYPE_EXTPEN,      0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_METADC,      0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_METAFILE,    0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_ENHMETAFILE, 0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_ENHMETADC,   0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_MEMDC,       0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_EMF,         0,                     GDI_CleanupDummy}
+};
+
+#define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
+
+static PGDI_HANDLE_TABLE HandleTable = NULL;
+static LARGE_INTEGER ShortDelay;
+
+#define DelayExecution() \
+  DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
+  KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
 
-static UINT align_DeviceDefaultFont = 1;
+/*!
+ * Allocate GDI object table.
+ * \param      Size - number of entries in the object table.
+*/
+static PGDI_HANDLE_TABLE INTERNAL_CALL
+GDIOBJ_iAllocHandleTable(VOID)
+{
+  PGDI_HANDLE_TABLE handleTable;
+  UINT ObjType;
+  UINT i;
+  PGDI_TABLE_ENTRY Entry;
 
-static LOGFONTW SystemFixedFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
+  handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
+  ASSERT( handleTable );
+  RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
+
+  /*
+   * initialize the free entry cache
+   */
+  InitializeSListHead(&handleTable->FreeEntriesHead);
+  Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
+  for(i = GDI_HANDLE_COUNT - 1; i >= RESERVE_ENTRIES_COUNT; i--)
+  {
+    InterlockedPushEntrySList(&handleTable->FreeEntriesHead, &handleTable->FreeEntries[i]);
+  }
 
-static UINT align_SystemFixedFont = 1;
+  handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
+                                                      OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
+                                                      TAG_GDIHNDTBLE);
+  if(handleTable->LookasideLists == NULL)
+  {
+    ExFreePool(handleTable);
+    return NULL;
+  }
 
-/* FIXME: Is this correct? */
-static LOGFONTW DefaultGuiFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" };
+  for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
+  {
+    ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
+                                   ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
+  }
 
-static UINT align_DefaultGuiFont = 1;
+  ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
 
-static HGDIOBJ *StockObjects[NB_STOCK_OBJECTS]; // we dont assign these statically as WINE does because we might redesign
-                                                // the way handles work, so it's more dynamic now
+  return handleTable;
+}
 
+static inline PPAGED_LOOKASIDE_LIST
+FindLookasideList(DWORD ObjectType)
+{
+  int Index;
 
-HBITMAP hPseudoStockBitmap; /*! 1x1 bitmap for memory DCs */
+  for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+  {
+    if (ObjInfo[Index].Type == ObjectType)
+    {
+      return HandleTable->LookasideLists + Index;
+    }
+  }
 
-static PGDI_HANDLE_TABLE  HandleTable = 0;
-static FAST_MUTEX  HandleTableMutex;
-static FAST_MUTEX  RefCountHandling;
+  DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
 
-/*! Size of the GDI handle table
- * per http://www.wd-mag.com/articles/1999/9902/9902b/9902b.htm?topic=articles
- * gdi handle table can hold 0x4000 handles
-*/
-#define GDI_HANDLE_NUMBER  0x4000
+  return NULL;
+}
 
-/*!
- * Allocate GDI object table.
- * \param      Size - number of entries in the object table.
-*/
-static PGDI_HANDLE_TABLE FASTCALL
-GDIOBJ_iAllocHandleTable (WORD Size)
+static inline BOOL
+RunCleanupCallback(PGDIOBJ pObj, DWORD ObjectType)
 {
-  PGDI_HANDLE_TABLE  handleTable;
-
-  ExAcquireFastMutexUnsafe (&HandleTableMutex);
-  handleTable = ExAllocatePool(PagedPool,
-                               sizeof (GDI_HANDLE_TABLE) +
-                                 sizeof (GDI_HANDLE_ENTRY) * Size);
-  ASSERT( handleTable );
-  memset (handleTable,
-          0,
-          sizeof (GDI_HANDLE_TABLE) + sizeof (GDI_HANDLE_ENTRY) * Size);
-  handleTable->wTableSize = Size;
-  ExReleaseFastMutexUnsafe (&HandleTableMutex);
+  int Index;
 
-  return  handleTable;
-}
+  for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+  {
+    if (ObjInfo[Index].Type == ObjectType)
+    {
+      return ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
+    }
+  }
 
-/*!
- * Returns the entry into the handle table by index.
-*/
-static PGDI_HANDLE_ENTRY FASTCALL
-GDIOBJ_iGetHandleEntryForIndex (WORD TableIndex)
-{
-  //DPRINT("GDIOBJ_iGetHandleEntryForIndex: TableIndex: %d,\n handle: %x, ptr: %x\n", TableIndex, HandleTable->Handles [TableIndex], &(HandleTable->Handles [TableIndex])  );
-  //DPRINT("GIG: HandleTable: %x, Handles: %x, \n TableIndex: %x, pt: %x\n", HandleTable,  HandleTable->Handles, TableIndex, ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex));
-  //DPRINT("GIG: Hndl: %x, mag: %x\n", ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex), ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex)->wMagic);
-  return  ((PGDI_HANDLE_ENTRY)HandleTable->Handles+TableIndex);
+  DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
+  return TRUE;
 }
 
-/*!
- * Finds next free entry in the GDI handle table.
- * \return     index into the table is successful, zero otherwise.
-*/
-static WORD FASTCALL
-GDIOBJ_iGetNextOpenHandleIndex (void)
+static inline ULONG
+GetObjectSize(DWORD ObjectType)
 {
-  WORD  tableIndex;
+  int Index;
 
-  ExAcquireFastMutexUnsafe (&HandleTableMutex);
-  for (tableIndex = 1; tableIndex < HandleTable->wTableSize; tableIndex++)
+  for (Index = 0; Index < OBJTYPE_COUNT; Index++)
   {
-    if (HandleTable->Handles [tableIndex].wMagic == 0)
+    if (ObjInfo[Index].Type == ObjectType)
     {
-      HandleTable->Handles [tableIndex].wMagic = GO_MAGIC_DONTCARE;
-      break;
+      return ObjInfo[Index].Size;
     }
   }
-  ExReleaseFastMutexUnsafe (&HandleTableMutex);
 
-  return  (tableIndex < HandleTable->wTableSize) ? tableIndex : 0;
+  DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
+  return 0;
+}
+
+#ifdef DBG
+
+static int leak_reported = 0;
+#define GDI_STACK_LEVELS 4
+static ULONG GDIHandleAllocator[GDI_STACK_LEVELS][GDI_HANDLE_COUNT];
+struct DbgOpenGDIHandle
+{
+       ULONG loc;
+       int count;
+};
+#define H 1024
+static struct DbgOpenGDIHandle h[H];
+
+void IntDumpHandleTable ( int which )
+{
+       int i, n = 0, j;
+
+       /*  step through GDI handle table and find out who our culprit is... */
+       for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
+       {
+               for ( j = 0; j < n; j++ )
+               {
+                       if ( GDIHandleAllocator[which][i] == h[j].loc )
+                               break;
+               }
+               if ( j < H )
+               {
+                       if ( j == n )
+                       {
+                               h[j].loc = GDIHandleAllocator[which][i];
+                               h[j].count = 1;
+                               n = n + 1;
+                       }
+                       else
+                               h[j].count++;
+               }
+       }
+       /* bubble sort time! weeeeee!! */
+       for ( i = 0; i < n-1; i++ )
+       {
+               if ( h[i].count < h[i+1].count )
+               {
+                       struct DbgOpenGDIHandle t;
+                       t.loc = h[i+1].loc;
+                       t.count = h[i+1].count;
+                       h[i+1].loc = h[i].loc;
+                       h[i+1].count = h[i].count;
+                       j = i;
+                       while ( j > 0 && h[j-1].count < t.count )
+                               j--;
+                       h[j] = t;
+               }
+       }
+       /* print the first 30 offenders... */
+       DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which, n );
+       for ( i = 0; i < 30 && i < n; i++ )
+       {
+               DbgPrint ( "\t" );
+               if ( !KeRosPrintAddress ( (PVOID)h[i].loc ) )
+                       DbgPrint ( "<%X>", h[i].loc );
+               DbgPrint ( " (%i allocations)\n", h[i].count );
+       }
 }
+#endif /* DBG */
 
 /*!
  * Allocate memory for GDI object and return handle to it.
  *
- * \param Size - size of the GDI object. This shouldn't to include the size of GDIOBJHDR.
- * The actual amount of allocated memory is sizeof(GDIOBJHDR)+Size
- * \param Magic - object magic (see GDI Magic)
+ * \param ObjectType - type of object \ref GDI object types
  *
  * \return Handle of the allocated object.
  *
  * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
+ * \todo return the object pointer and lock it by default.
 */
-HGDIOBJ FASTCALL GDIOBJ_AllocObj(WORD Size, WORD Magic)
+HGDIOBJ INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_AllocObj(ULONG ObjectType)
+#endif /* GDI_DEBUG */
 {
-       PGDIOBJHDR  newObject;
-       PGDI_HANDLE_ENTRY  handleEntry;
-
-       DPRINT("GDIOBJ_AllocObj: size: %d, magic: %x\n", Size, Magic);
-       newObject = ExAllocatePool (PagedPool, Size + sizeof (GDIOBJHDR));
-       if (newObject == NULL)
-       {
-         DPRINT("GDIOBJ_AllocObj: failed\n");
-         return  NULL;
-       }
-       RtlZeroMemory (newObject, Size + sizeof (GDIOBJHDR));
-
-       newObject->wTableIndex = GDIOBJ_iGetNextOpenHandleIndex ();
-       newObject->dwCount = 0;
-       handleEntry = GDIOBJ_iGetHandleEntryForIndex (newObject->wTableIndex);
-       handleEntry->wMagic = Magic;
-       handleEntry->hProcessId = PsGetCurrentProcessId ();
-       handleEntry->pObject = newObject;
-       DPRINT("GDIOBJ_AllocObj: object handle %d\n", newObject->wTableIndex );
-       return  (HGDIOBJ) newObject->wTableIndex;
+  PW32PROCESS W32Process;
+  PGDIOBJHDR  newObject;
+  PPAGED_LOOKASIDE_LIST LookasideList;
+  LONG CurrentProcessId, LockedProcessId;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  W32Process = PsGetWin32Process();
+  /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
+     to take too many GDI objects, itself. */
+  if ( W32Process && W32Process->GDIObjects >= 0x2710 )
+    return NULL;
+
+  ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
+
+  LookasideList = FindLookasideList(ObjectType);
+  if(LookasideList != NULL)
+  {
+    newObject = ExAllocateFromPagedLookasideList(LookasideList);
+    if(newObject != NULL)
+    {
+      PSLIST_ENTRY FreeEntry;
+      PGDI_TABLE_ENTRY Entry;
+      PGDIOBJ ObjectBody;
+      LONG TypeInfo;
+
+      /* shift the process id to the left so we can use the first bit to lock
+         the object.
+         FIXME - don't shift once ROS' PIDs match with nt! */
+      CurrentProcessId = (LONG)PsGetCurrentProcessId() << 1;
+      LockedProcessId = CurrentProcessId | 0x1;
+
+      newObject->LockingThread = NULL;
+      newObject->Locks = 0;
+
+#ifdef GDI_DEBUG
+      newObject->createdfile = file;
+      newObject->createdline = line;
+      newObject->lockfile = NULL;
+      newObject->lockline = 0;
+#endif
+
+      ObjectBody = GDIHdrToBdy(newObject);
+
+      RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
+
+      TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
+
+      FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
+      if(FreeEntry != NULL)
+      {
+        LONG PrevProcId;
+        UINT Index;
+        HGDIOBJ Handle;
+
+        /* calculate the entry from the address of the entry in the free slot array */
+        Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
+                sizeof(HandleTable->FreeEntries[0]);
+        Entry = &HandleTable->Entries[Index];
+        Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
+
+LockHandle:
+        PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
+        if(PrevProcId == 0)
+        {
+          ASSERT(Entry->KernelData == NULL);
+
+          Entry->KernelData = ObjectBody;
+
+          /* we found a free entry, no need to exchange this field atomically
+             since we're holding the lock */
+          Entry->Type = TypeInfo;
+
+          /* unlock the entry */
+          InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
+
+#ifdef DBG
+          {
+            PULONG Frame;
+                       int which;
+#if defined __GNUC__
+            __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
+#elif defined(_MSC_VER)
+            __asm mov [Frame], ebp
+#endif
+                       Frame = (PULONG)Frame[0]; /* step out of AllocObj() */
+                       for ( which = 0; which < GDI_STACK_LEVELS && Frame[1] != 0 && Frame[1] != 0xDEADBEEF; which++ )
+                       {
+                   GDIHandleAllocator[which][Index] = Frame[1]; /* step out of AllocObj() */
+                               Frame = ((PULONG)Frame[0]);
+                       }
+                       for ( ; which < GDI_STACK_LEVELS; which++ )
+                               GDIHandleAllocator[which][Index] = 0xDEADBEEF;
+          }
+#endif /* DBG */
+
+          if(W32Process != NULL)
+          {
+            InterlockedIncrement(&W32Process->GDIObjects);
+          }
+
+          DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
+          return Handle;
+        }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            DPRINT1("[%d]Waiting on 0x%x\n", Attempts, Handle);
+          }
+#endif
+          /* damn, someone is trying to lock the object even though it doesn't
+             eve nexist anymore, wait a little and try again!
+             FIXME - we shouldn't loop forever! Give up after some time! */
+          DelayExecution();
+          /* try again */
+          goto LockHandle;
+        }
+      }
+
+      ExFreeToPagedLookasideList(LookasideList, newObject);
+      DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
+#ifdef DBG
+         if ( !leak_reported )
+         {
+                 DPRINT1("reporting gdi handle abusers:\n");
+                 int which;
+                 for ( which = 0; which < GDI_STACK_LEVELS; which++ )
+                     IntDumpHandleTable(which);
+                 leak_reported = 1;
+         }
+         else
+         {
+                 DPRINT1("gdi handle abusers already reported!\n");
+         }
+#endif /* DBG */
+    }
+    else
+    {
+      DPRINT1("Not enough memory to allocate gdi object!\n");
+    }
+  }
+  else
+  {
+    DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
+  }
+  return NULL;
 }
 
 /*!
  * Free memory allocated for the GDI object. For each object type this function calls the
  * appropriate cleanup routine.
  *
- * \param hObj - handle of the object to be deleted.
- * \param Magic - object magic or GO_MAGIC_DONTCARE.
- * \param Flag - if set to GDIOBJFLAG_IGNOREPID then the routine doesn't check if the process that
- * tries to delete the object is the same one that created it.
+ * \param hObj       - handle of the object to be deleted.
  *
  * \return Returns TRUE if succesful.
- *
- * \note You should only use GDIOBJFLAG_IGNOREPID if you are cleaning up after the process that terminated.
- * \note This function deferres object deletion if it is still in use.
+ * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
+ * to the calling process.
 */
-BOOL STDCALL GDIOBJ_FreeObj(HGDIOBJ hObj, WORD Magic, DWORD Flag)
+BOOL INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_FreeObjDbg(const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
+#endif /* GDI_DEBUG */
 {
-       PGDIOBJHDR  objectHeader;
-       PGDI_HANDLE_ENTRY  handleEntry;
-       PGDIOBJ         Obj;
-       BOOL    bRet = TRUE;
-
-       handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD)hObj & 0xffff);
-       DPRINT("GDIOBJ_FreeObj: hObj: %d, magic: %x, handleEntry: %x\n", (WORD)hObj & 0xffff, Magic, handleEntry );
+  PGDI_TABLE_ENTRY Entry;
+  PPAGED_LOOKASIDE_LIST LookasideList;
+  LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
 
-       if (handleEntry == 0 || (handleEntry->wMagic != Magic && Magic != GO_MAGIC_DONTCARE )
-            || ((handleEntry->hProcessId != PsGetCurrentProcessId()) && !(Flag & GDIOBJFLAG_IGNOREPID))){
+  DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
 
-         DPRINT("Can't Delete hObj: %d, magic: %x, pid:%d\n currpid:%d, flag:%d, hmm:%d\n",(WORD)hObj & 0xffff, handleEntry->wMagic, handleEntry->hProcessId, PsGetCurrentProcessId(), (Flag&GDIOBJFLAG_IGNOREPID), ((handleEntry->hProcessId != PsGetCurrentProcessId()) && !(Flag&GDIOBJFLAG_IGNOREPID)) );
-         return  FALSE;
-       }
-
-       objectHeader = (PGDIOBJHDR) handleEntry->pObject;
-       ASSERT(objectHeader);
-       DPRINT("FreeObj: locks: %x\n", objectHeader->dwCount );
-       if( !(Flag & GDIOBJFLAG_IGNORELOCK) ){
-               // check that the reference count is zero. if not then set flag
-               // and delete object when releaseobj is called
-               ExAcquireFastMutex(&RefCountHandling);
-               if( ( objectHeader->dwCount & ~0x80000000 ) > 0 ){
-                       DPRINT("GDIOBJ_FreeObj: delayed object deletion: count %d\n", objectHeader->dwCount);
-                       objectHeader->dwCount |= 0x80000000;
-                       ExReleaseFastMutex(&RefCountHandling);
-                       return TRUE;
-               }
-               ExReleaseFastMutex(&RefCountHandling);
-       }
+  if(GDI_HANDLE_IS_STOCKOBJ(hObj))
+  {
+    DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
+#ifdef GDI_DEBUG
+    DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+    return FALSE;
+  }
 
-       //allow object to delete internal data
-       Obj = (PGDIOBJ)((PCHAR)handleEntry->pObject + sizeof(GDIOBJHDR));
-       switch( handleEntry->wMagic ){
-               case GO_REGION_MAGIC:
-                       bRet = RGNDATA_InternalDelete( (PROSRGNDATA) Obj );
-                       break;
-               case GO_BITMAP_MAGIC:
-                       bRet = Bitmap_InternalDelete( (PBITMAPOBJ) Obj );
-                       break;
-               case GO_DC_MAGIC:
-                       bRet = DC_InternalDeleteDC( (PDC) Obj );
-                       break;
-               case GO_PEN_MAGIC:
-               case GO_PALETTE_MAGIC:
-               case GO_DISABLED_DC_MAGIC:
-               case GO_META_DC_MAGIC:
-               case GO_METAFILE_MAGIC:
-               case GO_METAFILE_DC_MAGIC:
-               case GO_ENHMETAFILE_MAGIC:
-               case GO_ENHMETAFILE_DC_MAGIC:
-
-               case GO_BRUSH_MAGIC:
-               case GO_FONT_MAGIC:
-                       break;
-               case GO_DCE_MAGIC:
-                       bRet = DCE_InternalDelete( (PDCE) Obj );
-                       break;
-               case GO_ICONCURSOR_MAGIC:
-                       bRet = IconCursor_InternalDelete( (PICONCURSOROBJ) Obj );
-                       break;
-       }
-       handleEntry->hProcessId = 0;
-       ExFreePool (handleEntry->pObject);
-       handleEntry->pObject = 0;
-       handleEntry->wMagic = 0;
+  /* shift the process id to the left so we can use the first bit to lock the object.
+     FIXME - don't shift once ROS' PIDs match with nt! */
+  ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+  LockedProcessId = ProcessId | 0x1;
 
-       return  TRUE;
-}
+  ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
 
-/*!
- * Return pointer to the object by handle.
- *
- * \param hObj                 Object handle
- * \param Magic                one of the magic numbers defined in \ref GDI Magic
- * \return                     Pointer to the object.
- *
- * \note Process can only get pointer to the objects it created or global objects.
- *
- * \todo Don't allow to lock the objects twice! Synchronization!
-*/
-PGDIOBJ FASTCALL GDIOBJ_LockObj( HGDIOBJ hObj, WORD Magic )
-{
-       PGDI_HANDLE_ENTRY handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD) hObj & 0xffff);
-       PGDIOBJHDR  objectHeader;
-
-       DPRINT("GDIOBJ_LockObj: hObj: %d, magic: %x, \n handleEntry: %x, mag %x\n", hObj, Magic, handleEntry, handleEntry->wMagic);
-       if (handleEntry == 0 || (handleEntry->wMagic != Magic && Magic != GO_MAGIC_DONTCARE )
-            || (handleEntry->hProcessId != (HANDLE)0xFFFFFFFF &&
-                handleEntry->hProcessId != PsGetCurrentProcessId ())){
-         DPRINT("GDIBOJ_LockObj failed for %d, magic: %d, reqMagic\n",(WORD) hObj & 0xffff, handleEntry->wMagic, Magic);
-         return  NULL;
-       }
+  Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
 
-       objectHeader = (PGDIOBJHDR) handleEntry->pObject;
-       ASSERT(objectHeader);
-       if( objectHeader->dwCount > 0 ){
-               DbgPrint("Caution! GDIOBJ_LockObj trying to lock object second time\n" );
-               DbgPrint("\t called from: %x\n", __builtin_return_address(0));
-       }
+LockHandle:
+  /* lock the object, we must not delete global objects, so don't exchange the locking
+     process ID to zero when attempting to lock a global object... */
+  PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+  if(PrevProcId == ProcessId)
+  {
+    if(Entry->Type != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)))
+    {
+      PGDIOBJHDR GdiHdr;
+
+      GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+      if(GdiHdr->LockingThread == NULL)
+      {
+        BOOL Ret;
+        PW32PROCESS W32Process = PsGetWin32Process();
+        ULONG Type = Entry->Type << 16;
+
+        /* Clear the type field so when unlocking the handle it gets finally deleted */
+        Entry->Type = 0;
+        Entry->KernelData = NULL;
+
+        /* unlock the handle slot */
+        InterlockedExchange(&Entry->ProcessId, 0);
+
+        /* push this entry to the free list */
+        InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+                                  &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
+        if(W32Process != NULL)
+        {
+          InterlockedDecrement(&W32Process->GDIObjects);
+        }
+
+        /* call the cleanup routine. */
+        Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
+
+        /* Now it's time to free the memory */
+        LookasideList = FindLookasideList(Type);
+        if(LookasideList != NULL)
+        {
+          ExFreeToPagedLookasideList(LookasideList, GdiHdr);
+        }
+
+        return Ret;
+      }
+      else
+      {
+        /* the object is currently locked. just clear the type field so when the
+           object gets unlocked it will be finally deleted from the table. */
+        Entry->Type = 0;
+
+        /* unlock the handle slot */
+        InterlockedExchange(&Entry->ProcessId, 0);
+
+        /* report a successful deletion as the object is actually removed from the table */
+        return TRUE;
+      }
+    }
+    else
+    {
+      if(Entry->Type != 0)
+      {
+        DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
+      }
+      else
+      {
+        DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
+      }
+      InterlockedExchange(&Entry->ProcessId, PrevProcId);
+    }
+  }
+  else if(PrevProcId == LockedProcessId)
+  {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
+    }
+#endif
+    /* the object is currently locked, wait some time and try again.
+       FIXME - we shouldn't loop forever! Give up after some time! */
+    DelayExecution();
+    /* try again */
+    goto LockHandle;
+  }
+  else
+  {
+    if((PrevProcId >> 1) == 0)
+    {
+      DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj);
+    }
+    else
+    {
+      DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, PrevProcId >> 1, ProcessId >> 1);
+    }
+#ifdef GDI_DEBUG
+    DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+  }
 
-       ExAcquireFastMutex(&RefCountHandling);
-       objectHeader->dwCount++;
-       ExReleaseFastMutex(&RefCountHandling);
-       return (PGDIOBJ)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
+  return FALSE;
 }
 
 /*!
  * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
  * duplicates. You should use this function to avoid trying to lock the same object twice!
  *
- * \param      pList   pointer to the list that contains handles to the objects. You should set hObj and Magic fields.
+ * \param      pList   pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
  * \param      nObj    number of objects to lock
  * \return     for each entry in pList this function sets pObj field to point to the object.
  *
  * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
 */
-BOOL FASTCALL GDIOBJ_LockMultipleObj( PGDIMULTILOCK pList, INT nObj )
+BOOL INTERNAL_CALL
+GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
 {
-       INT i, j;
-       ASSERT( pList );
-       //go through the list checking for duplicate objects
-       for( i = 0; i < nObj; i++ ){
-               (pList+i)->pObj = NULL;
-               for( j = 0; j < i; j++ ){
-                       if( ((pList+i)->hObj == (pList+j)->hObj)
-                         && ((pList+i)->Magic == (pList+j)->Magic) ){
-                               //already locked, so just copy the pointer to the object
-                               (pList+i)->pObj = (pList+j)->pObj;
-                               break;
-                       }
-               }
-               if( (pList+i)->pObj == NULL ){
-                       //object hasn't been locked, so lock it.
-                       (pList+i)->pObj = GDIOBJ_LockObj( (pList+i)->hObj, (pList+i)->Magic );
-               }
-       }
-       return TRUE;
-}
-
-/*!
- * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
- * as soon as you don't need to have access to it's data.
-
- * \param hObj                 Object handle
- * \param Magic                one of the magic numbers defined in \ref GDI Magic
- *
- * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
- * then \em this function frees the object when reference count is zero.
- *
- * \todo Change synchronization algorithm.
-*/
-BOOL FASTCALL GDIOBJ_UnlockObj( HGDIOBJ hObj, WORD Magic )
-{
-       PGDI_HANDLE_ENTRY handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD) hObj & 0xffff);
-       PGDIOBJHDR  objectHeader;
-
-       DPRINT("GDIOBJ_UnlockObj: hObj: %d, magic: %x, \n handleEntry: %x\n", hObj, Magic, handleEntry);
-       if (handleEntry == 0 || (handleEntry->wMagic != Magic && Magic != GO_MAGIC_DONTCARE )
-             || (handleEntry->hProcessId != (HANDLE)0xFFFFFFFF &&
-                handleEntry->hProcessId != PsGetCurrentProcessId ())){
-         DPRINT( "GDIOBJ_UnLockObj: failed\n");
-         return  FALSE;
+  INT i, j;
+  ASSERT( pList );
+  /* FIXME - check for "invalid" handles */
+  /* go through the list checking for duplicate objects */
+  for (i = 0; i < nObj; i++)
+    {
+      pList[i].pObj = NULL;
+      for (j = 0; j < i; j++)
+       {
+         if (pList[i].hObj == pList[j].hObj)
+           {
+             /* already locked, so just copy the pointer to the object */
+             pList[i].pObj = pList[j].pObj;
+             break;
+           }
        }
 
-       objectHeader = (PGDIOBJHDR) handleEntry->pObject;
-       ASSERT(objectHeader);
-
-       ExAcquireFastMutex(&RefCountHandling);
-       if( ( objectHeader->dwCount & ~0x80000000 ) == 0 ){
-               ExReleaseFastMutex(&RefCountHandling);
-               DPRINT( "GDIOBJ_UnLockObj: unlock object that is not locked\n" );
-               return FALSE;
+      if (NULL == pList[i].pObj)
+       {
+         /* object hasn't been locked, so lock it. */
+         if (NULL != pList[i].hObj)
+           {
+             pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
+           }
        }
+    }
 
-       objectHeader = (PGDIOBJHDR) handleEntry->pObject;
-       ASSERT(objectHeader);
-       objectHeader->dwCount--;
-
-       if( objectHeader->dwCount  == 0x80000000 ){
-               //delayed object release
-               objectHeader->dwCount = 0;
-               ExReleaseFastMutex(&RefCountHandling);
-               DPRINT("GDIOBJ_UnlockObj: delayed delete\n");
-               return GDIOBJ_FreeObj( hObj, Magic, GDIOBJFLAG_DEFAULT );
-       }
-       ExReleaseFastMutex(&RefCountHandling);
-       return TRUE;
+  return TRUE;
 }
 
-
 /*!
  * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
  * duplicates.
  *
- * \param      pList   pointer to the list that contains handles to the objects. You should set hObj and Magic fields.
+ * \param      pList   pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
  * \param      nObj    number of objects to lock
  *
  * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
 */
-BOOL FASTCALL GDIOBJ_UnlockMultipleObj( PGDIMULTILOCK pList, INT nObj )
+BOOL INTERNAL_CALL
+GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
 {
-       INT i, j;
-       ASSERT( pList );
-       //go through the list checking for duplicate objects
-       for( i = 0; i < nObj; i++ ){
-               if( (pList+i)->pObj != NULL ){
-                       for( j = i+1; j < nObj; j++ ){
-                               if( ((pList+i)->pObj == (pList+j)->pObj) ){
-                                       //set the pointer to zero for all duplicates
-                                       (pList+j)->pObj = NULL;
-                               }
-                       }
-                       GDIOBJ_UnlockObj( (pList+i)->hObj, (pList+i)->Magic );
-                       (pList+i)->pObj = NULL;
+  INT i, j;
+  ASSERT(pList);
+
+  /* go through the list checking for duplicate objects */
+  for (i = 0; i < nObj; i++)
+    {
+      if (NULL != pList[i].pObj)
+       {
+         for (j = i + 1; j < nObj; j++)
+           {
+             if ((pList[i].pObj == pList[j].pObj))
+               {
+                 /* set the pointer to zero for all duplicates */
+                 pList[j].pObj = NULL;
                }
+           }
+         GDIOBJ_UnlockObj(pList[i].hObj);
+         pList[i].pObj = NULL;
        }
-       return TRUE;
+    }
+
+  return TRUE;
 }
 
 /*!
- * Marks the object as global. (Creator process ID is set to 0xFFFFFFFF). Global objects may be
- * accessed by any process.
- * \param      ObjectHandle - handle of the object to make global.
- *
- * \note       Only stock objects should be marked global.
+ * Initialization of the GDI object engine.
 */
-VOID FASTCALL GDIOBJ_MarkObjectGlobal(HGDIOBJ ObjectHandle)
+VOID INTERNAL_CALL
+InitGdiObjectHandleTable (VOID)
 {
-  PGDI_HANDLE_ENTRY  handleEntry;
+  DPRINT("InitGdiObjectHandleTable\n");
 
-  if (ObjectHandle == NULL)
-    return;
+  HandleTable = GDIOBJ_iAllocHandleTable();
+  DPRINT("HandleTable: %x\n", HandleTable);
+}
 
-  handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD)ObjectHandle & 0xffff);
-  if (handleEntry == 0)
-    return;
+/*!
+ * Delete GDI object
+ * \param      hObject object handle
+ * \return     if the function fails the returned value is FALSE.
+*/
+BOOL STDCALL
+NtGdiDeleteObject(HGDIOBJ hObject)
+{
+  DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
 
-  handleEntry->hProcessId = (HANDLE)0xFFFFFFFF;
+  return NULL != hObject
+         ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
 }
 
 /*!
- * Get the type (magic value) of the object.
- * \param      ObjectHandle - handle of the object.
- * \return     GDI Magic value.
+ * Internal function. Called when the process is destroyed to free the remaining GDI handles.
+ * \param      Process - PID of the process that will be destroyed.
 */
-WORD FASTCALL GDIOBJ_GetHandleMagic (HGDIOBJ ObjectHandle)
+BOOL INTERNAL_CALL
+GDI_CleanupForProcess (struct _EPROCESS *Process)
 {
-  PGDI_HANDLE_ENTRY  handleEntry;
+  PGDI_TABLE_ENTRY Entry, End;
+  PEPROCESS CurrentProcess;
+  PW32PROCESS W32Process;
+  LONG ProcId;
+  ULONG Index = RESERVE_ENTRIES_COUNT;
+
+  DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
+  CurrentProcess = PsGetCurrentProcess();
+  if (CurrentProcess != Process)
+    {
+      KeAttachProcess(Process);
+    }
+  W32Process = Process->Win32Process;
+  ASSERT(W32Process);
 
-  if (ObjectHandle == NULL)
-    return  0;
+  if(W32Process->GDIObjects > 0)
+  {
+    /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
+               we should delete it directly here! */
+    ProcId = ((LONG)Process->UniqueProcessId << 1);
+
+    End = &HandleTable->Entries[GDI_HANDLE_COUNT];
+    for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
+        Entry < End;
+        Entry++, Index++)
+    {
+      /* ignore the lock bit */
+      if((Entry->ProcessId & ~0x1) == ProcId && Entry->Type != 0)
+      {
+        HGDIOBJ ObjectHandle;
+
+        /* Create the object handle for the entry, the upper 16 bit of the
+           Type field includes the type of the object including the stock
+           object flag - but since stock objects don't have a process id we can
+           simply ignore this fact here. */
+        ObjectHandle = (HGDIOBJ)(Index | (Entry->Type & 0xFFFF0000));
+
+        if(GDIOBJ_FreeObj(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
+           W32Process->GDIObjects == 0)
+        {
+          /* there are no more gdi handles for this process, bail */
+          break;
+        }
+      }
+    }
+  }
 
-  handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD)ObjectHandle & 0xffff);
-  if (handleEntry == 0 ||
-      (handleEntry->hProcessId != (HANDLE)0xFFFFFFFF &&
-       handleEntry->hProcessId != PsGetCurrentProcessId ()))
-    return  0;
+  if (CurrentProcess != Process)
+    {
+      KeDetachProcess();
+    }
 
-  return  handleEntry->wMagic;
+  DPRINT("Completed cleanup for process %d\n", Pid);
+
+  return TRUE;
 }
 
 /*!
- * Initialization of the GDI object engine.
+ * Return pointer to the object by handle.
+ *
+ * \param hObj                 Object handle
+ * \return             Pointer to the object.
+ *
+ * \note Process can only get pointer to the objects it created or global objects.
+ *
+ * \todo Get rid of the ObjectType parameter!
 */
-VOID FASTCALL
-InitGdiObjectHandleTable (VOID)
+PGDIOBJ INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
+#endif /* GDI_DEBUG */
 {
-  DPRINT ("InitGdiObjectHandleTable\n");
-  ExInitializeFastMutex (&HandleTableMutex);
-  ExInitializeFastMutex (&RefCountHandling);
+  PGDI_TABLE_ENTRY Entry;
+  PETHREAD Thread;
+  LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
+
+  Thread = PsGetCurrentThread();
 
-  HandleTable = GDIOBJ_iAllocHandleTable (GDI_HANDLE_NUMBER);
-  DPRINT("HandleTable: %x\n", HandleTable );
+  /* shift the process id to the left so we can use the first bit to lock the object.
+     FIXME - don't shift once ROS' PIDs match with nt! */
+  ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+  LockedProcessId = ProcessId | 0x1;
 
-  InitEngHandleTable();
+  ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
+
+  Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
+
+LockHandle:
+  /* lock the object, we must not delete stock objects, so don't check!!! */
+  PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+  if(PrevProcId == ProcessId)
+  {
+    LONG EntryType = Entry->Type << 16;
+
+    /* we're locking an object that belongs to our process or it's a global
+       object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
+       failed to lock the object and it turned out to be a global object. */
+    if(EntryType != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || (EntryType == ExpectedType)))
+    {
+      PETHREAD PrevThread;
+      PGDIOBJHDR GdiHdr;
+
+      GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+      /* save the pointer to the calling thread so we know it was this thread
+         that locked the object. There's no need to do this atomically as we're
+         holding the lock of the handle slot, but this way it's easier ;) */
+      PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
+
+      if(PrevThread == NULL || PrevThread == Thread)
+      {
+        if(++GdiHdr->Locks == 1)
+        {
+#ifdef GDI_DEBUG
+          GdiHdr->lockfile = file;
+          GdiHdr->lockline = line;
+#endif
+        }
+
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+        /* we're done, return the object body */
+        return GDIHdrToBdy(GdiHdr);
+      }
+      else
+      {
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+#ifdef GDI_DEBUG
+        if(++Attempts > 20)
+        {
+          DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
+        }
+#endif
+
+        DelayExecution();
+        goto LockHandle;
+      }
+    }
+    else
+    {
+      InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+      if(EntryType == 0)
+      {
+        DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
+        KeRosDumpStackFrames ( NULL, 20 );
+      }
+      else
+      {
+        DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
+        KeRosDumpStackFrames ( NULL, 20 );
+      }
+#ifdef GDI_DEBUG
+      DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+    }
+  }
+  else if(PrevProcId == LockedProcessId)
+  {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
+    }
+#endif
+    /* the handle is currently locked, wait some time and try again.
+       FIXME - we shouldn't loop forever! Give up after some time! */
+    DelayExecution();
+    /* try again */
+    goto LockHandle;
+  }
+  else if((PrevProcId & ~0x1) == 0)
+  {
+    /* we're trying to lock a global object, change the ProcessId to 0 and try again */
+    ProcessId = 0x0;
+    LockedProcessId = ProcessId |0x1;
+
+    goto LockHandle;
+  }
+  else
+  {
+    DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
+    KeRosDumpStackFrames ( NULL, 20 );
+#ifdef GDI_DEBUG
+    DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+  }
+
+  return NULL;
 }
 
+
 /*!
- * Creates a bunch of stock objects: brushes, pens, fonts.
+ * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
+ * as soon as you don't need to have access to it's data.
+
+ * \param hObj                 Object handle
+ *
+ * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
+ * then \em this function frees the object when reference count is zero.
 */
-VOID FASTCALL CreateStockObjects(void)
+BOOL INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj)
+#else /* !GDI_DEBUG */
+GDIOBJ_UnlockObj (HGDIOBJ hObj)
+#endif /* GDI_DEBUG */
 {
-  // Create GDI Stock Objects from the logical structures we've defined
-
-  StockObjects[WHITE_BRUSH] =  W32kCreateBrushIndirect(&WhiteBrush);
-  GDIOBJ_MarkObjectGlobal(StockObjects[WHITE_BRUSH]);
-  StockObjects[LTGRAY_BRUSH] = W32kCreateBrushIndirect(&LtGrayBrush);
-  GDIOBJ_MarkObjectGlobal(StockObjects[LTGRAY_BRUSH]);
-  StockObjects[GRAY_BRUSH] =   W32kCreateBrushIndirect(&GrayBrush);
-  GDIOBJ_MarkObjectGlobal(StockObjects[GRAY_BRUSH]);
-  StockObjects[DKGRAY_BRUSH] = W32kCreateBrushIndirect(&DkGrayBrush);
-  GDIOBJ_MarkObjectGlobal(StockObjects[DKGRAY_BRUSH]);
-  StockObjects[BLACK_BRUSH] =  W32kCreateBrushIndirect(&BlackBrush);
-  GDIOBJ_MarkObjectGlobal(StockObjects[BLACK_BRUSH]);
-  StockObjects[NULL_BRUSH] =   W32kCreateBrushIndirect(&NullBrush);
-  GDIOBJ_MarkObjectGlobal(StockObjects[NULL_BRUSH]);
-
-  StockObjects[WHITE_PEN] = W32kCreatePenIndirect(&WhitePen);
-  GDIOBJ_MarkObjectGlobal(StockObjects[WHITE_PEN]);
-  StockObjects[BLACK_PEN] = W32kCreatePenIndirect(&BlackPen);
-  GDIOBJ_MarkObjectGlobal(StockObjects[BLACK_PEN]);
-  StockObjects[NULL_PEN] =  W32kCreatePenIndirect(&NullPen);
-  GDIOBJ_MarkObjectGlobal(StockObjects[NULL_PEN]);
-
-  (void) TextIntCreateFontIndirect(&OEMFixedFont, &StockObjects[OEM_FIXED_FONT]);
-  GDIOBJ_MarkObjectGlobal(StockObjects[OEM_FIXED_FONT]);
-  (void) TextIntCreateFontIndirect(&AnsiFixedFont, &StockObjects[ANSI_FIXED_FONT]);
-  GDIOBJ_MarkObjectGlobal(StockObjects[ANSI_FIXED_FONT]);
-  (void) TextIntCreateFontIndirect(&SystemFont, &StockObjects[SYSTEM_FONT]);
-  GDIOBJ_MarkObjectGlobal(StockObjects[SYSTEM_FONT]);
-  (void) TextIntCreateFontIndirect(&DeviceDefaultFont, &StockObjects[DEVICE_DEFAULT_FONT]);
-  GDIOBJ_MarkObjectGlobal(StockObjects[DEVICE_DEFAULT_FONT]);
-  (void) TextIntCreateFontIndirect(&SystemFixedFont, &StockObjects[SYSTEM_FIXED_FONT]);
-  GDIOBJ_MarkObjectGlobal(StockObjects[SYSTEM_FIXED_FONT]);
-  (void) TextIntCreateFontIndirect(&DefaultGuiFont, &StockObjects[DEFAULT_GUI_FONT]);
-  GDIOBJ_MarkObjectGlobal(StockObjects[DEFAULT_GUI_FONT]);
-
-  StockObjects[DEFAULT_PALETTE] = (HGDIOBJ*)PALETTE_Init();
+  PGDI_TABLE_ENTRY Entry;
+  PETHREAD Thread;
+  LONG ProcessId, LockedProcessId, PrevProcId;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj);
+  Thread = PsGetCurrentThread();
+
+  /* shift the process id to the left so we can use the first bit to lock the object.
+     FIXME - don't shift once ROS' PIDs match with nt! */
+  ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+  LockedProcessId = ProcessId | 0x1;
+
+  Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
+
+LockHandle:
+  /* lock the handle, we must not delete stock objects, so don't check!!! */
+  PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+  if(PrevProcId == ProcessId)
+  {
+    /* we're unlocking an object that belongs to our process or it's a global
+       object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
+       failed to lock the object and it turned out to be a global object. */
+    if(Entry->KernelData != NULL)
+    {
+      PETHREAD PrevThread;
+      PGDIOBJHDR GdiHdr;
+
+      GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+      PrevThread = GdiHdr->LockingThread;
+      if(PrevThread == Thread)
+      {
+        BOOL Ret;
+
+        if(--GdiHdr->Locks == 0)
+        {
+          GdiHdr->LockingThread = NULL;
+
+#ifdef GDI_DEBUG
+          GdiHdr->lockfile = NULL;
+          GdiHdr->lockline = 0;
+#endif
+        }
+
+        if(Entry->Type == 0 && GdiHdr->Locks == 0)
+        {
+          PPAGED_LOOKASIDE_LIST LookasideList;
+          PW32PROCESS W32Process = PsGetWin32Process();
+          DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
+
+          ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
+
+          /* we should delete the handle */
+          Entry->KernelData = NULL;
+          InterlockedExchange(&Entry->ProcessId, 0);
+
+          InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+                                    &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
+          if(W32Process != NULL)
+          {
+            InterlockedDecrement(&W32Process->GDIObjects);
+          }
+
+          /* call the cleanup routine. */
+          Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
+
+          /* Now it's time to free the memory */
+          LookasideList = FindLookasideList(Type);
+          if(LookasideList != NULL)
+          {
+            ExFreeToPagedLookasideList(LookasideList, GdiHdr);
+          }
+        }
+        else
+        {
+          /* remove the handle slot lock */
+          InterlockedExchange(&Entry->ProcessId, PrevProcId);
+          Ret = TRUE;
+        }
+
+        /* we're done*/
+        return Ret;
+      }
+#ifdef GDI_DEBUG
+      else if(PrevThread != NULL)
+      {
+        DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
+                hObj, PrevThread, GdiHdr->lockfile, GdiHdr->lockline, file, line);
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+      }
+#endif
+      else
+      {
+#ifdef GDI_DEBUG
+        if(++Attempts > 20)
+        {
+          DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
+        }
+#endif
+        /* FIXME - we should give up after some time unless we want to wait forever! */
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+        DelayExecution();
+        goto LockHandle;
+      }
+    }
+    else
+    {
+      InterlockedExchange(&Entry->ProcessId, PrevProcId);
+      DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj);
+    }
+  }
+  else if(PrevProcId == LockedProcessId)
+  {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
+    }
+#endif
+    /* the handle is currently locked, wait some time and try again.
+       FIXME - we shouldn't loop forever! Give up after some time! */
+    DelayExecution();
+    /* try again */
+    goto LockHandle;
+  }
+  else if((PrevProcId & ~0x1) == 0)
+  {
+    /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
+    ProcessId = 0x0;
+    LockedProcessId = ProcessId |0x1;
+
+    goto LockHandle;
+  }
+  else
+  {
+    DPRINT1("Attempted to unlock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
+  }
+
+  return FALSE;
 }
 
-/*!
- * Return stock object.
- * \param      Object - stock object id.
- * \return     Handle to the object.
-*/
-HGDIOBJ STDCALL  W32kGetStockObject(INT  Object)
+BOOL INTERNAL_CALL
+GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
 {
-  // check when adding new objects
-  if( (Object < 0) || (Object >= NB_STOCK_OBJECTS)  )
-       return NULL;
-  return StockObjects[Object];
+  PGDI_TABLE_ENTRY Entry;
+  LONG ProcessId;
+  BOOL Ret;
+
+  DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
+
+  if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
+  {
+    ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+
+    Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
+    Ret = Entry->KernelData != NULL &&
+          Entry->Type != 0 &&
+          (Entry->ProcessId & ~0x1) == ProcessId;
+
+    return Ret;
+  }
+
+  return FALSE;
 }
 
-/*!
- * Delete GDI object
- * \param      hObject object handle
- * \return     if the function fails the returned value is NULL.
-*/
-BOOL STDCALL  W32kDeleteObject(HGDIOBJ hObject)
+BOOL INTERNAL_CALL
+GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
 {
-  return GDIOBJ_FreeObj( hObject, GO_MAGIC_DONTCARE, GDIOBJFLAG_DEFAULT );
+/*
+ * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
+ *             MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
+ */
+  PGDI_TABLE_ENTRY Entry;
+  LONG ProcessId, LockedProcessId, PrevProcId;
+  PETHREAD Thread;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  ASSERT(hObj);
+
+  DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
+
+  Thread = PsGetCurrentThread();
+
+  if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
+  {
+    /* shift the process id to the left so we can use the first bit to lock the object.
+       FIXME - don't shift once ROS' PIDs match with nt! */
+    ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+    LockedProcessId = ProcessId | 0x1;
+
+    Entry = GDI_HANDLE_GET_ENTRY(HandleTable, *hObj);
+
+LockHandle:
+    /* lock the object, we must not convert stock objects, so don't check!!! */
+    PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+    if(PrevProcId == ProcessId)
+    {
+      LONG NewType, PrevType, OldType;
+
+      /* we're locking an object that belongs to our process. First calculate
+         the new object type including the stock object flag and then try to
+         exchange it.*/
+      NewType = GDI_HANDLE_GET_TYPE(*hObj);
+      NewType |= NewType >> 16;
+      /* This is the type that the object should have right now, save it */
+      OldType = NewType;
+      /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
+      NewType |= GDI_HANDLE_STOCK_MASK;
+
+      /* Try to exchange the type field - but only if the old (previous type) matches! */
+      PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
+      if(PrevType == OldType && Entry->KernelData != NULL)
+      {
+        PETHREAD PrevThread;
+        PGDIOBJHDR GdiHdr;
+
+        /* We successfully set the stock object flag.
+           KernelData should never be NULL here!!! */
+        ASSERT(Entry->KernelData);
+
+        GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+        PrevThread = GdiHdr->LockingThread;
+        if(PrevThread == NULL || PrevThread == Thread)
+        {
+          /* dereference the process' object counter */
+          if(PrevProcId != GDI_GLOBAL_PROCESS)
+          {
+            PEPROCESS OldProcess;
+            PW32PROCESS W32Process;
+            NTSTATUS Status;
+
+            /* FIXME */
+            Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
+            if(NT_SUCCESS(Status))
+            {
+              W32Process = OldProcess->Win32Process;
+              if(W32Process != NULL)
+              {
+                InterlockedDecrement(&W32Process->GDIObjects);
+              }
+              ObDereferenceObject(OldProcess);
+            }
+          }
+
+          /* remove the process id lock and make it global */
+          InterlockedExchange(&Entry->ProcessId, GDI_GLOBAL_PROCESS);
+
+          *hObj = (HGDIOBJ)((ULONG)(*hObj) | GDI_HANDLE_STOCK_MASK);
+
+          /* we're done, successfully converted the object */
+          return TRUE;
+        }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            if(GdiHdr->lockfile != NULL)
+            {
+              DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+            }
+          }
+#endif
+          /* WTF?! The object is already locked by a different thread!
+             Release the lock, wait a bit and try again!
+             FIXME - we should give up after some time unless we want to wait forever! */
+          InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+          DelayExecution();
+          goto LockHandle;
+        }
+      }
+      else
+      {
+        DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
+      }
+    }
+    else if(PrevProcId == LockedProcessId)
+    {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
+    }
+#endif
+      /* the object is currently locked, wait some time and try again.
+         FIXME - we shouldn't loop forever! Give up after some time! */
+      DelayExecution();
+      /* try again */
+      goto LockHandle;
+    }
+    else
+    {
+      DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
+    }
+  }
+
+  return FALSE;
 }
 
-/*!
- * Internal function. Called when the process is destroyed to free the remaining GDI handles.
- * \param      Process - PID of the process that was destroyed.
-*/
-BOOL STDCALL W32kCleanupForProcess(INT Pid)
+void INTERNAL_CALL
+GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
 {
-  DWORD i;
-  PGDI_HANDLE_ENTRY handleEntry;
-  PGDIOBJHDR objectHeader;
-  NTSTATUS Status;
-  PEPROCESS Process;
+  PGDI_TABLE_ENTRY Entry;
+  LONG ProcessId, LockedProcessId, PrevProcId;
+  PETHREAD Thread;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
+
+  Thread = PsGetCurrentThread();
 
-  if (! NT_SUCCESS(PsLookupProcessByProcessId(Pid, &Process)))
+  if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
+  {
+    /* shift the process id to the left so we can use the first bit to lock the object.
+       FIXME - don't shift once ROS' PIDs match with nt! */
+    ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+    LockedProcessId = ProcessId | 0x1;
+
+    Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
+
+LockHandle:
+    /* lock the object, we must not convert stock objects, so don't check!!! */
+    PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, ProcessId, LockedProcessId);
+    if(PrevProcId == ProcessId)
     {
-      return FALSE;
+      PETHREAD PrevThread;
+
+      if(Entry->Type != 0 && Entry->KernelData != NULL)
+      {
+        PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+        PrevThread = GdiHdr->LockingThread;
+        if(PrevThread == NULL || PrevThread == Thread)
+        {
+          PEPROCESS OldProcess;
+          PW32PROCESS W32Process;
+          NTSTATUS Status;
+
+          /* dereference the process' object counter */
+          /* FIXME */
+          Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
+          if(NT_SUCCESS(Status))
+          {
+            W32Process = OldProcess->Win32Process;
+            if(W32Process != NULL)
+            {
+              InterlockedDecrement(&W32Process->GDIObjects);
+            }
+            ObDereferenceObject(OldProcess);
+          }
+
+          if(NewOwner != NULL)
+          {
+            /* FIXME */
+            ProcessId = (LONG)PsGetProcessId(NewOwner) << 1;
+
+            /* Increase the new process' object counter */
+            W32Process = NewOwner->Win32Process;
+            if(W32Process != NULL)
+            {
+              InterlockedIncrement(&W32Process->GDIObjects);
+            }
+          }
+          else
+            ProcessId = 0;
+
+          /* remove the process id lock and change it to the new process id */
+          InterlockedExchange(&Entry->ProcessId, ProcessId);
+
+          /* we're done! */
+          return;
+        }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            if(GdiHdr->lockfile != NULL)
+            {
+              DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+            }
+          }
+#endif
+          /* WTF?! The object is already locked by a different thread!
+             Release the lock, wait a bit and try again! DO reset the pid lock
+             so we make sure we don't access invalid memory in case the object is
+             being deleted in the meantime (because we don't have aquired a reference
+             at this point).
+             FIXME - we should give up after some time unless we want to wait forever! */
+          InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+          DelayExecution();
+          goto LockHandle;
+        }
+      }
+      else
+      {
+        DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
+      }
     }
-  KeAttachProcess(Process);
-
-  for(i = 1; i < GDI_HANDLE_NUMBER; i++)
+    else if(PrevProcId == LockedProcessId)
     {
-      handleEntry = GDIOBJ_iGetHandleEntryForIndex((WORD) i & 0xffff);
-      if (NULL != handleEntry && 0 != handleEntry->wMagic &&
-          handleEntry->hProcessId == Pid)
-       {
-         objectHeader = (PGDIOBJHDR) handleEntry->pObject;
-         DPRINT("\nW32kCleanup: %d, magic: %x \n process: %d, locks: %d", i, handleEntry->wMagic, handleEntry->hProcessId, objectHeader->dwCount);
-         GDIOBJ_FreeObj( (WORD) i & 0xffff, GO_MAGIC_DONTCARE, GDIOBJFLAG_IGNOREPID|GDIOBJFLAG_IGNORELOCK );
-       }
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
+    }
+#endif
+      /* the object is currently locked, wait some time and try again.
+         FIXME - we shouldn't loop forever! Give up after some time! */
+      DelayExecution();
+      /* try again */
+      goto LockHandle;
+    }
+    else if((PrevProcId >> 1) == 0)
+    {
+      /* allow changing ownership of global objects */
+      ProcessId = 0;
+      LockedProcessId = ProcessId | 0x1;
+      goto LockHandle;
+    }
+    else if((PrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
+    {
+      DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
+    }
+    else
+    {
+      DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
     }
+  }
+}
 
-  KeDetachProcess();
+void INTERNAL_CALL
+GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
+{
+  PGDI_TABLE_ENTRY FromEntry;
+  PETHREAD Thread;
+  LONG FromProcessId, FromLockedProcessId, FromPrevProcId;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
 
-  return TRUE;
+  DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
+
+  Thread = PsGetCurrentThread();
+
+  if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
+  {
+    FromEntry = GDI_HANDLE_GET_ENTRY(HandleTable, CopyFrom);
+
+    FromProcessId = FromEntry->ProcessId & ~0x1;
+    FromLockedProcessId = FromProcessId | 0x1;
+
+LockHandleFrom:
+    /* lock the object, we must not convert stock objects, so don't check!!! */
+    FromPrevProcId = InterlockedCompareExchange(&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
+    if(FromPrevProcId == FromProcessId)
+    {
+      PETHREAD PrevThread;
+      PGDIOBJHDR GdiHdr;
+
+      if(FromEntry->Type != 0 && FromEntry->KernelData != NULL)
+      {
+        GdiHdr = GDIBdyToHdr(FromEntry->KernelData);
+
+        /* save the pointer to the calling thread so we know it was this thread
+           that locked the object */
+        PrevThread = GdiHdr->LockingThread;
+        if(PrevThread == NULL || PrevThread == Thread)
+        {
+          /* now let's change the ownership of the target object */
+
+          if((FromPrevProcId & ~0x1) != 0)
+          {
+            PEPROCESS ProcessTo;
+            /* FIXME */
+            if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(FromPrevProcId >> 1), &ProcessTo)))
+            {
+              GDIOBJ_SetOwnership(CopyTo, ProcessTo);
+              ObDereferenceObject(ProcessTo);
+            }
+          }
+          else
+          {
+            /* mark the object as global */
+            GDIOBJ_SetOwnership(CopyTo, NULL);
+          }
+
+          InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
+        }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            if(GdiHdr->lockfile != NULL)
+            {
+              DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+            }
+          }
+#endif
+          /* WTF?! The object is already locked by a different thread!
+             Release the lock, wait a bit and try again! DO reset the pid lock
+             so we make sure we don't access invalid memory in case the object is
+             being deleted in the meantime (because we don't have aquired a reference
+             at this point).
+             FIXME - we should give up after some time unless we want to wait forever! */
+          InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
+
+          DelayExecution();
+          goto LockHandleFrom;
+        }
+      }
+      else
+      {
+        DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
+      }
+    }
+    else if(FromPrevProcId == FromLockedProcessId)
+    {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
+    }
+#endif
+      /* the object is currently locked, wait some time and try again.
+         FIXME - we shouldn't loop forever! Give up after some time! */
+      DelayExecution();
+      /* try again */
+      goto LockHandleFrom;
+    }
+    else if((FromPrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
+    {
+      /* FIXME - should we really allow copying ownership from objects that we don't even own? */
+      DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, FromPrevProcId >> 1, PsGetCurrentProcessId());
+      FromProcessId = FromPrevProcId & ~0x1;
+      FromLockedProcessId = FromProcessId | 0x1;
+      goto LockHandleFrom;
+    }
+    else
+    {
+      DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
+    }
+  }
 }
 
-/*!
- *     Internal function. Dumps all the objects for the given process.
- * \param      If process == 0 dump all the objects.
- *
-*/
-VOID STDCALL W32kDumpGdiObjects( INT Process )
+PVOID INTERNAL_CALL
+GDI_MapHandleTable(HANDLE hProcess)
 {
-       DWORD i;
-       PGDI_HANDLE_ENTRY handleEntry;
-       PGDIOBJHDR  objectHeader;
-
-       for( i=1; i < GDI_HANDLE_NUMBER; i++ ){
-               handleEntry = GDIOBJ_iGetHandleEntryForIndex ((WORD) i & 0xffff);
-               if( handleEntry && handleEntry->wMagic != 0 ){
-                       objectHeader = (PGDIOBJHDR) handleEntry->pObject;
-                       DPRINT("\nHandle: %d, magic: %x \n process: %d, locks: %d", i, handleEntry->wMagic, handleEntry->hProcessId, objectHeader->dwCount);
-               }
-       }
-
+  DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
+         __FILE__, __LINE__, __FUNCTION__);
+  /* FIXME - Map the entire gdi handle table read-only to userland into the
+             scope of hProcess and return the pointer */
+  return NULL;
 }
+
 /* EOF */