/* * 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 #endif #include #include #ifdef __CYGWIN__ #include #include #endif #include "win.h" #include #include #include "winprefs.h" #include "windisplay.h" #include "winmultiwindowclass.h" #include "winmultiwindowicons.h" /* Where will the custom menu commands start counting from? */ #define STARTMENUID WM_USER extern const char *winGetBaseDir(void); /* From winprefslex.l, the real parser */ extern int parse_file(FILE * fp); /* Currently in use command ID, incremented each new menu item created */ static int g_cmdid = STARTMENUID; /* * 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; } /* * 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; 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 { /* Send a message to WM thread telling it re-evaluate the icon for this window */ { winWMMessageRec wmMsg; WindowPtr pWin = GetProp(hwnd, WIN_WINDOW_PROP); if (pWin) { winPrivWinPtr pWinPriv = winGetWindowPriv(pWin); winPrivScreenPtr s_pScreenPriv = pWinPriv->pScreenPriv; wmMsg.msg = WM_WM_ICON_EVENT; wmMsg.hwndWindow = hwnd; wmMsg.iWindow = (Window) (INT_PTR) GetProp(hwnd, WIN_WID_PROP); winSendMessageToWM(s_pScreenPriv->pWMInfo, &wmMsg); } } /* Update the system menu for this window */ SetupSysMenu(hwnd); /* That was easy... */ } return TRUE; } /* * 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(winPrivScreenPtr pScreenPriv) { int i; winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo; /* 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. */ if (pScreenInfo->fMultiWindow) EnumThreadWindows(g_dwCurrentThreadID, ReloadEnumWindowsProc, FALSE); /* 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; if (pScreenInfo->fMultiWindow) { winInitGlobalIcons(); /* Rebuild the icons and menus */ EnumThreadWindows(g_dwCurrentThreadID, ReloadEnumWindowsProc, TRUE); } /* Whew, done */ } /* * Check/uncheck the ALWAYSONTOP items in this menu */ void HandleCustomWM_INITMENU(HWND hwnd, HMENU hmenu) { DWORD dwExStyle; int i, j; 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 processed, FALSE otherwise. */ Bool HandleCustomWM_COMMAND(HWND hwnd, WORD command, winPrivScreenPtr pScreenPriv) { int i, j; MENUPARSED *m; DWORD dwExStyle; 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; int fd; /* Close any open descriptors except for STD* */ getrlimit(RLIMIT_NOFILE, &rl); for (fd = STDERR_FILENO + 1; fd < rl.rlim_cur; fd++) close(fd); /* 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); { winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo; if (pScreenInfo->fMultiWindow) /* Reflect the changed Z order */ winReorderWindowsMultiWindow(); } return TRUE; case CMD_RELOAD: ReloadPrefs(pScreenPriv); return TRUE; default: return FALSE; } } /* match */ } /* for j */ } /* for i */ return FALSE; } /* * Add the default or a custom menu depending on the class match */ void SetupSysMenu(HWND hwnd) { HMENU sys; int i; WindowPtr pWin; char *res_name, *res_class; 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); } } /* * Possibly add a menu to the toolbar icon */ void SetupRootMenu(HMENU root) { 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, pref.iconDirectory, 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, pref.iconDirectory, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0); if (hicon == NULL) ErrorF("winTaskbarIcon: LoadImageComma(%s) failed\n", pref.trayIconName); } /* 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; } /* * Handle comma-ified icon names * * 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 */ HICON LoadImageComma(char *fname, char *iconDirectory, int sx, int sy, int flags) { HICON hicon; int i; /* Some input error checking */ if (!fname || !fname[0]) return NULL; i = 0; hicon = NULL; if (fname[0] == ',') { /* It's the XWIN.EXE resource they want */ i = atoi(fname + 1); hicon = LoadImage(g_hInstance, MAKEINTRESOURCE(i), IMAGE_ICON, sx, sy, flags); } else { char *file = malloc(PATH_MAX + NAME_MAX + 2); #ifdef __CYGWIN__ Bool convert = FALSE; #endif if (!file) return NULL; file[0] = 0; /* If fname starts 'X:\', it's an absolute Windows path, do nothing */ if (!(fname[0] && fname[1] == ':' && fname[2] == '\\')) { #ifdef __CYGWIN__ /* If fname starts with '/', it's an absolute cygwin path, we'll need to convert it */ if (fname[0] == '/') { convert = TRUE; } else #endif if (iconDirectory) { /* Otherwise, prepend the default icon directory, which currently must be in absolute Windows path form */ strcpy(file, iconDirectory); if (iconDirectory[0]) if (iconDirectory[strlen(iconDirectory) - 1] != '\\') strcat(file, "\\"); } } strcat(file, fname); /* Trim off any ',index' */ if (strrchr(file, ',')) { *(strrchr(file, ',')) = 0; /* End string at comma */ i = atoi(strrchr(fname, ',') + 1); } else { i = -1; } #ifdef __CYGWIN__ /* Convert from Cygwin path to Windows path */ if (convert) { char *converted_file = cygwin_create_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, file); if (converted_file) { free(file); file = converted_file; } } #endif if (i >= 0) { /* Specified as , */ hicon = ExtractIcon(g_hInstance, file, i); } else { /* Specified as just an .ico file */ hicon = (HICON) LoadImage(NULL, file, IMAGE_ICON, sx, sy, LR_LOADFROMFILE | flags); } free(file); } 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(char *res_name, char *res_class, char *wmName) { int i; HICON hicon; for (i = 0; i < pref.iconItems; i++) { if ((res_name && !strcmp(pref.icon[i].match, res_name)) || (res_class && !strcmp(pref.icon[i].match, res_class)) || (wmName && strstr(wmName, pref.icon[i].match))) { if (pref.icon[i].hicon) return pref.icon[i].hicon; hicon = LoadImageComma(pref.icon[i].iconFile, pref.iconDirectory, 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 */ return 0; } /* * Should we free this icon or leave it in memory (is it part of our * ICONS{} overrides)? */ int winIconIsOverride(HICON hicon) { int i; if (!hicon) return 0; for (i = 0; i < pref.iconItems; i++) if ((HICON) pref.icon[i].hicon == hicon) return 1; return 0; } /* * Open and parse the XWinrc config file @path. * If @path is NULL, use the built-in default. */ static int winPrefsLoadPreferences(const char *path) { FILE *prefFile = NULL; if (path) prefFile = fopen(path, "r"); #ifdef __CYGWIN__ else { char defaultPrefs[] = "MENU rmenu {\n" " \"How to customize this menu\" EXEC \"xterm +tb -e man XWinrc\"\n" " \"Launch xterm\" EXEC xterm\n" " \"Load .XWinrc\" RELOAD\n" " SEPARATOR\n" "}\n" "\n" "ROOTMENU rmenu\n"; path = "built-in default"; prefFile = fmemopen(defaultPrefs, strlen(defaultPrefs), "r"); } #endif if (!prefFile) { ErrorF("LoadPreferences: %s not found\n", path); return FALSE; } ErrorF("LoadPreferences: Loading %s\n", path); if ((parse_file(prefFile)) != 0) { ErrorF("LoadPreferences: %s is badly formed!\n", path); fclose(prefFile); return FALSE; } fclose(prefFile); return TRUE; } /* * 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]; char szDisplay[512]; char *szEnvDisplay; int i, j; char param[PARAM_MAX + 1]; char *srcParam, *dstParam; int parsed = FALSE; /* First, clear all preference settings */ memset(&pref, 0, sizeof(pref)); /* 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"); parsed = winPrefsLoadPreferences(fname); } /* No home file found, check system default */ if (!parsed) { 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; parsed = winPrefsLoadPreferences(buffer); } /* Neither user nor system configuration found, or were badly formed */ if (!parsed) { ErrorF ("LoadPreferences: See \"man XWinrc\" to customize the XWin menu.\n"); parsed = winPrefsLoadPreferences(NULL); } /* Setup a DISPLAY environment variable, need to allocate on heap */ /* because putenv doesn't copy the argument... */ winGetDisplayName(szDisplay, 0); szEnvDisplay = (char *) (malloc(strlen(szDisplay) + strlen("DISPLAY=") + 1)); if (szEnvDisplay) { snprintf(szEnvDisplay, 512, "DISPLAY=%s", szDisplay); putenv(szEnvDisplay); } /* Replace any "%display%" in menu commands with display string */ 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(char *res_name, char *res_class, char *wmName) { int i; for (i = 0; i < pref.styleItems; i++) { if ((res_name && !strcmp(pref.style[i].match, res_name)) || (res_class && !strcmp(pref.style[i].match, res_class)) || (wmName && strstr(wmName, pref.style[i].match))) { if (pref.style[i].type) return pref.style[i].type; } } /* Didn't find the style, fail gracefully */ return STYLE_NONE; }