/* * Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved. * Copyright (C) Colin Harrison 2005-2008 * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of the XFree86 Project * shall not be used in advertising or otherwise to promote the sale, use * or other dealings in this Software without prior written authorization * from the XFree86 Project. * * Authors: Earle F. Philhower, III * Colin Harrison */ #ifdef HAVE_XWIN_CONFIG_H #include <xwin-config.h> #endif #include <stdio.h> #include <stdlib.h> #ifdef __CYGWIN__ #include <sys/resource.h> #endif #include "win.h" #include <X11/Xwindows.h> #include <shellapi.h> #include "winprefs.h" #include "winmultiwindowclass.h" /* Where will the custom menu commands start counting from? */ #define STARTMENUID WM_USER /* External global variables */ #ifdef XWIN_MULTIWINDOW extern DWORD g_dwCurrentThreadID; #endif extern const char *winGetBaseDir(void); /* From winmultiwindowflex.l, the real parser */ extern void parse_file (FILE *fp); /* From winprefyacc.y, the pref structure loaded by the parser */ extern WINPREFS pref; /* The global X default icon */ extern HICON g_hIconX; extern HICON g_hSmallIconX; /* Currently in use command ID, incremented each new menu item created */ static int g_cmdid = STARTMENUID; /* Defined in DIX */ extern char *display; /* Local function to handle comma-ified icon names */ static HICON LoadImageComma (char *fname, int sx, int sy, int flags); /* * Creates or appends a menu from a MENUPARSED structure */ static HMENU MakeMenu (char *name, HMENU editMenu, int editItem) { int i; int item; MENUPARSED *m; HMENU hmenu, hsub; for (i=0; i<pref.menuItems; i++) { if (!strcmp(name, pref.menu[i].menuName)) break; } /* Didn't find a match, bummer */ if (i==pref.menuItems) { ErrorF("MakeMenu: Can't find menu %s\n", name); return NULL; } m = &(pref.menu[i]); if (editMenu) { hmenu = editMenu; item = editItem; } else { hmenu = CreatePopupMenu(); if (!hmenu) { ErrorF("MakeMenu: Unable to CreatePopupMenu() %s\n", name); return NULL; } item = 0; } /* Add the menu items */ for (i=0; i<m->menuItems; i++) { /* Only assign IDs one time... */ if ( m->menuItem[i].commandID == 0 ) m->menuItem[i].commandID = g_cmdid++; switch (m->menuItem[i].cmd) { case CMD_EXEC: case CMD_ALWAYSONTOP: case CMD_RELOAD: InsertMenu (hmenu, item, MF_BYPOSITION|MF_ENABLED|MF_STRING, m->menuItem[i].commandID, m->menuItem[i].text); break; case CMD_SEPARATOR: InsertMenu (hmenu, item, MF_BYPOSITION|MF_SEPARATOR, 0, NULL); break; case CMD_MENU: /* Recursive! */ hsub = MakeMenu (m->menuItem[i].param, 0, 0); if (hsub) InsertMenu (hmenu, item, MF_BYPOSITION|MF_POPUP|MF_ENABLED|MF_STRING, (UINT_PTR)hsub, m->menuItem[i].text); break; } /* If item==-1 (means to add at end of menu) don't increment) */ if (item>=0) item++; } return hmenu; } #ifdef XWIN_MULTIWINDOW /* * Callback routine that is executed once per window class. * Removes or creates custom window settings depending on LPARAM */ static wBOOL CALLBACK ReloadEnumWindowsProc (HWND hwnd, LPARAM lParam) { HICON hicon; Window wid; if (!hwnd) { ErrorF("ReloadEnumWindowsProc: hwnd==NULL!\n"); return FALSE; } /* It's our baby, either clean or dirty it */ if (lParam==FALSE) { /* Reset the window's icon to undefined. */ hicon = (HICON)SendMessage(hwnd, WM_SETICON, ICON_BIG, 0); /* If the old icon is generated on-the-fly, get rid of it, will regen */ winDestroyIcon (hicon); /* Same for the small icon */ hicon = (HICON)SendMessage(hwnd, WM_SETICON, ICON_SMALL, 0); winDestroyIcon (hicon); /* Remove any menu additions; bRevert=TRUE destroys any modified menus */ GetSystemMenu (hwnd, TRUE); /* This window is now clean of our taint (but with undefined icons) */ } else { /* winUpdateIcon() will set the icon default, dynamic, or from xwinrc */ wid = (Window)GetProp (hwnd, WIN_WID_PROP); if (wid) winUpdateIcon (wid); /* Update the system menu for this window */ SetupSysMenu ((unsigned long)hwnd); /* That was easy... */ } return TRUE; } #endif /* * Removes any custom icons in classes, custom menus, etc. * Frees all members in pref structure. * Reloads the preferences file. * Set custom icons and menus again. */ static void ReloadPrefs (void) { int i; #ifdef XWIN_MULTIWINDOW /* First, iterate over all windows, deleting their icons and custom menus. * This is really only needed because winDestroyIcon() will try to * destroy the old global icons, which will have changed. * It is probably better to set a windows USER_DATA to flag locally defined * icons, and use that to accurately know when to destroy old icons. */ EnumThreadWindows (g_dwCurrentThreadID, ReloadEnumWindowsProc, FALSE); #endif /* Now, free/clear all info from our prefs structure */ for (i=0; i<pref.menuItems; i++) free (pref.menu[i].menuItem); free (pref.menu); pref.menu = NULL; pref.menuItems = 0; pref.rootMenuName[0] = 0; free (pref.sysMenu); pref.sysMenuItems = 0; pref.defaultSysMenuName[0] = 0; pref.defaultSysMenuPos = 0; pref.iconDirectory[0] = 0; pref.defaultIconName[0] = 0; pref.trayIconName[0] = 0; for (i=0; i<pref.iconItems; i++) if (pref.icon[i].hicon) DestroyIcon ((HICON)pref.icon[i].hicon); free (pref.icon); pref.icon = NULL; pref.iconItems = 0; /* Free global default X icon */ if (g_hIconX) DestroyIcon (g_hIconX); if (g_hSmallIconX) DestroyIcon (g_hSmallIconX); /* Reset the custom command IDs */ g_cmdid = STARTMENUID; /* Load the updated resource file */ LoadPreferences(); g_hIconX = NULL; g_hSmallIconX = NULL; #ifdef XWIN_MULTIWINDOW winInitGlobalIcons(); #endif #ifdef XWIN_MULTIWINDOW /* Rebuild the icons and menus */ EnumThreadWindows (g_dwCurrentThreadID, ReloadEnumWindowsProc, TRUE); #endif /* Whew, done */ } /* * Check/uncheck the ALWAYSONTOP items in this menu */ void HandleCustomWM_INITMENU(unsigned long hwndIn, unsigned long hmenuIn) { HWND hwnd; HMENU hmenu; DWORD dwExStyle; int i, j; hwnd = (HWND)hwndIn; hmenu = (HMENU)hmenuIn; if (!hwnd || !hmenu) return; if (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) dwExStyle = MF_BYCOMMAND | MF_CHECKED; else dwExStyle = MF_BYCOMMAND | MF_UNCHECKED; for (i=0; i<pref.menuItems; i++) for (j=0; j<pref.menu[i].menuItems; j++) if (pref.menu[i].menuItem[j].cmd==CMD_ALWAYSONTOP) CheckMenuItem (hmenu, pref.menu[i].menuItem[j].commandID, dwExStyle ); } /* * Searches for the custom WM_COMMAND command ID and performs action. * Return TRUE if command is proccessed, FALSE otherwise. */ Bool HandleCustomWM_COMMAND (unsigned long hwndIn, int command) { HWND hwnd; int i, j; MENUPARSED *m; DWORD dwExStyle; hwnd = (HWND)hwndIn; if (!command) return FALSE; for (i=0; i<pref.menuItems; i++) { m = &(pref.menu[i]); for (j=0; j<m->menuItems; j++) { if (command==m->menuItem[j].commandID) { /* Match! */ switch(m->menuItem[j].cmd) { #ifdef __CYGWIN__ case CMD_EXEC: if (fork()==0) { struct rlimit rl; unsigned long i; /* Close any open descriptors except for STD* */ getrlimit (RLIMIT_NOFILE, &rl); for (i = STDERR_FILENO+1; i < rl.rlim_cur; i++) close(i); /* Disassociate any TTYs */ setsid(); execl ("/bin/sh", "/bin/sh", "-c", m->menuItem[j].param, NULL); exit (0); } else return TRUE; break; #else case CMD_EXEC: { /* Start process without console window */ STARTUPINFO start; PROCESS_INFORMATION child; memset (&start, 0, sizeof (start)); start.cb = sizeof (start); start.dwFlags = STARTF_USESHOWWINDOW; start.wShowWindow = SW_HIDE; memset (&child, 0, sizeof (child)); if (CreateProcess (NULL, m->menuItem[j].param, NULL, NULL, FALSE, 0, NULL, NULL, &start, &child)) { CloseHandle (child.hThread); CloseHandle (child.hProcess); } else MessageBox(NULL, m->menuItem[j].param, "Mingrc Exec Command Error!", MB_OK | MB_ICONEXCLAMATION); } return TRUE; #endif case CMD_ALWAYSONTOP: if (!hwnd) return FALSE; /* Get extended window style */ dwExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); /* Handle topmost windows */ if (dwExStyle & WS_EX_TOPMOST) SetWindowPos (hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); else SetWindowPos (hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); #if XWIN_MULTIWINDOW /* Reflect the changed Z order */ winReorderWindowsMultiWindow (); #endif return TRUE; case CMD_RELOAD: ReloadPrefs(); return TRUE; default: return FALSE; } } /* match */ } /* for j */ } /* for i */ return FALSE; } #ifdef XWIN_MULTIWINDOW /* * Add the default or a custom menu depending on the class match */ void SetupSysMenu (unsigned long hwndIn) { HWND hwnd; HMENU sys; int i; WindowPtr pWin; char *res_name, *res_class; hwnd = (HWND)hwndIn; if (!hwnd) return; pWin = GetProp (hwnd, WIN_WINDOW_PROP); sys = GetSystemMenu (hwnd, FALSE); if (!sys) return; if (pWin) { /* First see if there's a class match... */ if (winMultiWindowGetClassHint (pWin, &res_name, &res_class)) { for (i=0; i<pref.sysMenuItems; i++) { if (!strcmp(pref.sysMenu[i].match, res_name) || !strcmp(pref.sysMenu[i].match, res_class) ) { free(res_name); free(res_class); MakeMenu (pref.sysMenu[i].menuName, sys, pref.sysMenu[i].menuPos==AT_START?0:-1); return; } } /* No match, just free alloc'd strings */ free(res_name); free(res_class); } /* Found wm_class */ } /* if pwin */ /* Fallback to system default */ if (pref.defaultSysMenuName[0]) { if (pref.defaultSysMenuPos==AT_START) MakeMenu (pref.defaultSysMenuName, sys, 0); else MakeMenu (pref.defaultSysMenuName, sys, -1); } } #endif /* * Possibly add a menu to the toolbar icon */ void SetupRootMenu (unsigned long hmenuRoot) { HMENU root; root = (HMENU)hmenuRoot; if (!root) return; if (pref.rootMenuName[0]) { MakeMenu(pref.rootMenuName, root, 0); } } /* * Check for and return an overridden default ICON specified in the prefs */ HICON winOverrideDefaultIcon(int size) { HICON hicon; if (pref.defaultIconName[0]) { hicon = LoadImageComma (pref.defaultIconName, size, size, 0); if (hicon==NULL) ErrorF ("winOverrideDefaultIcon: LoadImageComma(%s) failed\n", pref.defaultIconName); return hicon; } return 0; } /* * Return the HICON to use in the taskbar notification area */ HICON winTaskbarIcon(void) { HICON hicon; hicon = 0; /* First try and load an overridden, if success then return it */ if (pref.trayIconName[0]) { hicon = LoadImageComma (pref.trayIconName, GetSystemMetrics (SM_CXSMICON), GetSystemMetrics (SM_CYSMICON), 0 ); } /* Otherwise return the default */ if (!hicon) hicon = (HICON) LoadImage (g_hInstance, MAKEINTRESOURCE(IDI_XWIN), IMAGE_ICON, GetSystemMetrics (SM_CXSMICON), GetSystemMetrics (SM_CYSMICON), 0); return hicon; } /* * Parse a filename to extract an icon: * If fname is exactly ",nnn" then extract icon from our resource * else if it is "file,nnn" then extract icon nnn from that file * else try to load it as an .ico file and if that fails return NULL */ static HICON LoadImageComma (char *fname, int sx, int sy, int flags) { HICON hicon; int index; char file[PATH_MAX+NAME_MAX+2]; /* Some input error checking */ if (!fname || !fname[0]) return NULL; index = 0; hicon = NULL; if (fname[0]==',') { /* It's the XWIN.EXE resource they want */ index = atoi (fname+1); hicon = LoadImage (g_hInstance, MAKEINTRESOURCE(index), IMAGE_ICON, sx, sy, flags); } else { file[0] = 0; /* Prepend path if not given a "X:\" filename */ if ( !(fname[0] && fname[1]==':' && fname[2]=='\\') ) { strcpy (file, pref.iconDirectory); if (pref.iconDirectory[0]) if (fname[strlen(fname)-1]!='\\') strcat (file, "\\"); } strcat (file, fname); if (strrchr (file, ',')) { /* Specified as <fname>,<index> */ *(strrchr (file, ',')) = 0; /* End string at comma */ index = atoi (strrchr (fname, ',') + 1); hicon = ExtractIcon (g_hInstance, file, index); } else { /* Just an .ico file... */ hicon = (HICON)LoadImage (NULL, file, IMAGE_ICON, sx, sy, LR_LOADFROMFILE|flags); } } return hicon; } /* * Check for a match of the window class to one specified in the * ICONS{} section in the prefs file, and load the icon from a file */ HICON winOverrideIcon (unsigned long longWin) { WindowPtr pWin = (WindowPtr) longWin; char *res_name, *res_class; int i; HICON hicon; char *wmName; if (pWin==NULL) return 0; /* If we can't find the class, we can't override from default! */ if (!winMultiWindowGetClassHint (pWin, &res_name, &res_class)) return 0; winMultiWindowGetWMName (pWin, &wmName); for (i=0; i<pref.iconItems; i++) { if (!strcmp(pref.icon[i].match, res_name) || !strcmp(pref.icon[i].match, res_class) || (wmName && strstr(wmName, pref.icon[i].match))) { free (res_name); free (res_class); if (wmName) free (wmName); if (pref.icon[i].hicon) return pref.icon[i].hicon; hicon = LoadImageComma (pref.icon[i].iconFile, 0, 0, LR_DEFAULTSIZE); if (hicon==NULL) ErrorF ("winOverrideIcon: LoadImageComma(%s) failed\n", pref.icon[i].iconFile); pref.icon[i].hicon = hicon; return hicon; } } /* Didn't find the icon, fail gracefully */ free (res_name); free (res_class); if (wmName) free (wmName); return 0; } /* * Should we free this icon or leave it in memory (is it part of our * ICONS{} overrides)? */ int winIconIsOverride(unsigned hiconIn) { HICON hicon; int i; hicon = (HICON)hiconIn; if (!hicon) return 0; for (i=0; i<pref.iconItems; i++) if ((HICON)pref.icon[i].hicon == hicon) return 1; return 0; } /* * Try and open ~/.XWinrc and system.XWinrc * Load it into prefs structure for use by other functions */ void LoadPreferences (void) { char *home; char fname[PATH_MAX+NAME_MAX+2]; FILE *prefFile; char szDisplay[512]; char *szEnvDisplay; int i, j; char param[PARAM_MAX+1]; char *srcParam, *dstParam; /* First, clear all preference settings */ memset (&pref, 0, sizeof(pref)); prefFile = NULL; /* Now try and find a ~/.xwinrc file */ home = getenv ("HOME"); if (home) { strcpy (fname, home); if (fname[strlen(fname)-1]!='/') strcat (fname, "/"); strcat (fname, ".XWinrc"); prefFile = fopen (fname, "r"); if (prefFile) ErrorF ("winPrefsLoadPreferences: %s\n", fname); } /* No home file found, check system default */ if (!prefFile) { char buffer[MAX_PATH]; #ifdef RELOCATE_PROJECTROOT snprintf(buffer, sizeof(buffer), "%s\\system.XWinrc", winGetBaseDir()); #else strncpy(buffer, SYSCONFDIR"/X11/system.XWinrc", sizeof(buffer)); #endif buffer[sizeof(buffer)-1] = 0; prefFile = fopen (buffer, "r"); if (prefFile) ErrorF ("winPrefsLoadPreferences: %s\n", buffer); } /* If we could open it, then read the settings and close it */ if (prefFile) { parse_file (prefFile); fclose (prefFile); } /* Setup a DISPLAY environment variable, need to allocate on heap */ /* because putenv doesn't copy the argument... */ snprintf (szDisplay, 512, "DISPLAY=127.0.0.1:%s.0", display); szEnvDisplay = (char *)(malloc (strlen(szDisplay)+1)); if (szEnvDisplay) { strcpy (szEnvDisplay, szDisplay); putenv (szEnvDisplay); } /* Replace any "%display%" in menu commands with display string */ snprintf (szDisplay, 512, "127.0.0.1:%s.0", display); for (i=0; i<pref.menuItems; i++) { for (j=0; j<pref.menu[i].menuItems; j++) { if (pref.menu[i].menuItem[j].cmd==CMD_EXEC) { srcParam = pref.menu[i].menuItem[j].param; dstParam = param; while (*srcParam) { if (!strncmp(srcParam, "%display%", 9)) { memcpy (dstParam, szDisplay, strlen(szDisplay)); dstParam += strlen(szDisplay); srcParam += 9; } else { *dstParam = *srcParam; dstParam++; srcParam++; } } *dstParam = 0; strcpy (pref.menu[i].menuItem[j].param, param); } /* cmd==cmd_exec */ } /* for all menuitems */ } /* for all menus */ } /* * Check for a match of the window class to one specified in the * STYLES{} section in the prefs file, and return the style type */ unsigned long winOverrideStyle (unsigned long longpWin) { WindowPtr pWin = (WindowPtr) longpWin; char *res_name, *res_class; int i; char *wmName; if (pWin==NULL) return STYLE_NONE; /* If we can't find the class, we can't override from default! */ if (!winMultiWindowGetClassHint (pWin, &res_name, &res_class)) return STYLE_NONE; winMultiWindowGetWMName (pWin, &wmName); for (i=0; i<pref.styleItems; i++) { if (!strcmp(pref.style[i].match, res_name) || !strcmp(pref.style[i].match, res_class) || (wmName && strstr(wmName, pref.style[i].match))) { free (res_name); free (res_class); if (wmName) free (wmName); if (pref.style[i].type) return pref.style[i].type; } } /* Didn't find the style, fail gracefully */ free (res_name); free (res_class); if (wmName) free (wmName); return STYLE_NONE; }