dinput: Keep an internal refcount for dinput devices references.
[wine.git] / dlls / win32u / winstation.c
blob145e12a8e1f2274df89405793a78500b8f49a73a
1 /*
2 * Window stations and desktops
4 * Copyright 2002 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 "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "ntuser.h"
31 #include "ddk/wdm.h"
32 #include "ntgdi_private.h"
33 #include "ntuser_private.h"
34 #include "wine/server.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(winstation);
38 WINE_DECLARE_DEBUG_CHANNEL(win);
41 #define DESKTOP_ALL_ACCESS 0x01ff
43 /***********************************************************************
44 * NtUserCreateWindowStation (win32u.@)
46 HWINSTA WINAPI NtUserCreateWindowStation( OBJECT_ATTRIBUTES *attr, ACCESS_MASK access, ULONG arg3,
47 ULONG arg4, ULONG arg5, ULONG arg6, ULONG arg7 )
49 HANDLE ret;
51 if (attr->ObjectName->Length >= MAX_PATH * sizeof(WCHAR))
53 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE );
54 return 0;
57 SERVER_START_REQ( create_winstation )
59 req->flags = 0;
60 req->access = access;
61 req->attributes = attr->Attributes;
62 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
63 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
64 wine_server_call_err( req );
65 ret = wine_server_ptr_handle( reply->handle );
67 SERVER_END_REQ;
68 return ret;
71 /******************************************************************************
72 * NtUserOpenWindowStation (win32u.@)
74 HWINSTA WINAPI NtUserOpenWindowStation( OBJECT_ATTRIBUTES *attr, ACCESS_MASK access )
76 HANDLE ret = 0;
78 SERVER_START_REQ( open_winstation )
80 req->access = access;
81 req->attributes = attr->Attributes;
82 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
83 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
84 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->handle );
86 SERVER_END_REQ;
87 return ret;
90 /***********************************************************************
91 * NtUserCloseWindowStation (win32u.@)
93 BOOL WINAPI NtUserCloseWindowStation( HWINSTA handle )
95 BOOL ret;
96 SERVER_START_REQ( close_winstation )
98 req->handle = wine_server_obj_handle( handle );
99 ret = !wine_server_call_err( req );
101 SERVER_END_REQ;
102 return ret;
105 /***********************************************************************
106 * NtUSerGetProcessWindowStation (win32u.@)
108 HWINSTA WINAPI NtUserGetProcessWindowStation(void)
110 HWINSTA ret = 0;
112 SERVER_START_REQ( get_process_winstation )
114 if (!wine_server_call_err( req ))
115 ret = wine_server_ptr_handle( reply->handle );
117 SERVER_END_REQ;
118 return ret;
121 /***********************************************************************
122 * NtUserSetProcessWindowStation (win32u.@)
124 BOOL WINAPI NtUserSetProcessWindowStation( HWINSTA handle )
126 BOOL ret;
128 SERVER_START_REQ( set_process_winstation )
130 req->handle = wine_server_obj_handle( handle );
131 ret = !wine_server_call_err( req );
133 SERVER_END_REQ;
134 return ret;
137 /***********************************************************************
138 * NtUserCreateDesktopEx (win32u.@)
140 HDESK WINAPI NtUserCreateDesktopEx( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *device,
141 DEVMODEW *devmode, DWORD flags, ACCESS_MASK access,
142 ULONG heap_size )
144 HANDLE ret;
146 if ((device && device->Length) || devmode)
148 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
149 return 0;
151 if (attr->ObjectName->Length >= MAX_PATH * sizeof(WCHAR))
153 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE );
154 return 0;
156 SERVER_START_REQ( create_desktop )
158 req->flags = flags;
159 req->access = access;
160 req->attributes = attr->Attributes;
161 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
162 wine_server_call_err( req );
163 ret = wine_server_ptr_handle( reply->handle );
165 SERVER_END_REQ;
166 return ret;
169 /***********************************************************************
170 * NtUserOpenDesktop (win32u.@)
172 HDESK WINAPI NtUserOpenDesktop( OBJECT_ATTRIBUTES *attr, DWORD flags, ACCESS_MASK access )
174 HANDLE ret = 0;
175 if (attr->ObjectName->Length >= MAX_PATH * sizeof(WCHAR))
177 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE );
178 return 0;
180 SERVER_START_REQ( open_desktop )
182 req->winsta = wine_server_obj_handle( attr->RootDirectory );
183 req->flags = flags;
184 req->access = access;
185 req->attributes = attr->Attributes;
186 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
187 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->handle );
189 SERVER_END_REQ;
190 return ret;
193 /***********************************************************************
194 * NtUserCloseDesktop (win32u.@)
196 BOOL WINAPI NtUserCloseDesktop( HDESK handle )
198 BOOL ret;
199 SERVER_START_REQ( close_desktop )
201 req->handle = wine_server_obj_handle( handle );
202 ret = !wine_server_call_err( req );
204 SERVER_END_REQ;
205 return ret;
208 /***********************************************************************
209 * NtUserGetThreadDesktop (win32u.@)
211 HDESK WINAPI NtUserGetThreadDesktop( DWORD thread )
213 HDESK ret = 0;
215 SERVER_START_REQ( get_thread_desktop )
217 req->tid = thread;
218 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->handle );
220 SERVER_END_REQ;
221 return ret;
224 /***********************************************************************
225 * NtUserSetThreadDesktop (win32u.@)
227 BOOL WINAPI NtUserSetThreadDesktop( HDESK handle )
229 BOOL ret;
231 SERVER_START_REQ( set_thread_desktop )
233 req->handle = wine_server_obj_handle( handle );
234 ret = !wine_server_call_err( req );
236 SERVER_END_REQ;
238 if (ret) /* reset the desktop windows */
240 struct user_thread_info *thread_info = get_user_thread_info();
241 struct user_key_state_info *key_state_info = thread_info->key_state;
242 thread_info->client_info.top_window = 0;
243 thread_info->client_info.msg_window = 0;
244 if (key_state_info) key_state_info->time = 0;
246 return ret;
249 /***********************************************************************
250 * NtUserOpenInputDesktop (win32u.@)
252 HDESK WINAPI NtUserOpenInputDesktop( DWORD flags, BOOL inherit, ACCESS_MASK access )
254 HANDLE ret = 0;
256 TRACE( "(%x,%i,%x)\n", (int)flags, inherit, (int)access );
258 if (flags)
259 FIXME( "partial stub flags %08x\n", (int)flags );
261 SERVER_START_REQ( open_input_desktop )
263 req->flags = flags;
264 req->access = access;
265 req->attributes = inherit ? OBJ_INHERIT : 0;
266 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->handle );
268 SERVER_END_REQ;
270 return ret;
273 /***********************************************************************
274 * NtUserGetObjectInformation (win32u.@)
276 BOOL WINAPI NtUserGetObjectInformation( HANDLE handle, INT index, void *info,
277 DWORD len, DWORD *needed )
279 BOOL ret;
281 static const WCHAR desktopW[] = {'D','e','s','k','t','o','p',0};
282 static const WCHAR window_stationW[] = {'W','i','n','d','o','w','S','t','a','t','i','o','n',0};
284 switch(index)
286 case UOI_FLAGS:
288 USEROBJECTFLAGS *obj_flags = info;
289 if (needed) *needed = sizeof(*obj_flags);
290 if (len < sizeof(*obj_flags))
292 RtlSetLastWin32Error( ERROR_BUFFER_OVERFLOW );
293 return FALSE;
295 SERVER_START_REQ( set_user_object_info )
297 req->handle = wine_server_obj_handle( handle );
298 req->flags = 0;
299 ret = !wine_server_call_err( req );
300 if (ret)
302 /* FIXME: inherit flag */
303 obj_flags->dwFlags = reply->old_obj_flags;
306 SERVER_END_REQ;
308 return ret;
310 case UOI_TYPE:
311 SERVER_START_REQ( set_user_object_info )
313 req->handle = wine_server_obj_handle( handle );
314 req->flags = 0;
315 ret = !wine_server_call_err( req );
316 if (ret)
318 size_t size = reply->is_desktop ? sizeof(desktopW) : sizeof(window_stationW);
319 if (needed) *needed = size;
320 if (len < size)
322 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
323 ret = FALSE;
325 else memcpy( info, reply->is_desktop ? desktopW : window_stationW, size );
328 SERVER_END_REQ;
329 return ret;
331 case UOI_NAME:
333 WCHAR buffer[MAX_PATH];
334 SERVER_START_REQ( set_user_object_info )
336 req->handle = wine_server_obj_handle( handle );
337 req->flags = 0;
338 wine_server_set_reply( req, buffer, sizeof(buffer) - sizeof(WCHAR) );
339 ret = !wine_server_call_err( req );
340 if (ret)
342 size_t size = wine_server_reply_size( reply );
343 buffer[size / sizeof(WCHAR)] = 0;
344 size += sizeof(WCHAR);
345 if (needed) *needed = size;
346 if (len < size)
348 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
349 ret = FALSE;
351 else memcpy( info, buffer, size );
354 SERVER_END_REQ;
356 return ret;
358 case UOI_USER_SID:
359 FIXME( "not supported index %d\n", index );
360 /* fall through */
361 default:
362 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
363 return FALSE;
367 /***********************************************************************
368 * NtUserSetObjectInformation (win32u.@)
370 BOOL WINAPI NtUserSetObjectInformation( HANDLE handle, INT index, void *info, DWORD len )
372 BOOL ret;
373 const USEROBJECTFLAGS *obj_flags = info;
375 if (index != UOI_FLAGS || !info || len < sizeof(*obj_flags))
377 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
378 return FALSE;
380 /* FIXME: inherit flag */
381 SERVER_START_REQ( set_user_object_info )
383 req->handle = wine_server_obj_handle( handle );
384 req->flags = SET_USER_OBJECT_SET_FLAGS;
385 req->obj_flags = obj_flags->dwFlags;
386 ret = !wine_server_call_err( req );
388 SERVER_END_REQ;
389 return ret;
392 #ifdef _WIN64
393 static inline TEB64 *NtCurrentTeb64(void) { return NULL; }
394 #else
395 static inline TEB64 *NtCurrentTeb64(void) { return (TEB64 *)NtCurrentTeb()->GdiBatchCount; }
396 #endif
398 HWND get_desktop_window(void)
400 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
402 if (thread_info->top_window) return UlongToHandle( thread_info->top_window );
405 SERVER_START_REQ( get_desktop_window )
407 req->force = 0;
408 if (!wine_server_call( req ))
410 thread_info->top_window = reply->top_window;
411 thread_info->msg_window = reply->msg_window;
414 SERVER_END_REQ;
416 if (!thread_info->top_window)
418 static const WCHAR appnameW[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s',
419 '\\','s','y','s','t','e','m','3','2','\\','e','x','p','l','o','r','e','r','.','e','x','e',0};
420 static const WCHAR cmdlineW[] = {'"','C',':','\\','w','i','n','d','o','w','s','\\',
421 's','y','s','t','e','m','3','2','\\','e','x','p','l','o','r','e','r','.','e','x','e','"',
422 ' ','/','d','e','s','k','t','o','p',0};
423 static const WCHAR system_dir[] = {'C',':','\\','w','i','n','d','o','w','s','\\',
424 's','y','s','t','e','m','3','2','\\',0};
425 RTL_USER_PROCESS_PARAMETERS params = { sizeof(params), sizeof(params) };
426 PS_ATTRIBUTE_LIST ps_attr;
427 PS_CREATE_INFO create_info;
428 WCHAR desktop[MAX_PATH];
429 PEB *peb = NtCurrentTeb()->Peb;
430 HANDLE process, thread;
431 unsigned int status;
433 SERVER_START_REQ( set_user_object_info )
435 req->handle = wine_server_obj_handle( NtUserGetThreadDesktop(GetCurrentThreadId()) );
436 req->flags = SET_USER_OBJECT_GET_FULL_NAME;
437 wine_server_set_reply( req, desktop, sizeof(desktop) - sizeof(WCHAR) );
438 if (!wine_server_call( req ))
440 size_t size = wine_server_reply_size( reply );
441 desktop[size / sizeof(WCHAR)] = 0;
442 TRACE( "starting explorer for desktop %s\n", debugstr_w(desktop) );
444 else
445 desktop[0] = 0;
447 SERVER_END_REQ;
449 params.Flags = PROCESS_PARAMS_FLAG_NORMALIZED;
450 params.Environment = peb->ProcessParameters->Environment;
451 params.EnvironmentSize = peb->ProcessParameters->EnvironmentSize;
452 params.hStdError = peb->ProcessParameters->hStdError;
453 RtlInitUnicodeString( &params.CurrentDirectory.DosPath, system_dir );
454 RtlInitUnicodeString( &params.ImagePathName, appnameW + 4 );
455 RtlInitUnicodeString( &params.CommandLine, cmdlineW );
456 RtlInitUnicodeString( &params.WindowTitle, appnameW + 4 );
457 RtlInitUnicodeString( &params.Desktop, desktop );
459 ps_attr.TotalLength = sizeof(ps_attr);
460 ps_attr.Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
461 ps_attr.Attributes[0].Size = sizeof(appnameW) - sizeof(WCHAR);
462 ps_attr.Attributes[0].ValuePtr = (WCHAR *)appnameW;
463 ps_attr.Attributes[0].ReturnLength = NULL;
465 if (NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR])
467 NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = TRUE;
468 status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
469 NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, &params,
470 &create_info, &ps_attr );
471 NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = FALSE;
473 else
474 status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
475 NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, &params,
476 &create_info, &ps_attr );
477 if (!status)
479 NtResumeThread( thread, NULL );
480 TRACE_(win)( "started explorer\n" );
481 NtUserWaitForInputIdle( process, 10000, FALSE );
482 NtClose( thread );
483 NtClose( process );
485 else ERR_(win)( "failed to start explorer %x\n", status );
487 SERVER_START_REQ( get_desktop_window )
489 req->force = 1;
490 if (!wine_server_call( req ))
492 thread_info->top_window = reply->top_window;
493 thread_info->msg_window = reply->msg_window;
496 SERVER_END_REQ;
499 if (!thread_info->top_window ||
500 !user_driver->pCreateDesktopWindow( UlongToHandle( thread_info->top_window )))
501 ERR_(win)( "failed to create desktop window\n" );
503 register_builtin_classes();
504 return UlongToHandle( thread_info->top_window );
507 static HANDLE get_winstations_dir_handle(void)
509 char bufferA[64];
510 WCHAR buffer[64];
511 UNICODE_STRING str;
512 OBJECT_ATTRIBUTES attr;
513 NTSTATUS status;
514 HANDLE dir;
516 sprintf( bufferA, "\\Sessions\\%u\\Windows\\WindowStations", (int)NtCurrentTeb()->Peb->SessionId );
517 str.Buffer = buffer;
518 str.Length = str.MaximumLength = asciiz_to_unicode( buffer, bufferA ) - sizeof(WCHAR);
519 InitializeObjectAttributes( &attr, &str, 0, 0, NULL );
520 status = NtOpenDirectoryObject( &dir, DIRECTORY_CREATE_OBJECT | DIRECTORY_TRAVERSE, &attr );
521 return status ? 0 : dir;
524 /***********************************************************************
525 * get_default_desktop
527 * Get the name of the desktop to use for this app if not specified explicitly.
529 static const WCHAR *get_default_desktop( void *buf, size_t buf_size )
531 const WCHAR *p, *appname = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer;
532 KEY_VALUE_PARTIAL_INFORMATION *info = buf;
533 WCHAR *buffer = buf;
534 HKEY tmpkey, appkey;
535 DWORD len;
537 static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',0};
539 if ((p = wcsrchr( appname, '/' ))) appname = p + 1;
540 if ((p = wcsrchr( appname, '\\' ))) appname = p + 1;
541 len = lstrlenW(appname);
542 if (len > MAX_PATH) return defaultW;
543 memcpy( buffer, appname, len * sizeof(WCHAR) );
544 asciiz_to_unicode( buffer + len, "\\Explorer" );
546 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Explorer */
547 if ((tmpkey = reg_open_hkcu_key( "Software\\Wine\\AppDefaults" )))
549 appkey = reg_open_key( tmpkey, buffer, lstrlenW(buffer) * sizeof(WCHAR) );
550 NtClose( tmpkey );
551 if (appkey)
553 len = query_reg_ascii_value( appkey, "Desktop", info, buf_size );
554 NtClose( appkey );
555 if (len) return (const WCHAR *)info->Data;
559 /* @@ Wine registry key: HKCU\Software\Wine\Explorer */
560 if ((appkey = reg_open_hkcu_key( "Software\\Wine\\Explorer" )))
562 len = query_reg_ascii_value( appkey, "Desktop", info, buf_size );
563 NtClose( appkey );
564 if (len) return (const WCHAR *)info->Data;
567 return defaultW;
570 /***********************************************************************
571 * winstation_init
573 * Connect to the process window station and desktop.
575 void winstation_init(void)
577 RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters;
578 WCHAR *winstation = NULL, *desktop = NULL, *buffer = NULL;
579 HANDLE handle, dir = NULL;
580 OBJECT_ATTRIBUTES attr;
581 UNICODE_STRING str;
583 static const WCHAR winsta0[] = {'W','i','n','S','t','a','0',0};
585 if (params->Desktop.Length)
587 buffer = malloc( params->Desktop.Length + sizeof(WCHAR) );
588 memcpy( buffer, params->Desktop.Buffer, params->Desktop.Length );
589 buffer[params->Desktop.Length / sizeof(WCHAR)] = 0;
590 if ((desktop = wcschr( buffer, '\\' )))
592 *desktop++ = 0;
593 winstation = buffer;
595 else desktop = buffer;
598 /* set winstation if explicitly specified, or if we don't have one yet */
599 if (buffer || !NtUserGetProcessWindowStation())
601 str.Buffer = (WCHAR *)(winstation ? winstation : winsta0);
602 str.Length = str.MaximumLength = lstrlenW( str.Buffer ) * sizeof(WCHAR);
603 dir = get_winstations_dir_handle();
604 InitializeObjectAttributes( &attr, &str, OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
605 dir, NULL );
607 handle = NtUserCreateWindowStation( &attr, WINSTA_ALL_ACCESS, 0, 0, 0, 0, 0 );
608 if (handle)
610 NtUserSetProcessWindowStation( handle );
611 /* only WinSta0 is visible */
612 if (!winstation || !wcsicmp( winstation, winsta0 ))
614 USEROBJECTFLAGS flags;
615 flags.fInherit = FALSE;
616 flags.fReserved = FALSE;
617 flags.dwFlags = WSF_VISIBLE;
618 NtUserSetObjectInformation( handle, UOI_FLAGS, &flags, sizeof(flags) );
622 if (buffer || !NtUserGetThreadDesktop( GetCurrentThreadId() ))
624 char buffer[4096];
625 str.Buffer = (WCHAR *)(desktop ? desktop : get_default_desktop( buffer, sizeof(buffer) ));
626 str.Length = str.MaximumLength = lstrlenW( str.Buffer ) * sizeof(WCHAR);
627 if (!dir) dir = get_winstations_dir_handle();
628 InitializeObjectAttributes( &attr, &str, OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
629 dir, NULL );
631 handle = NtUserCreateDesktopEx( &attr, NULL, NULL, 0, DESKTOP_ALL_ACCESS, 0 );
632 if (handle) NtUserSetThreadDesktop( handle );
634 NtClose( dir );
635 free( buffer );