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

Reply via email to