Mercurial > pidgin.yaz
diff pidgin/win32/gtkdocklet-win32.c @ 20882:38c4fe48ebb5
Add new API to the docklet for retrieving the geometry (x,y,w,h) and the
GdkScreen (if available). The win32 implementation of this is really ugly
and could use some testing.
Refs #521 and #1632.
author | Casey Harkins <charkins@pidgin.im> |
---|---|
date | Fri, 12 Oct 2007 02:47:48 +0000 |
parents | 426df717a831 |
children | d3e0eb05ac94 |
line wrap: on
line diff
--- a/pidgin/win32/gtkdocklet-win32.c Fri Oct 12 02:33:31 2007 +0000 +++ b/pidgin/win32/gtkdocklet-win32.c Fri Oct 12 02:47:48 2007 +0000 @@ -570,6 +570,199 @@ RestoreWndFromTray(GDK_WINDOW_HWND(gtkblist->window->window)); } +/* Checks to see if a window matches a specified name. If it matches, + * the matched_hwnd pointer is set to the checked window. + * + * hwnd is the window to check + * matched_hwnd points to hwnd on a match + * name is the expected class name + * + * returns TRUE if there was a match, otherwise FALSE + */ +static BOOL +check_hwnd_class_name(HWND hwnd, HWND *matched_hwnd, char *name) +{ + TCHAR class_name[256]; + + /* get class name of window */ + GetClassName(hwnd, class_name, 255); + + /* compare class name with specified name */ + if(strncmp(class_name, name, 255)!=0) return FALSE; + + /* set matched_hwnd to hwnd */ + *matched_hwnd = hwnd; + return TRUE; +} + +/* callback for EnumChildWindows looking for TrayNotifyWnd */ +static BOOL CALLBACK +find_tray_notify_hwnd_cb(HWND hwnd, LPARAM lparam) +{ + return !check_hwnd_class_name(hwnd, (HWND*)lparam, "TrayNotifyWnd"); +} + +/* callback for EnumChildWindows looking for ToolbarWindow32 */ +static BOOL CALLBACK +find_tray_toolbar_hwnd_cb(HWND hwnd, LPARAM lparam) +{ + return !check_hwnd_class_name(hwnd, (HWND*)lparam, "ToolbarWindow32"); +} + +static HWND +get_tray_toolbar_hwnd() +{ + HWND shell_tray_hwnd = NULL; + HWND tray_notify_hwnd = NULL; + HWND tray_toolbar_hwnd = NULL; + + /* find the top-level window of the system tray area */ + shell_tray_hwnd = FindWindow("Shell_TrayWnd", NULL); + if(!shell_tray_hwnd) return NULL; + + /* enumerate over the shell_tray_hwnd children windows looking for the tray_notify_hwnd */ + EnumChildWindows(shell_tray_hwnd, find_tray_notify_hwnd_cb, (LPARAM)&tray_notify_hwnd); + if(!tray_notify_hwnd || !IsWindow(tray_notify_hwnd)) return NULL; + + /* enumerate over the tray_notify_hwnd children windows looking for tray_toolbar_hwnd */ + EnumChildWindows(tray_notify_hwnd, find_tray_toolbar_hwnd_cb, (LPARAM)&tray_toolbar_hwnd); + if(!tray_toolbar_hwnd || !IsWindow(tray_toolbar_hwnd)) return NULL; + + return tray_toolbar_hwnd; +} + + +/* Get the geometry of the tray icon. This might break if the user is running a + * non-standard shell, in which case this function will return FALSE. If the + * tray icon is hidden (possible >= winxp), then the geometry of the tray itself + * is returned. If FALSE is returned, x, y, w and h are left unchanged. + * Any of the parameters (x, y, w, h) may be NULL if that value is not + * desired. + * + * This code is based on the method and code described here by Irek Zielinski: + * http://www.codeproject.com/shell/ctrayiconposition.asp?msg=999295 + */ +static gboolean +winpidgin_tray_get_geometry(gint *x, gint *y, gint *w, gint *h) +{ + /* systray_hwnd is the parent window of our systray icon */ + HWND tray_toolbar_hwnd = NULL; + DWORD tray_toolbar_pid = -1; + HANDLE tray_toolbar_proc = NULL; + int tray_toolbar_bcount = 0; + LPVOID tray_toolbar_mem = NULL; + + TBBUTTON button; + DWORD nbytes = -1; + DWORD hwnd_id_pair[2] = { -1, -1}; + RECT rect; + POINT top_left; + POINT bot_right; + gboolean found_docklet = FALSE; + int i; + + /* get the tray_toolbar_hwnd */ + tray_toolbar_hwnd = get_tray_toolbar_hwnd(); + if(!tray_toolbar_hwnd) { + return FALSE; + } + + /* count buttons in the tray_toolbar_hwnd */ + tray_toolbar_bcount = SendMessage(tray_toolbar_hwnd, TB_BUTTONCOUNT, 0, 0); + if(tray_toolbar_bcount < 1) { + return FALSE; + } + + /* get pid of the tray_toolbar_hwnd parent process */ + GetWindowThreadProcessId(tray_toolbar_hwnd, &tray_toolbar_pid); + if(tray_toolbar_pid <= 0) { + return FALSE; + } + + /* open the tray_toolbar_hwnd parent process */ + tray_toolbar_proc = OpenProcess(PROCESS_ALL_ACCESS, 0, tray_toolbar_pid); + if(!tray_toolbar_proc) { + return FALSE; + } + + /* allocate some memory in the tray_toolbar_hwnd process space */ + tray_toolbar_mem = VirtualAllocEx(tray_toolbar_proc, NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE); + if(!tray_toolbar_mem) { + CloseHandle(tray_toolbar_proc); + return FALSE; + } + + /* loop through buttons, looking for the docklet */ + for(i=0; i<tray_toolbar_bcount; i++) { + + /* get the button */ + SendMessage(tray_toolbar_hwnd, TB_GETBUTTON, i, (LPARAM)tray_toolbar_mem); + ReadProcessMemory(tray_toolbar_proc, tray_toolbar_mem, &button, sizeof(TBBUTTON), &nbytes); + if(nbytes < sizeof(TBBUTTON)) { + continue; + } + + /* get the dwData from the button */ + ReadProcessMemory(tray_toolbar_proc, (LPVOID)button.dwData, &hwnd_id_pair, sizeof(hwnd_id_pair), &nbytes); + if(nbytes < sizeof(hwnd_id_pair)) { + continue; + } + + /* compare hwnd of button against systray_hwnd */ + if((HWND)hwnd_id_pair[0] != systray_hwnd) { + continue; + } + + /* check if button is hidden */ + if(button.fsState & TBSTATE_HIDDEN) { + break; + } + + /* get RECT of docklet icon */ + SendMessage(tray_toolbar_hwnd, TB_GETITEMRECT, i, (LPARAM)tray_toolbar_mem); + ReadProcessMemory(tray_toolbar_proc, tray_toolbar_mem, &rect, sizeof(RECT), &nbytes); + if(nbytes < sizeof(RECT)) { + break; + } + + /* translate to screen coordinates */ + top_left.x = rect.left; + top_left.y = rect.top; + bot_right.x = rect.right; + bot_right.y = rect.bottom; + + MapWindowPoints(tray_toolbar_hwnd, NULL, (LPPOINT)&top_left, 1); + MapWindowPoints(tray_toolbar_hwnd, NULL, (LPPOINT)&bot_right, 1); + + found_docklet = TRUE; + break; + } + + if(!found_docklet) { + /* fallback on geometry of tray itself */ + GetWindowRect(tray_toolbar_hwnd, &rect); + if(x!=NULL) *x = rect.left; + if(y!=NULL) *y = rect.top; + if(w!=NULL) *w = rect.right - rect.left; + if(h!=NULL) *h = rect.bottom - rect.top; + } else { + if(x!=NULL) *x = top_left.x; + if(y!=NULL) *y = top_left.y; + if(w!=NULL) *w = bot_right.x - top_left.x; + if(h!=NULL) *h = bot_right.y - top_left.y; + } + + /* clean up */ + VirtualFreeEx(tray_toolbar_proc, tray_toolbar_mem, 0, MEM_RELEASE); + CloseHandle(tray_toolbar_proc); + return TRUE; +} + +static GObject * +winpidgin_tray_get_gdk_screen() +{ + return (GObject *)gdk_screen_get_default(); +} static void winpidgin_tray_create() { OSVERSIONINFO osinfo; @@ -658,7 +851,9 @@ winpidgin_tray_update_icon, winpidgin_tray_blank_icon, winpidgin_tray_set_tooltip, - NULL + NULL, + winpidgin_tray_get_geometry, + winpidgin_tray_get_gdk_screen }; /* Used by docklet's plugin load func */