From 757845c7c6a652a3bd3c292456831c4ddb09f1ef Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 1 Nov 2010 19:19:58 +0100 Subject: [PATCH] winex11: Manage the system tray stand-alone window in x11drv for non-desktop mode. --- dlls/winex11.drv/systray.c | 264 ++++++++++++++++++++++++++++++----------- dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 4 + 3 files changed, 202 insertions(+), 67 deletions(-) diff --git a/dlls/winex11.drv/systray.c b/dlls/winex11.drv/systray.c index 8ed1550d65e..e500a333026 100644 --- a/dlls/winex11.drv/systray.c +++ b/dlls/winex11.drv/systray.c @@ -55,22 +55,34 @@ struct tray_icon HWND tooltip; /* Icon tooltip */ UINT id; /* the unique id given by the app */ UINT callback_message; + int display; /* display index, or -1 if hidden */ WCHAR tiptext[256]; /* Tooltip text. If empty => tooltip disabled */ WCHAR tiptitle[64]; /* Tooltip title for ballon style tooltips. If empty => tooltip is not balloon style. */ }; static struct list icon_list = LIST_INIT( icon_list ); -static const WCHAR tray_classname[] = {'_','_','w','i','n','e','x','1','1','_','t','r','a','y','_','w','i','n','d','o','w',0}; +static const WCHAR icon_classname[] = {'_','_','w','i','n','e','x','1','1','_','t','r','a','y','_','i','c','o','n',0}; +static const WCHAR tray_classname[] = {'_','_','w','i','n','e','x','1','1','_','s','t','a','n','d','a','l','o','n','e','_','t','r','a','y',0}; +static BOOL show_icon( struct tray_icon *icon ); +static BOOL hide_icon( struct tray_icon *icon ); static BOOL delete_icon( struct tray_icon *icon ); #define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 +static Atom systray_atom; + +#define MIN_DISPLAYED 8 #define ICON_BORDER 2 +/* stand-alone tray window */ +static HWND standalone_tray; +static int icon_cx, icon_cy; +static unsigned int nb_displayed; + /* retrieves icon record by owner window and ID */ static struct tray_icon *get_icon(HWND owner, UINT id) { @@ -140,8 +152,105 @@ static void update_tooltip_text(struct tray_icon *icon) SendMessageW(icon->tooltip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti); } -/* window procedure for the tray window */ -static LRESULT WINAPI tray_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +/* get the size of the stand-alone tray window */ +static SIZE get_window_size(void) +{ + SIZE size; + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = icon_cx * max( nb_displayed, MIN_DISPLAYED ); + rect.bottom = icon_cy; + AdjustWindowRect( &rect, WS_CAPTION, FALSE ); + size.cx = rect.right - rect.left; + size.cy = rect.bottom - rect.top; + return size; +} + +/* get the position of an icon in the stand-alone tray */ +static POINT get_icon_pos( struct tray_icon *icon ) +{ + POINT pos; + + pos.x = icon_cx * icon->display; + pos.y = 0; + return pos; +} + +/* window procedure for the standalone tray window */ +static LRESULT WINAPI standalone_tray_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + switch (msg) + { + case WM_CLOSE: + ShowWindow( hwnd, SW_HIDE ); + show_systray = FALSE; + return 0; + case WM_DESTROY: + standalone_tray = 0; + break; + } + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +/* add an icon to the standalone tray window */ +static void add_to_standalone_tray( struct tray_icon *icon ) +{ + SIZE size; + POINT pos; + + if (!standalone_tray) + { + static const WCHAR winname[] = {'W','i','n','e',' ','S','y','s','t','e','m',' ','T','r','a','y',0}; + + size = get_window_size(); + standalone_tray = CreateWindowExW( 0, tray_classname, winname, WS_CAPTION | WS_SYSMENU, + CW_USEDEFAULT, CW_USEDEFAULT, size.cx, size.cy, 0, 0, 0, 0 ); + if (!standalone_tray) return; + } + + icon->display = nb_displayed; + pos = get_icon_pos( icon ); + icon->window = CreateWindowW( icon_classname, NULL, WS_CHILD | WS_VISIBLE, + pos.x, pos.y, icon_cx, icon_cy, standalone_tray, NULL, NULL, icon ); + if (!icon->window) + { + icon->display = -1; + return; + } + create_tooltip( icon ); + + nb_displayed++; + size = get_window_size(); + SetWindowPos( standalone_tray, 0, 0, 0, size.cx, size.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER ); + if (nb_displayed == 1 && show_systray) ShowWindow( standalone_tray, SW_SHOWNA ); + TRACE( "added %u now %d icons\n", icon->id, nb_displayed ); +} + +/* remove an icon from the stand-alone tray */ +static void remove_from_standalone_tray( struct tray_icon *icon ) +{ + struct tray_icon *ptr; + POINT pos; + + if (icon->display == -1) return; + + LIST_FOR_EACH_ENTRY( ptr, &icon_list, struct tray_icon, entry ) + { + if (ptr == icon) continue; + if (ptr->display < icon->display) continue; + ptr->display--; + pos = get_icon_pos( ptr ); + SetWindowPos( ptr->window, 0, pos.x, pos.y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER ); + } + icon->display = -1; + if (!--nb_displayed) ShowWindow( standalone_tray, SW_HIDE ); + TRACE( "removed %u now %d icons\n", icon->id, nb_displayed ); +} + +/* window procedure for the individual tray icon window */ +static LRESULT WINAPI tray_icon_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { struct tray_icon *icon = NULL; BOOL ret; @@ -156,6 +265,10 @@ static LRESULT WINAPI tray_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp switch (msg) { + case WM_CREATE: + SetTimer( hwnd, 1, 1000, NULL ); + break; + case WM_PAINT: { PAINTSTRUCT ps; @@ -170,7 +283,7 @@ static LRESULT WINAPI tray_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp DrawIconEx( hdc, (rc.left + rc.right - cx) / 2, (rc.top + rc.bottom - cy) / 2, icon->image, cx, cy, 0, 0, DI_DEFAULTSIZE|DI_NORMAL ); EndPaint(hwnd, &ps); - break; + return 0; } case WM_MOUSEMOVE: @@ -191,57 +304,100 @@ static LRESULT WINAPI tray_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp WARN( "application window was destroyed, removing icon %u\n", icon->id ); delete_icon( icon ); } - break; + return 0; case WM_TIMER: if (!IsWindow( icon->owner )) delete_icon( icon ); return 0; - - default: - return DefWindowProcW(hwnd, msg, wparam, lparam); } - return 0; + return DefWindowProcW( hwnd, msg, wparam, lparam ); } /* find the X11 window owner the system tray selection */ static Window get_systray_selection_owner( Display *display ) { - static Atom systray_atom; Window ret; - if (root_window != DefaultRootWindow( display )) return 0; - wine_tsx11_lock(); - if (!systray_atom) - { - if (DefaultScreen( display ) == 0) - systray_atom = x11drv_atom(_NET_SYSTEM_TRAY_S0); - else - { - char systray_buffer[29]; /* strlen(_NET_SYSTEM_TRAY_S4294967295)+1 */ - sprintf( systray_buffer, "_NET_SYSTEM_TRAY_S%u", DefaultScreen( display ) ); - systray_atom = XInternAtom( display, systray_buffer, False ); - } - } ret = XGetSelectionOwner( display, systray_atom ); wine_tsx11_unlock(); return ret; } +static BOOL init_systray(void) +{ + static BOOL init_done; + WNDCLASSEXW class; + Display *display; + + if (root_window != DefaultRootWindow( gdi_display )) return FALSE; + if (init_done) return TRUE; + + icon_cx = GetSystemMetrics( SM_CXSMICON ) + 2 * ICON_BORDER; + icon_cy = GetSystemMetrics( SM_CYSMICON ) + 2 * ICON_BORDER; -/* dock the given X window with the NETWM system tray */ -static void dock_systray_window( Display *display, HWND hwnd, Window systray_window ) + memset( &class, 0, sizeof(class) ); + class.cbSize = sizeof(class); + class.lpfnWndProc = tray_icon_wndproc; + class.hIcon = LoadIconW(0, (LPCWSTR)IDI_WINLOGO); + class.hCursor = LoadCursorW( 0, (LPCWSTR)IDC_ARROW ); + class.lpszClassName = icon_classname; + class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + + if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + { + ERR( "Could not register icon tray window class\n" ); + return FALSE; + } + + class.lpfnWndProc = standalone_tray_wndproc; + class.hbrBackground = (HBRUSH)COLOR_WINDOW; + class.lpszClassName = tray_classname; + class.style = CS_DBLCLKS; + + if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + { + ERR( "Could not register standalone tray window class\n" ); + return FALSE; + } + + display = thread_init_display(); + wine_tsx11_lock(); + if (DefaultScreen( display ) == 0) + systray_atom = x11drv_atom(_NET_SYSTEM_TRAY_S0); + else + { + char systray_buffer[29]; /* strlen(_NET_SYSTEM_TRAY_S4294967295)+1 */ + sprintf( systray_buffer, "_NET_SYSTEM_TRAY_S%u", DefaultScreen( display ) ); + systray_atom = XInternAtom( display, systray_buffer, False ); + } + XSelectInput( display, root_window, StructureNotifyMask ); + wine_tsx11_unlock(); + + init_done = TRUE; + return TRUE; +} + +/* dock the given icon with the NETWM system tray */ +static void dock_systray_icon( Display *display, struct tray_icon *icon, Window systray_window ) { struct x11drv_win_data *data; XEvent ev; XSetWindowAttributes attr; - if (!(data = X11DRV_get_win_data( hwnd )) && - !(data = X11DRV_create_win_data( hwnd ))) return; + icon->window = CreateWindowW( icon_classname, NULL, WS_CLIPSIBLINGS | WS_POPUP, + CW_USEDEFAULT, CW_USEDEFAULT, icon_cx, icon_cy, + NULL, NULL, NULL, icon ); + if (!icon->window) return; + + if (!(data = X11DRV_get_win_data( icon->window )) && + !(data = X11DRV_create_win_data( icon->window ))) return; TRACE( "icon window %p/%lx managed %u\n", data->hwnd, data->whole_window, data->managed ); make_window_embedded( display, data ); + create_tooltip( icon ); + ShowWindow( icon->window, SW_SHOWNA ); /* send the docking request message */ ev.xclient.type = ClientMessage; @@ -274,55 +430,25 @@ static BOOL hide_icon( struct tray_icon *icon ) DestroyWindow(icon->tooltip); icon->window = 0; icon->tooltip = 0; + remove_from_standalone_tray( icon ); return TRUE; } /* make the icon visible */ static BOOL show_icon( struct tray_icon *icon ) { - RECT rect; - static BOOL class_registered; Window systray_window; - Display *display = thread_display(); + Display *display = thread_init_display(); TRACE( "id=0x%x, hwnd=%p\n", icon->id, icon->owner ); if (icon->window) return TRUE; /* already shown */ - if (!class_registered) - { - WNDCLASSEXW class; - - ZeroMemory( &class, sizeof(class) ); - class.cbSize = sizeof(class); - class.lpfnWndProc = tray_wndproc; - class.hCursor = LoadCursorW( 0, (LPCWSTR)IDC_ARROW ); - class.lpszClassName = tray_classname; - class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; - - if (!RegisterClassExW(&class) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) - { - WINE_ERR( "Could not register tray window class\n" ); - return FALSE; - } - class_registered = TRUE; - } - - if (!(systray_window = get_systray_selection_owner( display ))) return FALSE; - - rect.left = 0; - rect.top = 0; - rect.right = GetSystemMetrics( SM_CXSMICON ) + 2*ICON_BORDER; - rect.bottom = GetSystemMetrics( SM_CYSMICON ) + 2*ICON_BORDER; + if ((systray_window = get_systray_selection_owner( display ))) + dock_systray_icon( display, icon, systray_window ); + else + add_to_standalone_tray( icon ); - icon->window = CreateWindowExW( WS_EX_APPWINDOW, tray_classname, NULL, WS_CLIPSIBLINGS | WS_POPUP, - CW_USEDEFAULT, CW_USEDEFAULT, - rect.right - rect.left, rect.bottom - rect.top, - NULL, NULL, NULL, icon ); - create_tooltip( icon ); - dock_systray_window( display, icon->window, systray_window ); - SetTimer( icon->window, 1, 1000, NULL ); - ShowWindow( icon->window, SW_SHOWNA ); return TRUE; } @@ -343,8 +469,12 @@ static BOOL modify_icon( struct tray_icon *icon, NOTIFYICONDATAW *nid ) icon->image = CopyIcon(nid->hIcon); if (icon->window) { - struct x11drv_win_data *data = X11DRV_get_win_data( icon->window ); - if (data) XClearArea( thread_display(), data->client_window, 0, 0, 0, 0, True ); + if (icon->display != -1) InvalidateRect( icon->window, NULL, TRUE ); + else + { + struct x11drv_win_data *data = X11DRV_get_win_data( icon->window ); + if (data) XClearArea( gdi_display, data->client_window, 0, 0, 0, 0, True ); + } } } @@ -389,6 +519,7 @@ static BOOL add_icon(NOTIFYICONDATAW *nid) ZeroMemory(icon, sizeof(struct tray_icon)); icon->id = nid->uID; icon->owner = nid->hWnd; + icon->display = -1; list_add_tail(&icon_list, &icon->entry); @@ -423,8 +554,7 @@ int CDECL wine_notify_icon( DWORD msg, NOTIFYICONDATAW *data ) switch (msg) { case NIM_ADD: - if (!get_systray_selection_owner( thread_init_display() )) - return -1; /* fall back to default handling */ + if (!init_systray()) return -1; /* fall back to default handling */ ret = add_icon( data ); break; case NIM_DELETE: diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 536a4a1dfe3..722ca857472 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -597,6 +597,7 @@ extern int use_xkb; extern int use_take_focus; extern int use_primary_selection; extern int use_system_cursors; +extern int show_systray; extern int usexcomposite; extern int managed_mode; extern int decorated_mode; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index cf221b1d04d..84770821e31 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -83,6 +83,7 @@ int use_xkb = 1; int use_take_focus = 1; int use_primary_selection = 0; int use_system_cursors = 1; +int show_systray = 1; int managed_mode = 1; int decorated_mode = 1; int private_color_map = 0; @@ -400,6 +401,9 @@ static void setup_options(void) if (!get_config_key( hkey, appkey, "UseSystemCursors", buffer, sizeof(buffer) )) use_system_cursors = IS_OPTION_TRUE( buffer[0] ); + if (!get_config_key( hkey, appkey, "ShowSystray", buffer, sizeof(buffer) )) + show_systray = IS_OPTION_TRUE( buffer[0] ); + screen_depth = 0; if (!get_config_key( hkey, appkey, "ScreenDepth", buffer, sizeof(buffer) )) screen_depth = atoi(buffer); -- 2.11.4.GIT