These patches implement three variations of what I consider to be a
fairly useful feature of Windows 7 (of all things), namely the ability
to middle click on a taskbar entry and have a new instance of that
application be launched. So for example if you middle click on
Explorer a new Explorer window pops up.
I've split the patches up into separate methods of accomplishing
the same thing in case one or more are seen as undesirable.
Patch 1 defines a hotkey, WindowRelaunchKey, which when pushed will
launch a new instance of the focused window.
Patch 2 extends the functionality to the window menu, adding a new
Launch entry to do the same thing.
Patch 3 defines two ways to relaunch applications from their
appicon, either by selecting the new Launch menu entry or with a
Microsoft-style middle click.
In all cases the target window's WM_COMMAND property is queried and
used to build an argv list to launch a new instance of the
application. If for some reason WM_COMMAND is not set it won't work.
My first attempt used GetCommandForWindow() and
ExecuteShellCommand() but that didn't handle complex WM_COMMAND arrays
which aren't properly escaped, hence the addition of a slightly
different exec()ing function.
From 2b8ee89327423abb46c414b06ad8a2d8312e1adc Mon Sep 17 00:00:00 2001
From: Iain Patterson <[email protected]>
Date: Fri, 30 Mar 2012 17:35:57 +0100
Subject: [PATCH 1/3] Allow relaunch with shortcut key.
Use the WindowRelaunchKey shortcut to examine the WM_COMMAND property of
the active application's main window and launch a new instance of the
application using the retrieved command line.
---
WPrefs.app/KeyboardShortcuts.c | 2 +
src/defaults.c | 2 +
src/event.c | 6 ++++
src/funcs.h | 2 +
src/keybind.h | 3 ++
src/main.c | 62 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 77 insertions(+), 0 deletions(-)
diff --git a/WPrefs.app/KeyboardShortcuts.c b/WPrefs.app/KeyboardShortcuts.c
index 4fd5305..aad8f33 100644
--- a/WPrefs.app/KeyboardShortcuts.c
+++ b/WPrefs.app/KeyboardShortcuts.c
@@ -112,6 +112,7 @@ static char *keyOptions[] = {
"WindowShortcut8Key",
"WindowShortcut9Key",
"WindowShortcut10Key",
+ "WindowRelaunchKey",
"ScreenSwitchKey",
"DockRaiseLowerKey",
#ifndef XKB_MODELOCK
@@ -499,6 +500,7 @@ static void createPanel(Panel * p)
WMAddListItem(panel->actLs, _("Shortcut for window 8"));
WMAddListItem(panel->actLs, _("Shortcut for window 9"));
WMAddListItem(panel->actLs, _("Shortcut for window 10"));
+ WMAddListItem(panel->actLs, _("Launch new instance of application"));
WMAddListItem(panel->actLs, _("Switch to Next Screen/Monitor"));
WMAddListItem(panel->actLs, _("Raise/Lower Dock"));
WMAddListItem(panel->actLs, _("Raise/Lower Clip"));
diff --git a/src/defaults.c b/src/defaults.c
index 5b5269b..dba325a 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -639,6 +639,8 @@ WDefaultEntry optionList[] = {
NULL, getKeybind, setKeyGrab, NULL, NULL},
{"WindowShortcut10Key", "None", (void *)WKBD_WINDOW10,
NULL, getKeybind, setKeyGrab, NULL, NULL},
+ {"WindowRelaunchKey", "None", (void *)WKBD_RELAUNCH,
+ NULL, getKeybind, setKeyGrab, NULL, NULL},
{"ScreenSwitchKey", "None", (void *)WKBD_SWITCH_SCREEN,
NULL, getKeybind, setKeyGrab, NULL, NULL},
diff --git a/src/event.c b/src/event.c
index 3bc3c40..50c5a56 100644
--- a/src/event.c
+++ b/src/event.c
@@ -1651,6 +1651,12 @@ static void handleKeyPress(XEvent * event)
break;
+ case WKBD_RELAUNCH:
+ if (ISMAPPED(wwin) && ISFOCUSED(wwin))
+ (void) RelaunchWindow(wwin);
+
+ break;
+
case WKBD_SWITCH_SCREEN:
if (wScreenCount > 1) {
WScreen *scr2;
diff --git a/src/funcs.h b/src/funcs.h
index 6d2c552..ce9a99d 100644
--- a/src/funcs.h
+++ b/src/funcs.h
@@ -98,6 +98,8 @@ char *ExpandOptions(WScreen *scr, char *cmdline);
void ExecuteShellCommand(WScreen *scr, char *command);
+Bool RelaunchWindow(WWindow *wwin);
+
Bool IsDoubleClick(WScreen *scr, XEvent *event);
WWindow *NextToFocusAfter(WWindow *wwin);
diff --git a/src/keybind.h b/src/keybind.h
index e8b503c..99137d8 100644
--- a/src/keybind.h
+++ b/src/keybind.h
@@ -86,6 +86,9 @@ enum {
WKBD_WINDOW9,
WKBD_WINDOW10,
+ /* launch a new instance of the active window */
+ WKBD_RELAUNCH,
+
/* screen */
WKBD_SWITCH_SCREEN,
diff --git a/src/main.c b/src/main.c
index 9fc9976..7d44983 100644
--- a/src/main.c
+++ b/src/main.c
@@ -400,6 +400,68 @@ void ExecuteShellCommand(WScreen * scr, char *command)
/*
*---------------------------------------------------------------------
+ * RelaunchWindow--
+ * Launch a new instance of the active window
+ *
+ *----------------------------------------------------------------------
+ */
+Bool RelaunchWindow(WWindow *wwin)
+{
+ if (! wwin || ! wwin->client_win) {
+ werror("no window to relaunch");
+ return False;
+ }
+
+ char **argv;
+ int argc;
+
+ if (! XGetCommand(dpy, wwin->client_win, &argv, &argc) || argc == 0 ||
argv == NULL) {
+ werror("cannot relaunch the application because no WM_COMMAND
property is set");
+ return False;
+ }
+
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ SetupEnvironment(wwin->screen_ptr);
+#ifdef HAVE_SETSID
+ setsid();
+#endif
+ /* argv is not null-terminated */
+ char **a = (char **) malloc(argc + 1);
+ if (! a) {
+ werror("out of memory trying to relaunch the
application");
+ Exit(-1);
+ }
+
+ int i;
+ for (i = 0; i < argc; i++) a[i] = argv[i];
+ a[i] = NULL;
+
+ execvp(a[0], a);
+ Exit(-1);
+ } else if (pid < 0) {
+ werror("cannot fork a new process");
+
+ XFreeStringList(argv);
+ return False;
+ } else {
+ _tuple *data = wmalloc(sizeof(_tuple));
+
+ data->scr = wwin->screen_ptr;
+ data->command = wtokenjoin(argv, argc);
+
+ /* not actually a shell command */
+ wAddDeathHandler(pid, (WDeathHandler *) shellCommandHandler,
data);
+
+ XFreeStringList(argv);
+ return True;
+ }
+
+}
+
+/*
+ *---------------------------------------------------------------------
* wAbort--
* Do a major cleanup and exit the program
*
--
1.7.7.6
From 6ffb09ca6e2b738e8d92ccc55f92a74d4bd2075e Mon Sep 17 00:00:00 2001
From: Iain Patterson <[email protected]>
Date: Fri, 30 Mar 2012 17:36:07 +0100
Subject: [PATCH 2/3] Also allow relaunching from the window menu.
Allow relaunching an application from its window menu.
---
src/winmenu.c | 12 ++++++++++--
1 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/winmenu.c b/src/winmenu.c
index 8e657e6..ef97126 100644
--- a/src/winmenu.c
+++ b/src/winmenu.c
@@ -55,9 +55,10 @@
#define MC_PROPERTIES 7
#define MC_OPTIONS 8
#define MC_SHORTCUT 8
+#define MC_RELAUNCH 9
-#define MC_CLOSE 9
-#define MC_KILL 10
+#define MC_CLOSE 10
+#define MC_KILL 11
#define WO_KEEP_ON_TOP 0
#define WO_KEEP_AT_BOTTOM 1
@@ -167,6 +168,10 @@ static void execMenuCommand(WMenu * menu, WMenuEntry *
entry)
wShowInspectorForWindow(wwin);
break;
+ case MC_RELAUNCH:
+ (void) RelaunchWindow(wwin);
+ break;
+
case MC_HIDE:
wapp = wApplicationOf(wwin->main_window);
wHideApplication(wapp);
@@ -454,6 +459,9 @@ static WMenu *createWindowMenu(WScreen * scr)
wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
*/
+ entry = wMenuAddCallback(menu, _("Launch"), execMenuCommand, NULL);
+ entry->rtext = getShortcutString(wKeyBindings[WKBD_RELAUNCH]);
+
entry = wMenuAddCallback(menu, _("Close"), execMenuCommand, NULL);
entry->rtext = getShortcutString(wKeyBindings[WKBD_CLOSE]);
--
1.7.7.6
From 8f678f0d4a7a531f5ab2268c27c9ff00967373db Mon Sep 17 00:00:00 2001
From: Iain Patterson <[email protected]>
Date: Fri, 30 Mar 2012 17:36:12 +0100
Subject: [PATCH 3/3] Also allow relaunching from appicon.
Allow relaunching an application with a middle mouse click to its
appicon or the Launch option of its appicon menu.
---
src/appicon.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 44 insertions(+), 0 deletions(-)
diff --git a/src/appicon.c b/src/appicon.c
index d05413b..19f5614 100644
--- a/src/appicon.c
+++ b/src/appicon.c
@@ -295,6 +295,39 @@ Bool wAppIconSave(WAppIcon *aicon)
#define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
+/* main_window may not have the full command line; try to find one which does
*/
+static void relaunchApplication(WApplication *wapp)
+{
+ WScreen *scr;
+ WWindow *wlist, *next;
+
+ scr = wapp->main_window_desc->screen_ptr;
+ wlist = scr->focused_window;
+ if (! wlist)
+ return;
+
+ while (wlist->prev)
+ wlist = wlist->prev;
+
+ while (wlist) {
+ next = wlist->next;
+
+ if (wlist->main_window == wapp->main_window) {
+ if (RelaunchWindow(wlist))
+ return;
+ }
+
+ wlist = next;
+ }
+}
+
+static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
+{
+ WApplication *wapp = (WApplication *) entry->clientdata;
+
+ relaunchApplication(wapp);
+}
+
static void hideCallback(WMenu * menu, WMenuEntry * entry)
{
WApplication *wapp = (WApplication *) entry->clientdata;
@@ -403,6 +436,7 @@ static WMenu *createApplicationMenu(WScreen * scr)
WMenu *menu;
menu = wMenuCreate(scr, NULL, False);
+ wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
@@ -505,6 +539,16 @@ void appIconMouseDown(WObjDescriptor * desc, XEvent *
event)
return;
}
+ if (event->xbutton.button == Button2) {
+ WApplication *wapp =
wApplicationOf(aicon->icon->owner->main_window);
+
+ if (!wapp)
+ return;
+
+ relaunchApplication(wapp);
+ return;
+ }
+
if (event->xbutton.button == Button3) {
WObjDescriptor *desc;
WApplication *wapp =
wApplicationOf(aicon->icon->owner->main_window);
--
1.7.7.6