dbghelp: Fix memory leak on error path in dwarf2_read_range (cppcheck).
[wine.git] / dlls / win32u / gdiobj.c
blob002c753949864871e8a657ce84248192f27009aa
1 /*
2 * GDI functions
4 * Copyright 1993 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <pthread.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winreg.h"
35 #include "winnls.h"
36 #include "winerror.h"
37 #include "winternl.h"
39 #include "ntgdi_private.h"
40 #include "wine/debug.h"
41 #include "wine/unixlib.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(gdi);
45 #define FIRST_GDI_HANDLE 32
47 static GDI_SHARED_MEMORY *gdi_shared;
48 static GDI_HANDLE_ENTRY *next_free;
49 static GDI_HANDLE_ENTRY *next_unused;
50 static LONG debug_count;
51 SYSTEM_BASIC_INFORMATION system_info;
53 const struct user_callbacks *user_callbacks = NULL;
55 static inline HGDIOBJ entry_to_handle( GDI_HANDLE_ENTRY *entry )
57 unsigned int idx = entry - gdi_shared->Handles;
58 return ULongToHandle( idx | (entry->Unique << NTGDI_HANDLE_TYPE_SHIFT) );
61 static inline GDI_HANDLE_ENTRY *handle_entry( HGDIOBJ handle )
63 unsigned int idx = LOWORD(handle);
65 if (idx < GDI_MAX_HANDLE_COUNT && gdi_shared->Handles[idx].Type)
67 if (!HIWORD( handle ) || HIWORD( handle ) == gdi_shared->Handles[idx].Unique)
68 return &gdi_shared->Handles[idx];
70 if (handle) WARN( "invalid handle %p\n", handle );
71 return NULL;
74 static inline struct gdi_obj_header *entry_obj( GDI_HANDLE_ENTRY *entry )
76 return (struct gdi_obj_header *)(ULONG_PTR)entry->Object;
79 /***********************************************************************
80 * GDI stock objects
83 static const LOGBRUSH WhiteBrush = { BS_SOLID, RGB(255,255,255), 0 };
84 static const LOGBRUSH BlackBrush = { BS_SOLID, RGB(0,0,0), 0 };
85 static const LOGBRUSH NullBrush = { BS_NULL, 0, 0 };
87 static const LOGBRUSH LtGrayBrush = { BS_SOLID, RGB(192,192,192), 0 };
88 static const LOGBRUSH GrayBrush = { BS_SOLID, RGB(128,128,128), 0 };
89 static const LOGBRUSH DkGrayBrush = { BS_SOLID, RGB(64,64,64), 0 };
91 static const LOGBRUSH DCBrush = { BS_SOLID, RGB(255,255,255), 0 };
93 static pthread_mutex_t gdi_lock;
96 /****************************************************************************
98 * language-independent stock fonts
102 static const LOGFONTW OEMFixedFont =
103 { 12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, OEM_CHARSET,
104 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN };
106 static const LOGFONTW AnsiFixedFont =
107 { 12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
108 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
109 {'C','o','u','r','i','e','r'} };
111 static const LOGFONTW AnsiVarFont =
112 { 12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
113 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
114 {'M','S',' ','S','a','n','s',' ','S','e','r','i','f'} };
116 /******************************************************************************
118 * language-dependent stock fonts
120 * 'ANSI' charset and 'DEFAULT' charset is not same.
121 * The chars in CP_ACP should be drawn with 'DEFAULT' charset.
122 * 'ANSI' charset seems to be identical with ISO-8859-1.
123 * 'DEFAULT' charset is a language-dependent charset.
125 * 'System' font seems to be an alias for language-dependent font.
129 * language-dependent stock fonts for all known charsets
130 * please see TranslateCharsetInfo (dlls/gdi/font.c) and
131 * CharsetBindingInfo (dlls/x11drv/xfont.c),
132 * and modify entries for your language if needed.
134 struct DefaultFontInfo
136 UINT charset;
137 LOGFONTW SystemFont;
138 LOGFONTW DeviceDefaultFont;
139 LOGFONTW SystemFixedFont;
140 LOGFONTW DefaultGuiFont;
143 static const struct DefaultFontInfo default_fonts[] =
145 { ANSI_CHARSET,
146 { /* System */
147 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
148 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
149 {'S','y','s','t','e','m'}
151 { /* Device Default */
152 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
153 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
154 {'S','y','s','t','e','m'}
156 { /* System Fixed */
157 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
158 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
159 {'C','o','u','r','i','e','r'}
161 { /* DefaultGuiFont */
162 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
163 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
164 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
167 { EASTEUROPE_CHARSET,
168 { /* System */
169 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, EASTEUROPE_CHARSET,
170 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
171 {'S','y','s','t','e','m'}
173 { /* Device Default */
174 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, EASTEUROPE_CHARSET,
175 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
176 {'S','y','s','t','e','m'}
178 { /* System Fixed */
179 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, EASTEUROPE_CHARSET,
180 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
181 {'C','o','u','r','i','e','r'}
183 { /* DefaultGuiFont */
184 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, EASTEUROPE_CHARSET,
185 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
186 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
189 { RUSSIAN_CHARSET,
190 { /* System */
191 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, RUSSIAN_CHARSET,
192 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
193 {'S','y','s','t','e','m'}
195 { /* Device Default */
196 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, RUSSIAN_CHARSET,
197 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
198 {'S','y','s','t','e','m'}
200 { /* System Fixed */
201 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, RUSSIAN_CHARSET,
202 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
203 {'C','o','u','r','i','e','r'}
205 { /* DefaultGuiFont */
206 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, RUSSIAN_CHARSET,
207 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
208 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
211 { GREEK_CHARSET,
212 { /* System */
213 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, GREEK_CHARSET,
214 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
215 {'S','y','s','t','e','m'}
217 { /* Device Default */
218 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, GREEK_CHARSET,
219 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
220 {'S','y','s','t','e','m'}
222 { /* System Fixed */
223 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, GREEK_CHARSET,
224 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
225 {'C','o','u','r','i','e','r'}
227 { /* DefaultGuiFont */
228 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, GREEK_CHARSET,
229 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
230 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
233 { TURKISH_CHARSET,
234 { /* System */
235 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, TURKISH_CHARSET,
236 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
237 {'S','y','s','t','e','m'}
239 { /* Device Default */
240 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, TURKISH_CHARSET,
241 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
242 {'S','y','s','t','e','m'}
244 { /* System Fixed */
245 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, TURKISH_CHARSET,
246 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
247 {'C','o','u','r','i','e','r'}
249 { /* DefaultGuiFont */
250 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, TURKISH_CHARSET,
251 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
252 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
255 { HEBREW_CHARSET,
256 { /* System */
257 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, HEBREW_CHARSET,
258 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
259 {'S','y','s','t','e','m'}
261 { /* Device Default */
262 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, HEBREW_CHARSET,
263 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
264 {'S','y','s','t','e','m'}
266 { /* System Fixed */
267 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, HEBREW_CHARSET,
268 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
269 {'C','o','u','r','i','e','r'}
271 { /* DefaultGuiFont */
272 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, HEBREW_CHARSET,
273 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
274 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
277 { ARABIC_CHARSET,
278 { /* System */
279 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ARABIC_CHARSET,
280 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
281 {'S','y','s','t','e','m'}
283 { /* Device Default */
284 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ARABIC_CHARSET,
285 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
286 {'S','y','s','t','e','m'}
288 { /* System Fixed */
289 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ARABIC_CHARSET,
290 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
291 {'C','o','u','r','i','e','r'}
293 { /* DefaultGuiFont */
294 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ARABIC_CHARSET,
295 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
296 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
299 { BALTIC_CHARSET,
300 { /* System */
301 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, BALTIC_CHARSET,
302 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
303 {'S','y','s','t','e','m'}
305 { /* Device Default */
306 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, BALTIC_CHARSET,
307 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
308 {'S','y','s','t','e','m'}
310 { /* System Fixed */
311 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, BALTIC_CHARSET,
312 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
313 {'C','o','u','r','i','e','r'}
315 { /* DefaultGuiFont */
316 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, BALTIC_CHARSET,
317 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
318 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
321 { THAI_CHARSET,
322 { /* System */
323 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, THAI_CHARSET,
324 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
325 {'S','y','s','t','e','m'}
327 { /* Device Default */
328 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, THAI_CHARSET,
329 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
330 {'S','y','s','t','e','m'}
332 { /* System Fixed */
333 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, THAI_CHARSET,
334 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
335 {'C','o','u','r','i','e','r'}
337 { /* DefaultGuiFont */
338 -11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, THAI_CHARSET,
339 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
340 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
343 { SHIFTJIS_CHARSET,
344 { /* System */
345 18, 8, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
346 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
347 {'S','y','s','t','e','m'}
349 { /* Device Default */
350 18, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
351 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
352 {'S','y','s','t','e','m'}
354 { /* System Fixed */
355 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
356 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
357 {'C','o','u','r','i','e','r'}
359 { /* DefaultGuiFont */
360 -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
361 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
362 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
365 { GB2312_CHARSET,
366 { /* System */
367 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, GB2312_CHARSET,
368 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
369 {'S','y','s','t','e','m'}
371 { /* Device Default */
372 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, GB2312_CHARSET,
373 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
374 {'S','y','s','t','e','m'}
376 { /* System Fixed */
377 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, GB2312_CHARSET,
378 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
379 {'C','o','u','r','i','e','r'}
381 { /* DefaultGuiFont */
382 -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, GB2312_CHARSET,
383 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
384 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
387 { HANGEUL_CHARSET,
388 { /* System */
389 16, 8, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, HANGEUL_CHARSET,
390 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
391 {'S','y','s','t','e','m'}
393 { /* Device Default */
394 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, HANGEUL_CHARSET,
395 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
396 {'S','y','s','t','e','m'}
398 { /* System Fixed */
399 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, HANGEUL_CHARSET,
400 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
401 {'C','o','u','r','i','e','r'}
403 { /* DefaultGuiFont */
404 -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, HANGEUL_CHARSET,
405 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
406 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
409 { CHINESEBIG5_CHARSET,
410 { /* System */
411 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, CHINESEBIG5_CHARSET,
412 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
413 {'S','y','s','t','e','m'}
415 { /* Device Default */
416 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, CHINESEBIG5_CHARSET,
417 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
418 {'S','y','s','t','e','m'}
420 { /* System Fixed */
421 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, CHINESEBIG5_CHARSET,
422 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
423 {'C','o','u','r','i','e','r'}
425 { /* DefaultGuiFont */
426 -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, CHINESEBIG5_CHARSET,
427 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
428 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
431 { JOHAB_CHARSET,
432 { /* System */
433 16, 7, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, JOHAB_CHARSET,
434 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
435 {'S','y','s','t','e','m'}
437 { /* Device Default */
438 16, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, JOHAB_CHARSET,
439 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
440 {'S','y','s','t','e','m'}
442 { /* System Fixed */
443 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, JOHAB_CHARSET,
444 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
445 {'C','o','u','r','i','e','r'}
447 { /* DefaultGuiFont */
448 -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, JOHAB_CHARSET,
449 0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS,
450 {'M','S',' ','S','h','e','l','l',' ','D','l','g'}
455 void make_gdi_object_system( HGDIOBJ handle, BOOL set)
457 GDI_HANDLE_ENTRY *entry;
459 pthread_mutex_lock( &gdi_lock );
460 if ((entry = handle_entry( handle ))) entry_obj( entry )->system = !!set;
461 pthread_mutex_unlock( &gdi_lock );
464 /******************************************************************************
465 * get_default_fonts
467 static const struct DefaultFontInfo* get_default_fonts(UINT charset)
469 unsigned int n;
471 for(n = 0; n < ARRAY_SIZE( default_fonts ); n++)
473 if ( default_fonts[n].charset == charset )
474 return &default_fonts[n];
477 FIXME( "unhandled charset 0x%08x - use ANSI_CHARSET for default stock objects\n", charset );
478 return &default_fonts[0];
482 /******************************************************************************
483 * get_default_charset (internal)
485 * get the language-dependent charset that can handle CP_ACP correctly.
487 static UINT get_default_charset( void )
489 CHARSETINFO csi;
490 UINT uACP;
492 uACP = get_acp();
493 csi.ciCharset = ANSI_CHARSET;
494 if ( !translate_charset_info( ULongToPtr(uACP), &csi, TCI_SRCCODEPAGE ) )
496 FIXME( "unhandled codepage %u - use ANSI_CHARSET for default stock objects\n", uACP );
497 return ANSI_CHARSET;
500 return csi.ciCharset;
504 /***********************************************************************
505 * GDI_get_ref_count
507 * Retrieve the reference count of a GDI object.
508 * Note: the object must be locked otherwise the count is meaningless.
510 UINT GDI_get_ref_count( HGDIOBJ handle )
512 GDI_HANDLE_ENTRY *entry;
513 UINT ret = 0;
515 pthread_mutex_lock( &gdi_lock );
516 if ((entry = handle_entry( handle ))) ret = entry_obj( entry )->selcount;
517 pthread_mutex_unlock( &gdi_lock );
518 return ret;
522 /***********************************************************************
523 * GDI_inc_ref_count
525 * Increment the reference count of a GDI object.
527 HGDIOBJ GDI_inc_ref_count( HGDIOBJ handle )
529 GDI_HANDLE_ENTRY *entry;
531 pthread_mutex_lock( &gdi_lock );
532 if ((entry = handle_entry( handle ))) entry_obj( entry )->selcount++;
533 else handle = 0;
534 pthread_mutex_unlock( &gdi_lock );
535 return handle;
539 /***********************************************************************
540 * GDI_dec_ref_count
542 * Decrement the reference count of a GDI object.
544 BOOL GDI_dec_ref_count( HGDIOBJ handle )
546 GDI_HANDLE_ENTRY *entry;
548 pthread_mutex_lock( &gdi_lock );
549 if ((entry = handle_entry( handle )))
551 assert( entry_obj( entry )->selcount );
552 if (!--entry_obj( entry )->selcount && entry_obj( entry )->deleted)
554 /* handle delayed DeleteObject*/
555 entry_obj( entry )->deleted = 0;
556 pthread_mutex_unlock( &gdi_lock );
557 TRACE( "executing delayed DeleteObject for %p\n", handle );
558 NtGdiDeleteObjectApp( handle );
559 return TRUE;
562 pthread_mutex_unlock( &gdi_lock );
563 return entry != NULL;
567 static HFONT create_font( const LOGFONTW *deffont )
569 ENUMLOGFONTEXDVW lf;
571 memset( &lf, 0, sizeof(lf) );
572 lf.elfEnumLogfontEx.elfLogFont = *deffont;
573 return NtGdiHfontCreate( &lf, sizeof(lf), 0, 0, NULL );
576 static HFONT create_scaled_font( const LOGFONTW *deffont, unsigned int dpi )
578 LOGFONTW lf;
580 lf = *deffont;
581 lf.lfHeight = muldiv( lf.lfHeight, dpi, 96 );
582 return create_font( &lf );
585 static void init_gdi_shared(void)
587 SIZE_T size = sizeof(*gdi_shared);
589 if (NtAllocateVirtualMemory( GetCurrentProcess(), (void **)&gdi_shared, 0, &size,
590 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ))
591 return;
592 next_unused = gdi_shared->Handles + FIRST_GDI_HANDLE;
594 #ifndef _WIN64
595 if (NtCurrentTeb()->GdiBatchCount)
597 TEB64 *teb64 = (TEB64 *)(UINT_PTR)NtCurrentTeb()->GdiBatchCount;
598 PEB64 *peb64 = (PEB64 *)(UINT_PTR)teb64->Peb;
599 peb64->GdiSharedHandleTable = (UINT_PTR)gdi_shared;
600 return;
602 #endif
603 /* NOTE: Windows uses 32-bit for 32-bit kernel */
604 NtCurrentTeb()->Peb->GdiSharedHandleTable = gdi_shared;
607 HGDIOBJ get_stock_object( INT obj )
609 assert( obj >= 0 && obj <= STOCK_LAST + 1 && obj != 9 );
611 switch (obj)
613 case OEM_FIXED_FONT:
614 if (get_system_dpi() != 96) obj = 9;
615 break;
616 case SYSTEM_FONT:
617 if (get_system_dpi() != 96) obj = STOCK_LAST + 2;
618 break;
619 case SYSTEM_FIXED_FONT:
620 if (get_system_dpi() != 96) obj = STOCK_LAST + 3;
621 break;
622 case DEFAULT_GUI_FONT:
623 if (get_system_dpi() != 96) obj = STOCK_LAST + 4;
624 break;
627 return entry_to_handle( handle_entry( ULongToHandle( obj + FIRST_GDI_HANDLE )));
630 static void init_stock_objects( unsigned int dpi )
632 const struct DefaultFontInfo *deffonts;
633 unsigned int i;
634 HGDIOBJ obj;
636 /* Create stock objects in order matching stock object macros,
637 * so that they use predictable handle slots. Our GetStockObject
638 * depends on it. */
639 create_brush( &WhiteBrush );
640 create_brush( &LtGrayBrush );
641 create_brush( &GrayBrush );
642 create_brush( &DkGrayBrush );
643 create_brush( &BlackBrush );
644 create_brush( &NullBrush );
646 create_pen( PS_SOLID, 0, RGB(255,255,255) );
647 create_pen( PS_SOLID, 0, RGB(0,0,0) );
648 create_pen( PS_NULL, 0, RGB(0,0,0) );
650 /* slot 9 is not used for non-scaled stock objects */
651 create_scaled_font( &OEMFixedFont, dpi );
653 /* language-independent stock fonts */
654 create_font( &OEMFixedFont );
655 create_font( &AnsiFixedFont );
656 create_font( &AnsiVarFont );
658 /* language-dependent stock fonts */
659 deffonts = get_default_fonts(get_default_charset());
660 create_font( &deffonts->SystemFont );
661 create_font( &deffonts->DeviceDefaultFont );
663 PALETTE_Init();
665 create_font( &deffonts->SystemFixedFont );
666 create_font( &deffonts->DefaultGuiFont );
668 create_brush( &DCBrush );
669 NtGdiCreatePen( PS_SOLID, 0, RGB(0,0,0), NULL );
671 obj = NtGdiCreateBitmap( 1, 1, 1, 1, NULL );
673 assert( (HandleToULong( obj ) & 0xffff) == FIRST_GDI_HANDLE + DEFAULT_BITMAP );
675 create_scaled_font( &deffonts->SystemFont, dpi );
676 create_scaled_font( &deffonts->SystemFixedFont, dpi );
677 create_scaled_font( &deffonts->DefaultGuiFont, dpi );
679 /* clear the NOSYSTEM bit on all stock objects*/
680 for (i = 0; i < STOCK_LAST + 5; i++)
682 GDI_HANDLE_ENTRY *entry = &gdi_shared->Handles[FIRST_GDI_HANDLE + i];
683 entry_obj( entry )->system = TRUE;
684 entry->StockFlag = 1;
689 static const char *gdi_obj_type( unsigned type )
691 switch ( type )
693 case NTGDI_OBJ_PEN: return "NTGDI_OBJ_PEN";
694 case NTGDI_OBJ_BRUSH: return "NTGDI_OBJ_BRUSH";
695 case NTGDI_OBJ_DC: return "NTGDI_OBJ_DC";
696 case NTGDI_OBJ_METADC: return "NTGDI_OBJ_METADC";
697 case NTGDI_OBJ_PAL: return "NTGDI_OBJ_PAL";
698 case NTGDI_OBJ_FONT: return "NTGDI_OBJ_FONT";
699 case NTGDI_OBJ_BITMAP: return "NTGDI_OBJ_BITMAP";
700 case NTGDI_OBJ_REGION: return "NTGDI_OBJ_REGION";
701 case NTGDI_OBJ_METAFILE: return "NTGDI_OBJ_METAFILE";
702 case NTGDI_OBJ_MEMDC: return "NTGDI_OBJ_MEMDC";
703 case NTGDI_OBJ_EXTPEN: return "NTGDI_OBJ_EXTPEN";
704 case NTGDI_OBJ_ENHMETADC: return "NTGDI_OBJ_ENHMETADC";
705 case NTGDI_OBJ_ENHMETAFILE: return "NTGDI_OBJ_ENHMETAFILE";
706 default: return "UNKNOWN";
710 static void dump_gdi_objects( void )
712 GDI_HANDLE_ENTRY *entry;
714 TRACE( "%u objects:\n", GDI_MAX_HANDLE_COUNT );
716 pthread_mutex_lock( &gdi_lock );
717 for (entry = gdi_shared->Handles; entry < next_unused; entry++)
719 if (!entry->Type)
720 TRACE( "handle %p FREE\n", entry_to_handle( entry ));
721 else
722 TRACE( "handle %p obj %s type %s selcount %u deleted %u\n",
723 entry_to_handle( entry ), wine_dbgstr_longlong( entry->Object ),
724 gdi_obj_type( entry->ExtType << NTGDI_HANDLE_TYPE_SHIFT ),
725 entry_obj( entry )->selcount, entry_obj( entry )->deleted );
727 pthread_mutex_unlock( &gdi_lock );
730 /***********************************************************************
731 * alloc_gdi_handle
733 * Allocate a GDI handle for an object, which must have been allocated on the process heap.
735 HGDIOBJ alloc_gdi_handle( struct gdi_obj_header *obj, DWORD type, const struct gdi_obj_funcs *funcs )
737 GDI_HANDLE_ENTRY *entry;
738 HGDIOBJ ret;
740 assert( type ); /* type 0 is reserved to mark free entries */
742 pthread_mutex_lock( &gdi_lock );
744 entry = next_free;
745 if (entry)
746 next_free = (GDI_HANDLE_ENTRY *)(UINT_PTR)entry->Object;
747 else if (next_unused < gdi_shared->Handles + GDI_MAX_HANDLE_COUNT)
748 entry = next_unused++;
749 else
751 pthread_mutex_unlock( &gdi_lock );
752 ERR( "out of GDI object handles, expect a crash\n" );
753 if (TRACE_ON(gdi)) dump_gdi_objects();
754 return 0;
756 obj->funcs = funcs;
757 obj->selcount = 0;
758 obj->system = 0;
759 obj->deleted = 0;
760 entry->Object = (UINT_PTR)obj;
761 entry->ExtType = type >> NTGDI_HANDLE_TYPE_SHIFT;
762 entry->Type = entry->ExtType & 0x1f;
763 if (++entry->Generation == 0xff) entry->Generation = 1;
764 ret = entry_to_handle( entry );
765 pthread_mutex_unlock( &gdi_lock );
766 TRACE( "allocated %s %p %u/%u\n", gdi_obj_type(type), ret,
767 InterlockedIncrement( &debug_count ), GDI_MAX_HANDLE_COUNT );
768 return ret;
772 /***********************************************************************
773 * free_gdi_handle
775 * Free a GDI handle and return a pointer to the object.
777 void *free_gdi_handle( HGDIOBJ handle )
779 void *object = NULL;
780 GDI_HANDLE_ENTRY *entry;
782 pthread_mutex_lock( &gdi_lock );
783 if ((entry = handle_entry( handle )))
785 TRACE( "freed %s %p %u/%u\n", gdi_obj_type( entry->ExtType << NTGDI_HANDLE_TYPE_SHIFT ),
786 handle, InterlockedDecrement( &debug_count ) + 1, GDI_MAX_HANDLE_COUNT );
787 object = entry_obj( entry );
788 entry->Type = 0;
789 entry->Object = (UINT_PTR)next_free;
790 next_free = entry;
792 pthread_mutex_unlock( &gdi_lock );
793 return object;
796 DWORD get_gdi_object_type( HGDIOBJ obj )
798 GDI_HANDLE_ENTRY *entry = handle_entry( obj );
799 return entry ? entry->ExtType << NTGDI_HANDLE_TYPE_SHIFT : 0;
802 void set_gdi_client_ptr( HGDIOBJ obj, void *ptr )
804 GDI_HANDLE_ENTRY *entry = handle_entry( obj );
805 if (entry) entry->UserPointer = (UINT_PTR)ptr;
808 /***********************************************************************
809 * get_any_obj_ptr
811 * Return a pointer to, and the type of, the GDI object
812 * associated with the handle.
813 * The object must be released with GDI_ReleaseObj.
815 void *get_any_obj_ptr( HGDIOBJ handle, DWORD *type )
817 void *ptr = NULL;
818 GDI_HANDLE_ENTRY *entry;
820 pthread_mutex_lock( &gdi_lock );
822 if ((entry = handle_entry( handle )))
824 ptr = entry_obj( entry );
825 *type = entry->ExtType << NTGDI_HANDLE_TYPE_SHIFT;
828 if (!ptr) pthread_mutex_unlock( &gdi_lock );
829 return ptr;
832 /***********************************************************************
833 * GDI_GetObjPtr
835 * Return a pointer to the GDI object associated with the handle.
836 * Return NULL if the object has the wrong type.
837 * The object must be released with GDI_ReleaseObj.
839 void *GDI_GetObjPtr( HGDIOBJ handle, DWORD type )
841 DWORD ret_type;
842 void *ptr = get_any_obj_ptr( handle, &ret_type );
843 if (ptr && ret_type != type)
845 GDI_ReleaseObj( handle );
846 ptr = NULL;
848 return ptr;
851 /***********************************************************************
852 * GDI_ReleaseObj
855 void GDI_ReleaseObj( HGDIOBJ handle )
857 pthread_mutex_unlock( &gdi_lock );
861 /***********************************************************************
862 * NtGdiDeleteObjectApp (win32u.@)
864 * Delete a Gdi object.
866 * PARAMS
867 * obj [I] Gdi object to delete
869 * RETURNS
870 * Success: TRUE. If obj was not returned from GetStockObject(), any resources
871 * it consumed are released.
872 * Failure: FALSE, if obj is not a valid Gdi object, or is currently selected
873 * into a DC.
875 BOOL WINAPI NtGdiDeleteObjectApp( HGDIOBJ obj )
877 GDI_HANDLE_ENTRY *entry;
878 const struct gdi_obj_funcs *funcs = NULL;
879 struct gdi_obj_header *header;
881 pthread_mutex_lock( &gdi_lock );
882 if (!(entry = handle_entry( obj )))
884 pthread_mutex_unlock( &gdi_lock );
885 return FALSE;
888 header = entry_obj( entry );
889 if (header->system)
891 TRACE("Preserving system object %p\n", obj);
892 pthread_mutex_unlock( &gdi_lock );
893 return TRUE;
896 obj = entry_to_handle( entry ); /* make it a full handle */
898 if (header->selcount)
900 TRACE("delayed for %p because object in use, count %u\n", obj, header->selcount );
901 header->deleted = 1; /* mark for delete */
903 else funcs = header->funcs;
905 pthread_mutex_unlock( &gdi_lock );
907 TRACE("%p\n", obj );
909 if (funcs && funcs->pDeleteObject) return funcs->pDeleteObject( obj );
910 return TRUE;
913 /***********************************************************************
914 * NtGdiCreateClientObj (win32u.@)
916 HANDLE WINAPI NtGdiCreateClientObj( ULONG type )
918 struct gdi_obj_header *obj;
919 HGDIOBJ handle;
921 if (!(obj = malloc( sizeof(*obj) )))
922 return 0;
924 handle = alloc_gdi_handle( obj, type, NULL );
925 if (!handle) free( obj );
926 return handle;
929 /***********************************************************************
930 * NtGdiDeleteClientObj (win32u.@)
932 BOOL WINAPI NtGdiDeleteClientObj( HGDIOBJ handle )
934 void *obj;
935 if (!(obj = free_gdi_handle( handle ))) return FALSE;
936 free( obj );
937 return TRUE;
941 /***********************************************************************
942 * NtGdiExtGetObjectW (win32u.@)
944 INT WINAPI NtGdiExtGetObjectW( HGDIOBJ handle, INT count, void *buffer )
946 GDI_HANDLE_ENTRY *entry;
947 const struct gdi_obj_funcs *funcs = NULL;
948 INT result = 0;
950 TRACE("%p %d %p\n", handle, count, buffer );
952 pthread_mutex_lock( &gdi_lock );
953 if ((entry = handle_entry( handle )))
955 funcs = entry_obj( entry )->funcs;
956 handle = entry_to_handle( entry ); /* make it a full handle */
958 pthread_mutex_unlock( &gdi_lock );
960 if (funcs && funcs->pGetObjectW)
962 if (buffer && ((ULONG_PTR)buffer >> 16) == 0) /* catch apps getting argument order wrong */
963 SetLastError( ERROR_NOACCESS );
964 else
965 result = funcs->pGetObjectW( handle, count, buffer );
967 return result;
970 /***********************************************************************
971 * NtGdiGetDCObject (win32u.@)
973 * Get the currently selected object of a given type in a device context.
975 HANDLE WINAPI NtGdiGetDCObject( HDC hdc, UINT type )
977 HGDIOBJ ret = 0;
978 DC *dc;
980 if (!(dc = get_dc_ptr( hdc ))) return 0;
982 switch (type)
984 case NTGDI_OBJ_EXTPEN: /* fall through */
985 case NTGDI_OBJ_PEN: ret = dc->hPen; break;
986 case NTGDI_OBJ_BRUSH: ret = dc->hBrush; break;
987 case NTGDI_OBJ_PAL: ret = dc->hPalette; break;
988 case NTGDI_OBJ_FONT: ret = dc->hFont; break;
989 case NTGDI_OBJ_SURF: ret = dc->hBitmap; break;
990 default:
991 FIXME( "(%p, %d): unknown type.\n", hdc, type );
992 break;
994 release_dc_ptr( dc );
995 return ret;
999 /***********************************************************************
1000 * NtGdiUnrealizeObject (win32u.@)
1002 BOOL WINAPI NtGdiUnrealizeObject( HGDIOBJ obj )
1004 const struct gdi_obj_funcs *funcs = NULL;
1005 GDI_HANDLE_ENTRY *entry;
1007 pthread_mutex_lock( &gdi_lock );
1008 if ((entry = handle_entry( obj )))
1010 funcs = entry_obj( entry )->funcs;
1011 obj = entry_to_handle( entry ); /* make it a full handle */
1013 pthread_mutex_unlock( &gdi_lock );
1015 if (funcs && funcs->pUnrealizeObject) return funcs->pUnrealizeObject( obj );
1016 return funcs != NULL;
1020 /***********************************************************************
1021 * NtGdiFlush (win32u.@)
1023 BOOL WINAPI NtGdiFlush(void)
1025 return TRUE; /* FIXME */
1029 /*******************************************************************
1030 * NtGdiGetColorAdjustment (win32u.@)
1032 BOOL WINAPI NtGdiGetColorAdjustment( HDC hdc, COLORADJUSTMENT *ca )
1034 FIXME( "stub\n" );
1035 return FALSE;
1038 /*******************************************************************
1039 * NtGdiSetColorAdjustment (win32u.@)
1041 BOOL WINAPI NtGdiSetColorAdjustment( HDC hdc, const COLORADJUSTMENT *ca )
1043 FIXME( "stub\n" );
1044 return FALSE;
1048 static struct unix_funcs unix_funcs =
1050 NtGdiAbortDoc,
1051 NtGdiAbortPath,
1052 NtGdiAlphaBlend,
1053 NtGdiAngleArc,
1054 NtGdiArcInternal,
1055 NtGdiBeginPath,
1056 NtGdiBitBlt,
1057 NtGdiCloseFigure,
1058 NtGdiComputeXformCoefficients,
1059 NtGdiCreateCompatibleBitmap,
1060 NtGdiCreateCompatibleDC,
1061 NtGdiCreateDIBitmapInternal,
1062 NtGdiCreateMetafileDC,
1063 NtGdiDdDDICheckVidPnExclusiveOwnership,
1064 NtGdiDdDDICreateDCFromMemory,
1065 NtGdiDdDDIDestroyDCFromMemory,
1066 NtGdiDdDDIDestroyDevice,
1067 NtGdiDdDDIEscape,
1068 NtGdiDdDDISetVidPnSourceOwner,
1069 NtGdiDeleteObjectApp,
1070 NtGdiDoPalette,
1071 NtGdiEllipse,
1072 NtGdiEndDoc,
1073 NtGdiEndPath,
1074 NtGdiEndPage,
1075 NtGdiEnumFonts,
1076 NtGdiExcludeClipRect,
1077 NtGdiExtEscape,
1078 NtGdiExtFloodFill,
1079 NtGdiExtTextOutW,
1080 NtGdiExtSelectClipRgn,
1081 NtGdiFillPath,
1082 NtGdiFillRgn,
1083 NtGdiFontIsLinked,
1084 NtGdiFrameRgn,
1085 NtGdiGetAndSetDCDword,
1086 NtGdiGetAppClipBox,
1087 NtGdiGetBoundsRect,
1088 NtGdiGetCharABCWidthsW,
1089 NtGdiGetCharWidthW,
1090 NtGdiGetCharWidthInfo,
1091 NtGdiGetDIBitsInternal,
1092 NtGdiGetDeviceCaps,
1093 NtGdiGetDeviceGammaRamp,
1094 NtGdiGetFontData,
1095 NtGdiGetFontUnicodeRanges,
1096 NtGdiGetGlyphIndicesW,
1097 NtGdiGetGlyphOutline,
1098 NtGdiGetKerningPairs,
1099 NtGdiGetNearestColor,
1100 NtGdiGetOutlineTextMetricsInternalW,
1101 NtGdiGetPixel,
1102 NtGdiGetRandomRgn,
1103 NtGdiGetRasterizerCaps,
1104 NtGdiGetRealizationInfo,
1105 NtGdiGetTextCharsetInfo,
1106 NtGdiGetTextExtentExW,
1107 NtGdiGetTextFaceW,
1108 NtGdiGetTextMetricsW,
1109 NtGdiGradientFill,
1110 NtGdiIntersectClipRect,
1111 NtGdiInvertRgn,
1112 NtGdiLineTo,
1113 NtGdiMaskBlt,
1114 NtGdiModifyWorldTransform,
1115 NtGdiMoveTo,
1116 NtGdiOffsetClipRgn,
1117 NtGdiOpenDCW,
1118 NtGdiPatBlt,
1119 NtGdiPlgBlt,
1120 NtGdiPolyDraw,
1121 NtGdiPolyPolyDraw,
1122 NtGdiPtVisible,
1123 NtGdiRectVisible,
1124 NtGdiRectangle,
1125 NtGdiResetDC,
1126 NtGdiResizePalette,
1127 NtGdiRestoreDC,
1128 NtGdiRoundRect,
1129 NtGdiScaleViewportExtEx,
1130 NtGdiScaleWindowExtEx,
1131 NtGdiSelectBitmap,
1132 NtGdiSelectBrush,
1133 NtGdiSelectClipPath,
1134 NtGdiSelectFont,
1135 NtGdiSelectPen,
1136 NtGdiSetBoundsRect,
1137 NtGdiSetDIBitsToDeviceInternal,
1138 NtGdiSetDeviceGammaRamp,
1139 NtGdiSetLayout,
1140 NtGdiSetPixel,
1141 NtGdiSetSystemPaletteUse,
1142 NtGdiStartDoc,
1143 NtGdiStartPage,
1144 NtGdiStretchBlt,
1145 NtGdiStretchDIBitsInternal,
1146 NtGdiStrokeAndFillPath,
1147 NtGdiStrokePath,
1148 NtGdiTransparentBlt,
1149 NtGdiUnrealizeObject,
1150 NtGdiUpdateColors,
1151 NtGdiWidenPath,
1152 NtUserActivateKeyboardLayout,
1153 NtUserCallOneParam,
1154 NtUserCallTwoParam,
1155 NtUserChangeDisplaySettings,
1156 NtUserCountClipboardFormats,
1157 NtUserEnumDisplayDevices,
1158 NtUserEnumDisplayMonitors,
1159 NtUserEnumDisplaySettings,
1160 NtUserGetDisplayConfigBufferSizes,
1161 NtUserGetKeyNameText,
1162 NtUserGetKeyboardLayoutList,
1163 NtUserGetPriorityClipboardFormat,
1164 NtUserGetUpdatedClipboardFormats,
1165 NtUserIsClipboardFormatAvailable,
1166 NtUserMapVirtualKeyEx,
1167 NtUserScrollDC,
1168 NtUserSelectPalette,
1169 NtUserSetSysColors,
1170 NtUserShowCursor,
1171 NtUserSystemParametersInfo,
1172 NtUserSystemParametersInfoForDpi,
1173 NtUserToUnicodeEx,
1174 NtUserUnregisterHotKey,
1175 NtUserVkKeyScanEx,
1177 GetDCHook,
1178 SetDCHook,
1179 SetDIBits,
1180 SetHookFlags,
1181 __wine_get_brush_bitmap_info,
1182 __wine_get_file_outline_text_metric,
1183 __wine_get_icm_profile,
1184 __wine_get_vulkan_driver,
1185 __wine_get_wgl_driver,
1186 __wine_set_display_driver,
1187 __wine_set_visible_region,
1190 NTSTATUS gdi_init(void)
1192 pthread_mutexattr_t attr;
1193 unsigned int dpi;
1195 pthread_mutexattr_init( &attr );
1196 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
1197 pthread_mutex_init( &gdi_lock, &attr );
1198 pthread_mutexattr_destroy( &attr );
1200 NtQuerySystemInformation( SystemBasicInformation, &system_info, sizeof(system_info), NULL );
1201 init_gdi_shared();
1202 if (!gdi_shared) return STATUS_NO_MEMORY;
1204 dpi = font_init();
1205 init_stock_objects( dpi );
1206 return 0;
1209 NTSTATUS callbacks_init( void *args )
1211 user_callbacks = *(const struct user_callbacks **)args;
1212 *(const struct unix_funcs **)args = &unix_funcs;
1213 return 0;