2 * Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 * Copyright (C) Colin Harrison 2005-2008
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * Except as contained in this notice, the name of the XFree86 Project
25 * shall not be used in advertising or otherwise to promote the sale, use
26 * or other dealings in this Software without prior written authorization
27 * from the XFree86 Project.
29 * Authors: Earle F. Philhower, III
33 #ifdef HAVE_XWIN_CONFIG_H
34 #include <xwin-config.h>
39 #include <sys/resource.h>
43 #include <X11/Xwindows.h>
47 #include "winmultiwindowclass.h"
49 /* Where will the custom menu commands start counting from? */
50 #define STARTMENUID WM_USER
52 extern const char *winGetBaseDir(void);
54 /* From winprefslex.l, the real parser */
55 extern int parse_file(FILE * fp
);
57 /* Currently in use command ID, incremented each new menu item created */
58 static int g_cmdid
= STARTMENUID
;
60 /* Local function to handle comma-ified icon names */
61 static HICON
LoadImageComma(char *fname
, int sx
, int sy
, int flags
);
64 * Creates or appends a menu from a MENUPARSED structure
67 MakeMenu(char *name
, HMENU editMenu
, int editItem
)
74 for (i
= 0; i
< pref
.menuItems
; i
++) {
75 if (!strcmp(name
, pref
.menu
[i
].menuName
))
79 /* Didn't find a match, bummer */
80 if (i
== pref
.menuItems
) {
81 ErrorF("MakeMenu: Can't find menu %s\n", name
);
92 hmenu
= CreatePopupMenu();
94 ErrorF("MakeMenu: Unable to CreatePopupMenu() %s\n", name
);
100 /* Add the menu items */
101 for (i
= 0; i
< m
->menuItems
; i
++) {
102 /* Only assign IDs one time... */
103 if (m
->menuItem
[i
].commandID
== 0)
104 m
->menuItem
[i
].commandID
= g_cmdid
++;
106 switch (m
->menuItem
[i
].cmd
) {
108 case CMD_ALWAYSONTOP
:
112 MF_BYPOSITION
| MF_ENABLED
| MF_STRING
,
113 m
->menuItem
[i
].commandID
, m
->menuItem
[i
].text
);
117 InsertMenu(hmenu
, item
, MF_BYPOSITION
| MF_SEPARATOR
, 0, NULL
);
122 hsub
= MakeMenu(m
->menuItem
[i
].param
, 0, 0);
126 MF_BYPOSITION
| MF_POPUP
| MF_ENABLED
| MF_STRING
,
127 (UINT_PTR
) hsub
, m
->menuItem
[i
].text
);
131 /* If item==-1 (means to add at end of menu) don't increment) */
139 #ifdef XWIN_MULTIWINDOW
141 * Callback routine that is executed once per window class.
142 * Removes or creates custom window settings depending on LPARAM
144 static wBOOL CALLBACK
145 ReloadEnumWindowsProc(HWND hwnd
, LPARAM lParam
)
150 ErrorF("ReloadEnumWindowsProc: hwnd==NULL!\n");
154 /* It's our baby, either clean or dirty it */
155 if (lParam
== FALSE
) {
156 /* Reset the window's icon to undefined. */
157 hicon
= (HICON
) SendMessage(hwnd
, WM_SETICON
, ICON_BIG
, 0);
159 /* If the old icon is generated on-the-fly, get rid of it, will regen */
160 winDestroyIcon(hicon
);
162 /* Same for the small icon */
163 hicon
= (HICON
) SendMessage(hwnd
, WM_SETICON
, ICON_SMALL
, 0);
164 winDestroyIcon(hicon
);
166 /* Remove any menu additions; bRevert=TRUE destroys any modified menus */
167 GetSystemMenu(hwnd
, TRUE
);
169 /* This window is now clean of our taint (but with undefined icons) */
172 /* Send a message to WM thread telling it re-evaluate the icon for this window */
174 winWMMessageRec wmMsg
;
176 WindowPtr pWin
= GetProp(hwnd
, WIN_WINDOW_PROP
);
179 winPrivWinPtr pWinPriv
= winGetWindowPriv(pWin
);
180 winPrivScreenPtr s_pScreenPriv
= pWinPriv
->pScreenPriv
;
182 wmMsg
.msg
= WM_WM_ICON_EVENT
;
183 wmMsg
.hwndWindow
= hwnd
;
184 wmMsg
.iWindow
= (Window
) (INT_PTR
) GetProp(hwnd
, WIN_WID_PROP
);
186 winSendMessageToWM(s_pScreenPriv
->pWMInfo
, &wmMsg
);
190 /* Update the system menu for this window */
193 /* That was easy... */
201 * Removes any custom icons in classes, custom menus, etc.
202 * Frees all members in pref structure.
203 * Reloads the preferences file.
204 * Set custom icons and menus again.
211 #ifdef XWIN_MULTIWINDOW
212 /* First, iterate over all windows, deleting their icons and custom menus.
213 * This is really only needed because winDestroyIcon() will try to
214 * destroy the old global icons, which will have changed.
215 * It is probably better to set a windows USER_DATA to flag locally defined
216 * icons, and use that to accurately know when to destroy old icons.
218 EnumThreadWindows(g_dwCurrentThreadID
, ReloadEnumWindowsProc
, FALSE
);
221 /* Now, free/clear all info from our prefs structure */
222 for (i
= 0; i
< pref
.menuItems
; i
++)
223 free(pref
.menu
[i
].menuItem
);
228 pref
.rootMenuName
[0] = 0;
231 pref
.sysMenuItems
= 0;
233 pref
.defaultSysMenuName
[0] = 0;
234 pref
.defaultSysMenuPos
= 0;
236 pref
.iconDirectory
[0] = 0;
237 pref
.defaultIconName
[0] = 0;
238 pref
.trayIconName
[0] = 0;
240 for (i
= 0; i
< pref
.iconItems
; i
++)
241 if (pref
.icon
[i
].hicon
)
242 DestroyIcon((HICON
) pref
.icon
[i
].hicon
);
247 /* Free global default X icon */
249 DestroyIcon(g_hIconX
);
251 DestroyIcon(g_hSmallIconX
);
253 /* Reset the custom command IDs */
254 g_cmdid
= STARTMENUID
;
256 /* Load the updated resource file */
260 g_hSmallIconX
= NULL
;
262 #ifdef XWIN_MULTIWINDOW
263 winInitGlobalIcons();
266 #ifdef XWIN_MULTIWINDOW
267 /* Rebuild the icons and menus */
268 EnumThreadWindows(g_dwCurrentThreadID
, ReloadEnumWindowsProc
, TRUE
);
275 * Check/uncheck the ALWAYSONTOP items in this menu
278 HandleCustomWM_INITMENU(HWND hwnd
, HMENU hmenu
)
286 if (GetWindowLongPtr(hwnd
, GWL_EXSTYLE
) & WS_EX_TOPMOST
)
287 dwExStyle
= MF_BYCOMMAND
| MF_CHECKED
;
289 dwExStyle
= MF_BYCOMMAND
| MF_UNCHECKED
;
291 for (i
= 0; i
< pref
.menuItems
; i
++)
292 for (j
= 0; j
< pref
.menu
[i
].menuItems
; j
++)
293 if (pref
.menu
[i
].menuItem
[j
].cmd
== CMD_ALWAYSONTOP
)
294 CheckMenuItem(hmenu
, pref
.menu
[i
].menuItem
[j
].commandID
,
300 * Searches for the custom WM_COMMAND command ID and performs action.
301 * Return TRUE if command is proccessed, FALSE otherwise.
304 HandleCustomWM_COMMAND(HWND hwnd
, int command
)
313 for (i
= 0; i
< pref
.menuItems
; i
++) {
315 for (j
= 0; j
< m
->menuItems
; j
++) {
316 if (command
== m
->menuItem
[j
].commandID
) {
318 switch (m
->menuItem
[j
].cmd
) {
325 /* Close any open descriptors except for STD* */
326 getrlimit(RLIMIT_NOFILE
, &rl
);
327 for (fd
= STDERR_FILENO
+ 1; fd
< rl
.rlim_cur
; fd
++)
330 /* Disassociate any TTYs */
334 "/bin/sh", "-c", m
->menuItem
[j
].param
, NULL
);
343 /* Start process without console window */
345 PROCESS_INFORMATION child
;
347 memset(&start
, 0, sizeof(start
));
348 start
.cb
= sizeof(start
);
349 start
.dwFlags
= STARTF_USESHOWWINDOW
;
350 start
.wShowWindow
= SW_HIDE
;
352 memset(&child
, 0, sizeof(child
));
355 (NULL
, m
->menuItem
[j
].param
, NULL
, NULL
, FALSE
, 0, NULL
,
356 NULL
, &start
, &child
)) {
357 CloseHandle(child
.hThread
);
358 CloseHandle(child
.hProcess
);
361 MessageBox(NULL
, m
->menuItem
[j
].param
,
362 "Mingrc Exec Command Error!",
363 MB_OK
| MB_ICONEXCLAMATION
);
367 case CMD_ALWAYSONTOP
:
371 /* Get extended window style */
372 dwExStyle
= GetWindowLongPtr(hwnd
, GWL_EXSTYLE
);
374 /* Handle topmost windows */
375 if (dwExStyle
& WS_EX_TOPMOST
)
378 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
);
382 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
);
384 /* Reflect the changed Z order */
385 winReorderWindowsMultiWindow();
403 #ifdef XWIN_MULTIWINDOW
405 * Add the default or a custom menu depending on the class match
408 SetupSysMenu(HWND hwnd
)
413 char *res_name
, *res_class
;
418 pWin
= GetProp(hwnd
, WIN_WINDOW_PROP
);
420 sys
= GetSystemMenu(hwnd
, FALSE
);
425 /* First see if there's a class match... */
426 if (winMultiWindowGetClassHint(pWin
, &res_name
, &res_class
)) {
427 for (i
= 0; i
< pref
.sysMenuItems
; i
++) {
428 if (!strcmp(pref
.sysMenu
[i
].match
, res_name
) ||
429 !strcmp(pref
.sysMenu
[i
].match
, res_class
)) {
433 MakeMenu(pref
.sysMenu
[i
].menuName
, sys
,
434 pref
.sysMenu
[i
].menuPos
== AT_START
? 0 : -1);
439 /* No match, just free alloc'd strings */
442 } /* Found wm_class */
445 /* Fallback to system default */
446 if (pref
.defaultSysMenuName
[0]) {
447 if (pref
.defaultSysMenuPos
== AT_START
)
448 MakeMenu(pref
.defaultSysMenuName
, sys
, 0);
450 MakeMenu(pref
.defaultSysMenuName
, sys
, -1);
456 * Possibly add a menu to the toolbar icon
459 SetupRootMenu(HMENU root
)
464 if (pref
.rootMenuName
[0]) {
465 MakeMenu(pref
.rootMenuName
, root
, 0);
470 * Check for and return an overridden default ICON specified in the prefs
473 winOverrideDefaultIcon(int size
)
477 if (pref
.defaultIconName
[0]) {
478 hicon
= LoadImageComma(pref
.defaultIconName
, size
, size
, 0);
480 ErrorF("winOverrideDefaultIcon: LoadImageComma(%s) failed\n",
481 pref
.defaultIconName
);
490 * Return the HICON to use in the taskbar notification area
498 /* First try and load an overridden, if success then return it */
499 if (pref
.trayIconName
[0]) {
500 hicon
= LoadImageComma(pref
.trayIconName
,
501 GetSystemMetrics(SM_CXSMICON
),
502 GetSystemMetrics(SM_CYSMICON
), 0);
505 /* Otherwise return the default */
507 hicon
= (HICON
) LoadImage(g_hInstance
,
508 MAKEINTRESOURCE(IDI_XWIN
),
510 GetSystemMetrics(SM_CXSMICON
),
511 GetSystemMetrics(SM_CYSMICON
), 0);
517 * Parse a filename to extract an icon:
518 * If fname is exactly ",nnn" then extract icon from our resource
519 * else if it is "file,nnn" then extract icon nnn from that file
520 * else try to load it as an .ico file and if that fails return NULL
523 LoadImageComma(char *fname
, int sx
, int sy
, int flags
)
527 char file
[PATH_MAX
+ NAME_MAX
+ 2];
529 /* Some input error checking */
530 if (!fname
|| !fname
[0])
536 if (fname
[0] == ',') {
537 /* It's the XWIN.EXE resource they want */
539 hicon
= LoadImage(g_hInstance
,
540 MAKEINTRESOURCE(i
), IMAGE_ICON
, sx
, sy
, flags
);
544 /* Prepend path if not given a "X:\" filename */
545 if (!(fname
[0] && fname
[1] == ':' && fname
[2] == '\\')) {
546 strcpy(file
, pref
.iconDirectory
);
547 if (pref
.iconDirectory
[0])
548 if (fname
[strlen(fname
) - 1] != '\\')
553 if (strrchr(file
, ',')) {
554 /* Specified as <fname>,<index> */
556 *(strrchr(file
, ',')) = 0; /* End string at comma */
557 i
= atoi(strrchr(fname
, ',') + 1);
558 hicon
= ExtractIcon(g_hInstance
, file
, i
);
561 /* Just an .ico file... */
563 hicon
= (HICON
) LoadImage(NULL
,
566 sx
, sy
, LR_LOADFROMFILE
| flags
);
573 * Check for a match of the window class to one specified in the
574 * ICONS{} section in the prefs file, and load the icon from a file
577 winOverrideIcon(char *res_name
, char *res_class
, char *wmName
)
582 for (i
= 0; i
< pref
.iconItems
; i
++) {
583 if ((res_name
&& !strcmp(pref
.icon
[i
].match
, res_name
)) ||
584 (res_class
&& !strcmp(pref
.icon
[i
].match
, res_class
)) ||
585 (wmName
&& strstr(wmName
, pref
.icon
[i
].match
))) {
586 if (pref
.icon
[i
].hicon
)
587 return pref
.icon
[i
].hicon
;
589 hicon
= LoadImageComma(pref
.icon
[i
].iconFile
, 0, 0, LR_DEFAULTSIZE
);
591 ErrorF("winOverrideIcon: LoadImageComma(%s) failed\n",
592 pref
.icon
[i
].iconFile
);
594 pref
.icon
[i
].hicon
= hicon
;
599 /* Didn't find the icon, fail gracefully */
604 * Should we free this icon or leave it in memory (is it part of our
605 * ICONS{} overrides)?
608 winIconIsOverride(HICON hicon
)
615 for (i
= 0; i
< pref
.iconItems
; i
++)
616 if ((HICON
) pref
.icon
[i
].hicon
== hicon
)
623 * Open and parse the XWinrc config file @path.
624 * If @path is NULL, use the built-in default.
627 winPrefsLoadPreferences(char *path
)
629 FILE *prefFile
= NULL
;
632 prefFile
= fopen(path
, "r");
635 char defaultPrefs
[] =
637 " \"How to customize this menu\" EXEC \"xterm +tb -e man XWinrc\"\n"
638 " \"Launch xterm\" EXEC xterm\n"
639 " \"Load .XWinrc\" RELOAD\n"
640 " SEPARATOR\n" "}\n" "\n" "ROOTMENU rmenu\n";
642 path
= "built-in default";
643 prefFile
= fmemopen(defaultPrefs
, strlen(defaultPrefs
), "r");
648 ErrorF("LoadPreferences: %s not found\n", path
);
652 ErrorF("LoadPreferences: Loading %s\n", path
);
654 if ((parse_file(prefFile
)) != 0) {
655 ErrorF("LoadPreferences: %s is badly formed!\n", path
);
665 * Try and open ~/.XWinrc and system.XWinrc
666 * Load it into prefs structure for use by other functions
669 LoadPreferences(void)
672 char fname
[PATH_MAX
+ NAME_MAX
+ 2];
676 char param
[PARAM_MAX
+ 1];
677 char *srcParam
, *dstParam
;
680 /* First, clear all preference settings */
681 memset(&pref
, 0, sizeof(pref
));
683 /* Now try and find a ~/.xwinrc file */
684 home
= getenv("HOME");
687 if (fname
[strlen(fname
) - 1] != '/')
689 strcat(fname
, ".XWinrc");
690 parsed
= winPrefsLoadPreferences(fname
);
693 /* No home file found, check system default */
695 char buffer
[MAX_PATH
];
697 #ifdef RELOCATE_PROJECTROOT
698 snprintf(buffer
, sizeof(buffer
), "%s\\system.XWinrc", winGetBaseDir());
700 strncpy(buffer
, SYSCONFDIR
"/X11/system.XWinrc", sizeof(buffer
));
702 buffer
[sizeof(buffer
) - 1] = 0;
703 parsed
= winPrefsLoadPreferences(buffer
);
706 /* Neither user nor system configuration found, or were badly formed */
709 ("LoadPreferences: See \"man XWinrc\" to customize the XWin menu.\n");
710 parsed
= winPrefsLoadPreferences(NULL
);
713 /* Setup a DISPLAY environment variable, need to allocate on heap */
714 /* because putenv doesn't copy the argument... */
715 snprintf(szDisplay
, 512, "DISPLAY=127.0.0.1:%s.0", display
);
716 szEnvDisplay
= (char *) (malloc(strlen(szDisplay
) + 1));
718 strcpy(szEnvDisplay
, szDisplay
);
719 putenv(szEnvDisplay
);
722 /* Replace any "%display%" in menu commands with display string */
723 snprintf(szDisplay
, 512, "127.0.0.1:%s.0", display
);
724 for (i
= 0; i
< pref
.menuItems
; i
++) {
725 for (j
= 0; j
< pref
.menu
[i
].menuItems
; j
++) {
726 if (pref
.menu
[i
].menuItem
[j
].cmd
== CMD_EXEC
) {
727 srcParam
= pref
.menu
[i
].menuItem
[j
].param
;
730 if (!strncmp(srcParam
, "%display%", 9)) {
731 memcpy(dstParam
, szDisplay
, strlen(szDisplay
));
732 dstParam
+= strlen(szDisplay
);
736 *dstParam
= *srcParam
;
742 strcpy(pref
.menu
[i
].menuItem
[j
].param
, param
);
743 } /* cmd==cmd_exec */
744 } /* for all menuitems */
745 } /* for all menus */
750 * Check for a match of the window class to one specified in the
751 * STYLES{} section in the prefs file, and return the style type
754 winOverrideStyle(char *res_name
, char *res_class
, char *wmName
)
758 for (i
= 0; i
< pref
.styleItems
; i
++) {
759 if ((res_name
&& !strcmp(pref
.style
[i
].match
, res_name
)) ||
760 (res_class
&& !strcmp(pref
.style
[i
].match
, res_class
)) ||
761 (wmName
&& strstr(wmName
, pref
.style
[i
].match
))) {
762 if (pref
.style
[i
].type
)
763 return pref
.style
[i
].type
;
767 /* Didn't find the style, fail gracefully */