desktop.c 11.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Explorer desktop support
 *
 * Copyright 2006 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
19 20
 */

21 22
#include "config.h"
#include "wine/port.h"
23
#include <stdio.h>
24
#include "wine/unicode.h"
25 26 27

#define OEMRESOURCE

28 29 30 31 32 33
#include <windows.h>
#include <wine/debug.h>
#include "explorer_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(explorer);

34
#define DESKTOP_CLASS_ATOM ((LPCWSTR)MAKEINTATOM(32769))
35
#define DESKTOP_ALL_ACCESS 0x01ff
36

37 38
static BOOL using_root;

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
/* screen saver handler */
static BOOL start_screensaver( void )
{
    if (using_root)
    {
        const char *argv[3] = { "xdg-screensaver", "activate", NULL };
        int pid = spawnvp( _P_NOWAIT, argv[0], argv );
        if (pid > 0)
        {
            WINE_TRACE( "started process %d\n", pid );
            return TRUE;
        }
    }
    return FALSE;
}

55
/* window procedure for the desktop window */
56 57
static LRESULT WINAPI desktop_wnd_proc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
{
58
    WINE_TRACE( "got msg %04x wp %lx lp %lx\n", message, wp, lp );
59 60 61 62

    switch(message)
    {
    case WM_SYSCOMMAND:
63 64 65 66 67 68 69 70
        switch(wp & 0xfff0)
        {
        case SC_CLOSE:
            ExitWindows( 0, 0 );
            break;
        case SC_SCREENSAVE:
            return start_screensaver();
        }
71
        return 0;
72

73 74 75 76
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;

77 78 79 80 81 82 83
    case WM_SETCURSOR:
        return (LRESULT)SetCursor( LoadCursorA( 0, (LPSTR)IDC_ARROW ) );

    case WM_NCHITTEST:
        return HTCLIENT;

    case WM_ERASEBKGND:
84
        if (!using_root) PaintDesktop( (HDC)wp );
85 86 87 88 89 90
        return TRUE;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            BeginPaint( hwnd, &ps );
91
            if (!using_root && ps.fErase) PaintDesktop( ps.hdc );
92 93 94
            EndPaint( hwnd, &ps );
        }
        return 0;
95 96 97 98 99 100 101

    default:
        return DefWindowProcW( hwnd, message, wp, lp );
    }
}

/* create the desktop and the associated X11 window, and make it the current desktop */
102
static unsigned long create_desktop( const WCHAR *name, unsigned int width, unsigned int height )
103
{
104
    static const WCHAR rootW[] = {'r','o','o','t',0};
105 106 107
    HMODULE x11drv = GetModuleHandleA( "winex11.drv" );
    HDESK desktop;
    unsigned long xwin = 0;
108
    unsigned long (CDECL *create_desktop_func)(unsigned int, unsigned int);
109

110
    desktop = CreateDesktopW( name, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
111 112
    if (!desktop)
    {
113
        WINE_ERR( "failed to create desktop %s error %d\n", wine_dbgstr_w(name), GetLastError() );
114 115 116
        ExitProcess( 1 );
    }
    /* magic: desktop "root" means use the X11 root window */
117
    if (x11drv && strcmpiW( name, rootW ))
118 119 120
    {
        create_desktop_func = (void *)GetProcAddress( x11drv, "wine_create_desktop" );
        if (create_desktop_func) xwin = create_desktop_func( width, height );
121
    }
122 123
    SetThreadDesktop( desktop );
    return xwin;
124 125
}

126 127 128 129 130 131 132 133 134 135 136 137 138
/* parse the desktop size specification */
static BOOL parse_size( const WCHAR *size, unsigned int *width, unsigned int *height )
{
    WCHAR *end;

    *width = strtoulW( size, &end, 10 );
    if (end == size) return FALSE;
    if (*end != 'x') return FALSE;
    size = end + 1;
    *height = strtoulW( size, &end, 10 );
    return !*end;
}

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
/* retrieve the desktop name to use if not specified on the command line */
static const WCHAR *get_default_desktop_name(void)
{
    static const WCHAR desktopW[] = {'D','e','s','k','t','o','p',0};
    static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',0};
    static const WCHAR explorer_keyW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
                                          'E','x','p','l','o','r','e','r',0};
    static WCHAR buffer[MAX_PATH];
    DWORD size = sizeof(buffer);
    HDESK desk = GetThreadDesktop( GetCurrentThreadId() );
    WCHAR *ret = NULL;
    HKEY hkey;

    if (desk && GetUserObjectInformationW( desk, UOI_NAME, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
    {
        if (strcmpiW( buffer, defaultW )) return buffer;
    }

    /* @@ Wine registry key: HKCU\Software\Wine\Explorer */
    if (!RegOpenKeyW( HKEY_CURRENT_USER, explorer_keyW, &hkey ))
    {
        if (!RegQueryValueExW( hkey, desktopW, 0, NULL, (LPBYTE)buffer, &size )) ret = buffer;
        RegCloseKey( hkey );
    }
    return ret;
}

166 167
/* retrieve the default desktop size from the registry */
static BOOL get_default_desktop_size( const WCHAR *name, unsigned int *width, unsigned int *height )
168
{
169 170 171
    static const WCHAR desktop_keyW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
                                         'E','x','p','l','o','r','e','r','\\',
                                         'D','e','s','k','t','o','p','s',0};
172
    HKEY hkey;
173
    WCHAR buffer[64];
174
    DWORD size = sizeof(buffer);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    BOOL found = FALSE;

    *width = 800;
    *height = 600;

    /* @@ Wine registry key: HKCU\Software\Wine\Explorer\Desktops */
    if (!RegOpenKeyW( HKEY_CURRENT_USER, desktop_keyW, &hkey ))
    {
        if (!RegQueryValueExW( hkey, name, 0, NULL, (LPBYTE)buffer, &size ))
        {
            found = TRUE;
            if (!parse_size( buffer, width, height )) *width = *height = 0;
        }
        RegCloseKey( hkey );
    }
    return found;
191 192
}

193 194 195 196 197 198
static void initialize_display_settings( HWND desktop )
{
    static const WCHAR display_device_guid_propW[] = {
        '_','_','w','i','n','e','_','d','i','s','p','l','a','y','_',
        'd','e','v','i','c','e','_','g','u','i','d',0 };
    GUID guid;
199
    RPC_CSTR guid_str;
200
    ATOM guid_atom;
201
    DEVMODEW dmW;
202 203

    UuidCreate( &guid );
204 205
    UuidToStringA( &guid, &guid_str );
    WINE_TRACE( "display guid %s\n", guid_str );
206

207
    guid_atom = GlobalAddAtomA( (LPCSTR)guid_str );
208 209
    SetPropW( desktop, display_device_guid_propW, ULongToHandle(guid_atom) );

210
    RpcStringFreeA( &guid_str );
211 212 213 214 215 216 217 218 219 220

    /* Store current display mode in the registry */
    if (EnumDisplaySettingsExW( NULL, ENUM_CURRENT_SETTINGS, &dmW, 0 ))
    {
        WINE_TRACE( "Current display mode %ux%u %u bpp %u Hz\n", dmW.dmPelsWidth,
                    dmW.dmPelsHeight, dmW.dmBitsPerPel, dmW.dmDisplayFrequency );
        ChangeDisplaySettingsExW( NULL, &dmW, 0,
                                  CDS_GLOBAL | CDS_NORESET | CDS_UPDATEREGISTRY,
                                  NULL );
    }
221 222
}

223
static void set_desktop_window_title( HWND hwnd, const WCHAR *name )
224 225 226 227 228 229 230 231 232 233 234 235
{
    static const WCHAR desktop_nameW[] = {'W','i','n','e',' ','d','e','s','k','t','o','p',0};
    static const WCHAR desktop_name_separatorW[] = {' ', '-', ' ', 0};
    WCHAR *window_titleW = NULL;
    int window_title_len;

    if (!name[0])
    {
        SetWindowTextW( hwnd, desktop_nameW );
        return;
    }

236
    window_title_len = strlenW(name) * sizeof(WCHAR)
237 238 239 240 241 242 243 244 245
                     + sizeof(desktop_name_separatorW)
                     + sizeof(desktop_nameW);
    window_titleW = HeapAlloc( GetProcessHeap(), 0, window_title_len );
    if (!window_titleW)
    {
        SetWindowTextW( hwnd, desktop_nameW );
        return;
    }

246
    strcpyW( window_titleW, name );
247 248 249 250 251 252 253
    strcatW( window_titleW, desktop_name_separatorW );
    strcatW( window_titleW, desktop_nameW );

    SetWindowTextW( hwnd, window_titleW );
    HeapFree( GetProcessHeap(), 0, window_titleW );
}

254
/* main desktop management function */
255
void manage_desktop( WCHAR *arg )
256
{
257
    static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',0};
258
    static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
259
    MSG msg;
260
    HWND hwnd, msg_hwnd;
261 262
    unsigned long xwin = 0;
    unsigned int width, height;
263 264 265
    WCHAR *cmdline = NULL;
    WCHAR *p = arg;
    const WCHAR *name = NULL;
266

267 268 269 270 271 272 273 274
    /* get the rest of the command line (if any) */
    while (*p && !isspace(*p)) p++;
    if (*p)
    {
        *p++ = 0;
        while (*p && isspace(*p)) p++;
        if (*p) cmdline = p;
    }
275

276 277 278 279 280
    /* parse the desktop option */
    /* the option is of the form /desktop=name[,widthxheight] */
    if (*arg == '=' || *arg == ',')
    {
        arg++;
281
        name = arg;
282 283
        if ((p = strchrW( arg, ',' ))) *p++ = 0;
        if (!p || !parse_size( p, &width, &height ))
284
            get_default_desktop_size( name, &width, &height );
285
    }
286 287 288 289
    else if ((name = get_default_desktop_name()))
    {
        if (!get_default_desktop_size( name, &width, &height )) width = height = 0;
    }
290
    else  /* check for the X11 driver key for backwards compatibility (to be removed) */
291
    {
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
        static const WCHAR desktopW[] = {'D','e','s','k','t','o','p',0};
        static const WCHAR x11_keyW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
                                         'X','1','1',' ','D','r','i','v','e','r',0};
        HKEY hkey;
        WCHAR buffer[64];
        DWORD size = sizeof(buffer);

        width = height = 0;
        /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */
        if (!RegOpenKeyW( HKEY_CURRENT_USER, x11_keyW, &hkey ))
        {
            if (!RegQueryValueExW( hkey, desktopW, 0, NULL, (LPBYTE)buffer, &size ))
            {
                name = defaultW;
                if (!parse_size( buffer, &width, &height )) width = height = 0;
            }
            RegCloseKey( hkey );
        }
310 311
    }

312 313
    if (name && width && height) xwin = create_desktop( name, width, height );

314
    if (!xwin) using_root = TRUE; /* using the root window */
315 316 317 318

    /* create the desktop window */
    hwnd = CreateWindowExW( 0, DESKTOP_CLASS_ATOM, NULL,
                            WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
319 320 321
                            GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN),
                            GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN),
                            0, 0, 0, NULL );
322 323 324 325 326

    /* create the HWND_MESSAGE parent */
    msg_hwnd = CreateWindowExW( 0, messageW, NULL, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                                0, 0, 100, 100, 0, 0, 0, NULL );

327 328 329 330
    if (hwnd == GetDesktopWindow())
    {
        SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)desktop_wnd_proc );
        SendMessageW( hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIconW( 0, MAKEINTRESOURCEW(OIC_WINLOGO)));
331
        if (name) set_desktop_window_title( hwnd, name );
332 333
        SystemParametersInfoA( SPI_SETDESKPATTERN, -1, NULL, FALSE );
        SetDeskWallPaper( (LPSTR)-1 );
334
        initialize_display_settings( hwnd );
335
        initialize_appbar();
336 337 338 339 340 341 342 343
        initialize_systray();
    }
    else
    {
        DestroyWindow( hwnd );  /* someone beat us to it */
        hwnd = 0;
    }

344 345
    if (GetAncestor( msg_hwnd, GA_PARENT )) DestroyWindow( msg_hwnd );  /* someone beat us to it */

346 347 348
    /* if we have a command line, execute it */
    if (cmdline)
    {
349
        STARTUPINFOW si;
350 351 352 353
        PROCESS_INFORMATION pi;

        memset( &si, 0, sizeof(si) );
        si.cb = sizeof(si);
354 355
        WINE_TRACE( "starting %s\n", wine_dbgstr_w(cmdline) );
        if (CreateProcessW( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
356 357 358 359 360 361 362 363 364 365 366 367 368
        {
            CloseHandle( pi.hThread );
            CloseHandle( pi.hProcess );
        }
    }

    /* run the desktop message loop */
    if (hwnd)
    {
        WINE_TRACE( "desktop message loop starting on hwnd %p\n", hwnd );
        while (GetMessageW( &msg, 0, 0, 0 )) DispatchMessageW( &msg );
        WINE_TRACE( "desktop message loop exiting for hwnd %p\n", hwnd );
    }
369

370
    ExitProcess( 0 );
371
}