user: Call the A version of message functions from 16-bit code.
[wine] / dlls / user / user_main.c
1 /*
2  * USER initialization code
3  *
4  * Copyright 2000 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winreg.h"
29 #include "tlhelp32.h"
30
31 #include "controls.h"
32 #include "user_private.h"
33 #include "win.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(graphics);
38
39 #define DESKTOP_ALL_ACCESS 0x01ff
40
41 WORD USER_HeapSel = 0;  /* USER heap selector */
42 HMODULE user32_module = 0;
43
44 static SYSLEVEL USER_SysLevel;
45 static CRITICAL_SECTION_DEBUG critsect_debug =
46 {
47     0, 0, &USER_SysLevel.crst,
48     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
49       0, 0, { (DWORD_PTR)(__FILE__ ": USER_SysLevel") }
50 };
51 static SYSLEVEL USER_SysLevel = { { &critsect_debug, -1, 0, 0, 0, 0 }, 2 };
52
53 static HPALETTE (WINAPI *pfnGDISelectPalette)( HDC hdc, HPALETTE hpal, WORD bkgnd );
54 static UINT (WINAPI *pfnGDIRealizePalette)( HDC hdc );
55 static HPALETTE hPrimaryPalette;
56
57 static DWORD exiting_thread_id;
58
59 extern void WDML_NotifyThreadDetach(void);
60
61
62 /***********************************************************************
63  *           USER_Lock
64  */
65 void USER_Lock(void)
66 {
67     _EnterSysLevel( &USER_SysLevel );
68 }
69
70
71 /***********************************************************************
72  *           USER_Unlock
73  */
74 void USER_Unlock(void)
75 {
76     _LeaveSysLevel( &USER_SysLevel );
77 }
78
79
80 /***********************************************************************
81  *           USER_CheckNotLock
82  *
83  * Make sure that we don't hold the user lock.
84  */
85 void USER_CheckNotLock(void)
86 {
87     _CheckNotSysLevel( &USER_SysLevel );
88 }
89
90
91 /***********************************************************************
92  *              UserSelectPalette (Not a Windows API)
93  */
94 static HPALETTE WINAPI UserSelectPalette( HDC hDC, HPALETTE hPal, BOOL bForceBackground )
95 {
96     WORD wBkgPalette = 1;
97
98     if (!bForceBackground && (hPal != GetStockObject(DEFAULT_PALETTE)))
99     {
100         HWND hwnd = WindowFromDC( hDC );
101         if (hwnd)
102         {
103             HWND hForeground = GetForegroundWindow();
104             /* set primary palette if it's related to current active */
105             if (hForeground == hwnd || IsChild(hForeground,hwnd))
106             {
107                 wBkgPalette = 0;
108                 hPrimaryPalette = hPal;
109             }
110         }
111     }
112     return pfnGDISelectPalette( hDC, hPal, wBkgPalette);
113 }
114
115
116 /***********************************************************************
117  *              UserRealizePalette (USER32.@)
118  */
119 UINT WINAPI UserRealizePalette( HDC hDC )
120 {
121     UINT realized = pfnGDIRealizePalette( hDC );
122
123     /* do not send anything if no colors were changed */
124     if (realized && GetCurrentObject( hDC, OBJ_PAL ) == hPrimaryPalette)
125     {
126         /* send palette change notification */
127         HWND hWnd = WindowFromDC( hDC );
128         if (hWnd) SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0,
129                                        SMTO_ABORTIFHUNG, 2000, NULL );
130     }
131     return realized;
132 }
133
134
135 /***********************************************************************
136  *           palette_init
137  *
138  * Patch the function pointers in GDI for SelectPalette and RealizePalette
139  */
140 static void palette_init(void)
141 {
142     void **ptr;
143     HMODULE module = GetModuleHandleA( "gdi32" );
144     if (!module)
145     {
146         ERR( "cannot get GDI32 handle\n" );
147         return;
148     }
149     if ((ptr = (void**)GetProcAddress( module, "pfnSelectPalette" )))
150         pfnGDISelectPalette = InterlockedExchangePointer( ptr, UserSelectPalette );
151     else ERR( "cannot find pfnSelectPalette in GDI32\n" );
152     if ((ptr = (void**)GetProcAddress( module, "pfnRealizePalette" )))
153         pfnGDIRealizePalette = InterlockedExchangePointer( ptr, UserRealizePalette );
154     else ERR( "cannot find pfnRealizePalette in GDI32\n" );
155 }
156
157
158 /***********************************************************************
159  *           winstation_init
160  *
161  * Connect to the process window station and desktop.
162  */
163 static void winstation_init(void)
164 {
165     static const WCHAR WinSta0[] = {'W','i','n','S','t','a','0',0};
166     static const WCHAR Default[] = {'D','e','f','a','u','l','t',0};
167
168     STARTUPINFOW info;
169     WCHAR *winstation = NULL, *desktop = NULL, *buffer = NULL;
170     HANDLE handle;
171
172     GetStartupInfoW( &info );
173     if (info.lpDesktop && *info.lpDesktop)
174     {
175         buffer = HeapAlloc( GetProcessHeap(), 0, (strlenW(info.lpDesktop) + 1) * sizeof(WCHAR) );
176         strcpyW( buffer, info.lpDesktop );
177         if ((desktop = strchrW( buffer, '\\' )))
178         {
179             *desktop++ = 0;
180             winstation = buffer;
181         }
182         else desktop = buffer;
183     }
184
185     /* set winstation if explicitly specified, or if we don't have one yet */
186     if (buffer || !GetProcessWindowStation())
187     {
188         handle = CreateWindowStationW( winstation ? winstation : WinSta0, 0, WINSTA_ALL_ACCESS, NULL );
189         if (handle) SetProcessWindowStation( handle );
190     }
191     if (buffer || !GetThreadDesktop( GetCurrentThreadId() ))
192     {
193         handle = CreateDesktopW( desktop ? desktop : Default, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
194         if (handle) SetThreadDesktop( handle );
195     }
196     HeapFree( GetProcessHeap(), 0, buffer );
197 }
198
199
200 /***********************************************************************
201  *           USER initialisation routine
202  */
203 static BOOL process_attach(void)
204 {
205     HINSTANCE16 instance;
206
207     /* Create USER heap */
208     if ((instance = LoadLibrary16( "USER.EXE" )) >= 32) USER_HeapSel = instance | 7;
209     else
210     {
211         USER_HeapSel = GlobalAlloc16( GMEM_FIXED, 65536 );
212         LocalInit16( USER_HeapSel, 32, 65534 );
213     }
214
215     /* some Win9x dlls expect keyboard to be loaded */
216     if (GetVersion() & 0x80000000) LoadLibrary16( "keyboard.drv" );
217
218     winstation_init();
219
220     /* Initialize system colors and metrics */
221     SYSPARAMS_Init();
222
223     /* Setup palette function pointers */
224     palette_init();
225
226     /* Initialize built-in window classes */
227     CLASS_RegisterBuiltinClasses();
228
229     /* Initialize message spying */
230     if (!SPY_Init()) return FALSE;
231
232     return TRUE;
233 }
234
235
236 /**********************************************************************
237  *           USER_IsExitingThread
238  */
239 BOOL USER_IsExitingThread( DWORD tid )
240 {
241     return (tid == exiting_thread_id);
242 }
243
244
245 /**********************************************************************
246  *           thread_detach
247  */
248 static void thread_detach(void)
249 {
250     exiting_thread_id = GetCurrentThreadId();
251
252     WDML_NotifyThreadDetach();
253
254     WIN_DestroyThreadWindows( get_user_thread_info()->desktop );
255     CloseHandle( get_user_thread_info()->server_queue );
256
257     exiting_thread_id = 0;
258 }
259
260
261 /***********************************************************************
262  *           UserClientDllInitialize  (USER32.@)
263  *
264  * USER dll initialisation routine (exported as UserClientDllInitialize for compatibility).
265  */
266 BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved )
267 {
268     BOOL ret = TRUE;
269     switch(reason)
270     {
271     case DLL_PROCESS_ATTACH:
272         user32_module = inst;
273         ret = process_attach();
274         break;
275     case DLL_THREAD_DETACH:
276         thread_detach();
277         break;
278     case DLL_PROCESS_DETACH:
279         USER_unload_driver();
280         break;
281     }
282     return ret;
283 }
284
285
286 /***********************************************************************
287  *           USER_GetProcessHandleList(Internal)
288  */
289 static HANDLE *USER_GetProcessHandleList(void)
290 {
291     DWORD count, i, n;
292     HANDLE *list;
293     PROCESSENTRY32 pe;
294     HANDLE hSnapshot;
295     BOOL r;
296
297     hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
298     if (!hSnapshot)
299     {
300         ERR("cannot create snapshot\n");
301         return FALSE;
302     }
303
304     /* count the number of processes plus one */
305     for (count=0; ;count++)
306     {
307         pe.dwSize = sizeof pe;
308         if (count)
309             r = Process32Next( hSnapshot, &pe );
310         else
311             r = Process32First( hSnapshot, &pe );
312         if (!r)
313             break;
314     }
315
316     /* allocate memory make a list of the process handles */
317     list = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof(HANDLE) );
318     n=0;
319     for (i=0; i<count; i++)
320     {
321         pe.dwSize = sizeof pe;
322         if (i)
323             r = Process32Next( hSnapshot, &pe );
324         else
325             r = Process32First( hSnapshot, &pe );
326         if (!r)
327             break;
328
329         /* don't kill ourselves */
330         if (GetCurrentProcessId() == pe.th32ProcessID )
331             continue;
332
333         /* open the process so we don't can track it */
334         list[n] = OpenProcess( PROCESS_QUERY_INFORMATION|
335                                   PROCESS_TERMINATE,
336                                   FALSE, pe.th32ProcessID );
337
338         /* check it didn't terminate already */
339         if( list[n] )
340             n++;
341     }
342     list[n]=0;
343     CloseHandle( hSnapshot );
344
345     if (!r)
346         ERR("Error enumerating processes\n");
347
348     TRACE("return %lu processes\n", n);
349
350     return list;
351 }
352
353
354 /***********************************************************************
355  *              USER_KillProcesses (Internal)
356  */
357 static DWORD USER_KillProcesses(void)
358 {
359     DWORD n, r, i;
360     HANDLE *handles;
361     const DWORD dwShutdownTimeout = 10000;
362
363     TRACE("terminating other processes\n");
364
365     /* kill it and add it to our list of object to wait on */
366     handles = USER_GetProcessHandleList();
367     for (n=0; handles && handles[n]; n++)
368         TerminateProcess( handles[n], 0 );
369
370     /* wait for processes to exit */
371     for (i=0; i<n; i+=MAXIMUM_WAIT_OBJECTS)
372     {
373         int n_objs = ((n-i)>MAXIMUM_WAIT_OBJECTS) ? MAXIMUM_WAIT_OBJECTS : (n-i);
374         r = WaitForMultipleObjects( n_objs, &handles[i], TRUE, dwShutdownTimeout );
375         if (r==WAIT_TIMEOUT)
376             ERR("wait failed!\n");
377     }
378
379     /* close the handles */
380     for (i=0; i<n; i++)
381         CloseHandle( handles[i] );
382
383     HeapFree( GetProcessHeap(), 0, handles );
384
385     return n;
386 }
387
388
389 /***********************************************************************
390  *              USER_DoShutdown (Internal)
391  */
392 static void USER_DoShutdown(void)
393 {
394     DWORD i, n;
395     const DWORD nRetries = 10;
396
397     for (i=0; i<nRetries; i++)
398     {
399         n = USER_KillProcesses();
400         TRACE("Killed %ld processes, attempt %ld\n", n, i);
401         if(!n)
402             break;
403     }
404 }
405
406
407 /***********************************************************************
408  *              ExitWindowsEx (USER32.@)
409  */
410 BOOL WINAPI ExitWindowsEx( UINT flags, DWORD reason )
411 {
412     TRACE("(%x,%lx)\n", flags, reason);
413
414     if ((flags & EWX_FORCE) == 0)
415     {
416         HWND *list;
417
418         /* We have to build a list of all windows first, as in EnumWindows */
419         list = WIN_ListChildren( GetDesktopWindow() );
420         if (list)
421         {
422             HWND *phwnd;
423             UINT send_flags;
424             DWORD_PTR result=1;
425
426             /* Send a WM_QUERYENDSESSION / WM_ENDSESSION message pair to
427              * each window. Note: it might be better to send all the
428              * WM_QUERYENDSESSION messages, aggregate the results and then
429              * send all the WM_ENDSESSION messages with the results but
430              * that's not what Windows does.
431              */
432             send_flags=(flags & EWX_FORCEIFHUNG) ? SMTO_ABORTIFHUNG : SMTO_NORMAL;
433             for (phwnd = list; *phwnd; phwnd++)
434             {
435                 /* Make sure that the window still exists */
436                 if (!IsWindow( *phwnd )) continue;
437                 if (SendMessageTimeoutW( *phwnd, WM_QUERYENDSESSION, 0, 0, send_flags, 0, &result))
438                 {
439                     DWORD_PTR dummy;
440                     SendMessageTimeoutW( *phwnd, WM_ENDSESSION, result, 0, send_flags, 0, &dummy );
441                     if (!result) break;
442                 }
443             }
444             HeapFree( GetProcessHeap(), 0, list );
445
446             if (!result)
447                 return TRUE;
448         }
449     }
450
451     /* USER_DoShutdown will kill all processes except the current process */
452     USER_DoShutdown();
453
454     if (flags & EWX_REBOOT)
455     {
456         WCHAR winebootW[] = { 'w','i','n','e','b','o','o','t',0 };
457         PROCESS_INFORMATION pi;
458         STARTUPINFOW si;
459
460         memset( &si, 0, sizeof si );
461         si.cb = sizeof si;
462         if (CreateProcessW( NULL, winebootW, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
463         {
464             CloseHandle( pi.hProcess );
465             CloseHandle( pi.hThread );
466         }
467         else
468             MESSAGE("wine: Failed to start wineboot\n");
469     }
470
471     ExitProcess(0);
472     return TRUE;
473 }