xserver-multidpi/hw/xwin/winprefs.c
Jon TURNEY 16d9da0886 hw/xwin: Improve choice of display name used by internal clients
Choose the display name used to connect to internal clients and exported into
environment of processes started from the traymenu so that it uses a transport
we know is working

This should mean the server can start correctly with -multiwindow and/or
-clipboard and any two of -nolisten inet6, -nolisten inet and -nolisten unix
(the server will correctly refuse to start if all 3 are used, as it must be
listening on at least one socket)

v2:
Place prototype for winGetDisplayName() in windisplay.h, and include it where
needed.

v3:
Include xwin-config.h, so that _XSERVER64 is defined, just in case anything
relies on that.

v4:
Replace grovelling around in the server's list of listeners with new Xtrans
TransIsListening() interface, added in Xtrans 1.3.3

See also [1]

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=10725

Signed-off-by: Jon TURNEY <jon.turney@dronecode.org.uk>
Reviewed-by: Yaakov Selkowitz <yselkowitz@users.sourceforge.net>
Reviewed-by: Colin Harrison <colin.harrison@virgin.net>
2014-08-29 13:28:00 +01:00

770 lines
22 KiB
C

/*
* 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 "windisplay.h"
#include "winmultiwindowclass.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;
/* 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;
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;
}
#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(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 proccessed, FALSE otherwise.
*/
Bool
HandleCustomWM_COMMAND(HWND hwnd, int command)
{
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);
#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(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);
}
}
#endif
/*
* 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, 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 i;
char file[PATH_MAX + NAME_MAX + 2];
/* 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 {
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 */
i = atoi(strrchr(fname, ',') + 1);
hicon = ExtractIcon(g_hInstance, file, i);
}
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(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, 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(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;
}