diff --git a/WPrefs.app/KeyboardShortcuts.c b/WPrefs.app/KeyboardShortcuts.c index 7d18a90a..f6750c1b 100644 --- a/WPrefs.app/KeyboardShortcuts.c +++ b/WPrefs.app/KeyboardShortcuts.c @@ -106,6 +106,13 @@ static struct keyOption { { "GroupNextKey", N_("Focus next group window") }, { "GroupPrevKey", N_("Focus previous group window") }, + /* Vim-like Window Marking */ + { "MarkSetKey", N_("Mark window: set mark") }, + { "MarkUnsetKey", N_("Mark window: unset mark") }, + { "MarkBringKey", N_("Mark window: bring marked window here") }, + { "MarkJumpKey", N_("Mark window: jump to marked window") }, + { "MarkSwapKey", N_("Mark window: swap with marked window") }, + /* Workspace Related */ { "WorkspaceMapKey", N_("Open workspace pager") }, { "NextWorkspaceKey", N_("Switch to next workspace") }, diff --git a/src/WindowMaker.h b/src/WindowMaker.h index f979573f..da727b37 100644 --- a/src/WindowMaker.h +++ b/src/WindowMaker.h @@ -609,6 +609,7 @@ extern struct wmaker_global_variables { Atom icon_size; Atom icon_tile; + Atom mark_key; } wmaker; } atom; diff --git a/src/defaults.c b/src/defaults.c index 101e4dc1..0a9da900 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -823,6 +823,17 @@ WDefaultEntry optionList[] = { NULL, getKeybind, setKeyGrab, NULL, NULL}, {"PartialCaptureKey", "None", (void *)WKBD_PRINTP, NULL, getKeybind, setKeyGrab, NULL, NULL}, + /* Vim-like Window Marking */ + {"MarkSetKey", "None", (void *)WKBD_MARK_SET, + NULL, getKeybind, setKeyGrab, NULL, NULL}, + {"MarkUnsetKey", "None", (void *)WKBD_MARK_UNSET, + NULL, getKeybind, setKeyGrab, NULL, NULL}, + {"MarkBringKey", "None", (void *)WKBD_MARK_BRING, + NULL, getKeybind, setKeyGrab, NULL, NULL}, + {"MarkJumpKey", "None", (void *)WKBD_MARK_JUMP, + NULL, getKeybind, setKeyGrab, NULL, NULL}, + {"MarkSwapKey", "None", (void *)WKBD_MARK_SWAP, + NULL, getKeybind, setKeyGrab, NULL, NULL}, #ifdef KEEP_XKB_LOCK_STATUS {"ToggleKbdModeKey", "None", (void *)WKBD_TOGGLE, diff --git a/src/event.c b/src/event.c index e5422ea4..c3063642 100644 --- a/src/event.c +++ b/src/event.c @@ -1471,6 +1471,12 @@ static void wCancelChainTimer(void) #define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded)) #define ISFOCUSED(w) ((w) && (w)->flags.focused) +static void startMarkCapture(WScreen *scr, Display *dpy, WMarkCaptureMode mode) +{ + scr->flags.mark_capture_mode = mode; + XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync, CurrentTime); +} + static void dispatchWKBDCommand(int command, WScreen *scr, WWindow *wwin, XEvent *event) { short widx; @@ -1975,6 +1981,29 @@ static void dispatchWKBDCommand(int command, WScreen *scr, WWindow *wwin, XEvent } break; #endif /* KEEP_XKB_LOCK_STATUS */ + + case WKBD_MARK_SET: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) + startMarkCapture(scr, dpy, MARK_CAPTURE_SET); + break; + + case WKBD_MARK_UNSET: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) && wwin->mark_key_label != NULL) + wWindowUnsetMark(wwin); + break; + + case WKBD_MARK_BRING: + startMarkCapture(scr, dpy, MARK_CAPTURE_BRING); + break; + + case WKBD_MARK_JUMP: + startMarkCapture(scr, dpy, MARK_CAPTURE_JUMP); + break; + + case WKBD_MARK_SWAP: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) + startMarkCapture(scr, dpy, MARK_CAPTURE_SWAP); + break; } } @@ -1990,6 +2019,118 @@ static void handleKeyPress(XEvent * event) /* ignore CapsLock */ modifiers = event->xkey.state & w_global.shortcut.modifiers_mask; + /* ----------------------------------------------------------- * + * Window mark capture mode * + * * + * We grabbed the keyboard so all keypresses come here until * + * we release the grab. * + * ------------------------------------------------------------ */ + if (scr->flags.mark_capture_mode != MARK_CAPTURE_IDLE) { + int capture_mode = scr->flags.mark_capture_mode; + KeySym cap_ksym; + char label[MAX_SHORTCUT_LENGTH]; + + /* Skip modifier-only keypresses */ + cap_ksym = XLookupKeysym(&event->xkey, 0); + if (cap_ksym == NoSymbol || IsModifierKey(cap_ksym)) + return; + + /* Real key received: exit capture mode and release grab */ + scr->flags.mark_capture_mode = MARK_CAPTURE_IDLE; + XUngrabKeyboard(dpy, CurrentTime); + + if (!GetCanonicalShortcutLabel(modifiers, cap_ksym, label, sizeof(label))) + wstrlcpy(label, "?", sizeof(label)); + + if (capture_mode == MARK_CAPTURE_SET) { + WWindow *target = scr->focused_window; + Bool conflict = False; + int i; + + /* Conflict check against static wmaker bindings */ + for (i = 0; i < WKBD_LAST; i++) { + if (wKeyBindings[i].keycode == event->xkey.keycode && wKeyBindings[i].modifier == modifiers) { + wwarning("window mark: '%s' is already a wmaker binding, mark not assigned", label); + conflict = True; + break; + } + } + + /* Conflict check against existing marks on other windows */ + if (!conflict) { + WWindow *tw = scr->focused_window; + while (tw) { + if (tw != target && tw->mark_key_label != NULL && strcmp(tw->mark_key_label, label) == 0) { + wwarning("window mark: label '%s' is already used by another window, mark not assigned", label); + conflict = True; + break; + } + tw = tw->prev; + } + } + + if (!conflict && target != NULL) + wWindowSetMark(target, label); + + } else { + /* Find marked window by label */ + WWindow *tw = scr->focused_window; + + while (tw) { + if (tw->mark_key_label != NULL && strcmp(tw->mark_key_label, label) == 0) + break; + tw = tw->prev; + } + if (tw == NULL) { + wwarning("window mark: no window labelled '%s'", label); + } else if (capture_mode == MARK_CAPTURE_BRING) { + if (tw->frame->workspace != scr->current_workspace) + wWindowChangeWorkspace(tw, scr->current_workspace); + wMakeWindowVisible(tw); + } else if (capture_mode == MARK_CAPTURE_JUMP) { + wMakeWindowVisible(tw); + } else { + /* MARK_CAPTURE_SWAP: swap position, size and workspace between focused and tw */ + WWindow *focused = scr->focused_window; + int fx, fy, fw, fh, tx, ty, tw_w, tw_h; + int f_ws, t_ws; + + if (focused == NULL || focused == tw) + return; + + /* Snapshot both geometries */ + fx = focused->frame_x; + fy = focused->frame_y; + fw = focused->client.width; + fh = focused->client.height; + f_ws = focused->frame->workspace; + + tx = tw->frame_x; + ty = tw->frame_y; + tw_w = tw->client.width; + tw_h = tw->client.height; + t_ws = tw->frame->workspace; + + /* Swap workspaces first so configure lands in the right one */ + if (f_ws != t_ws) { + wWindowChangeWorkspace(focused, t_ws); + wWindowChangeWorkspace(tw, f_ws); + } + + /* Swap positions and sizes */ + wWindowConfigure(focused, tx, ty, tw_w, tw_h); + wWindowConfigure(tw, fx, fy, fw, fh); + + /* Follow origin window: switch view to the workspace it landed in, + * then restore focus to it */ + if (f_ws != t_ws) + wWorkspaceChange(scr, t_ws); + wSetFocusTo(scr, focused); + } + } + return; + } + /* ------------------------------------------------------------------ * * Trie-based key-chain matching * * * diff --git a/src/keybind.h b/src/keybind.h index 292cbb71..ae9a5022 100644 --- a/src/keybind.h +++ b/src/keybind.h @@ -80,6 +80,11 @@ enum { WKBD_GROUPNEXT, WKBD_GROUPPREV, WKBD_CENTRAL, + WKBD_MARK_SET, + WKBD_MARK_UNSET, + WKBD_MARK_BRING, + WKBD_MARK_JUMP, + WKBD_MARK_SWAP, /* window, menu */ WKBD_CLOSE, diff --git a/src/misc.c b/src/misc.c index 38d6238c..788c1b5d 100644 --- a/src/misc.c +++ b/src/misc.c @@ -847,6 +847,34 @@ static char *keysymToString(KeySym keysym, unsigned int state) } #endif +Bool GetCanonicalShortcutLabel(unsigned int modifiers, KeySym ksym, char *buf, size_t bufsz) +{ + static const struct { unsigned int mask; const char *name; } mt[] = { + { ShiftMask, "Shift+" }, + { ControlMask, "Control+" }, + { Mod1Mask, "Mod1+" }, + { Mod2Mask, "Mod2+" }, + { Mod3Mask, "Mod3+" }, + { Mod4Mask, "Mod4+" }, + { Mod5Mask, "Mod5+" }, + { 0, NULL } + }; + const char *kname = XKeysymToString(ksym); + size_t i; + + if (!kname) + return False; + + buf[0] = '\0'; + for (i = 0; mt[i].mask; i++) { + if (modifiers & mt[i].mask) + wstrlcat(buf, mt[i].name, bufsz); + } + wstrlcat(buf, kname, bufsz); + + return True; +} + char *GetShortcutString(const char *shortcut) { char *buffer = NULL; @@ -882,50 +910,12 @@ char *GetShortcutString(const char *shortcut) char *GetShortcutKey(WShortKey key) { char buf[MAX_SHORTCUT_LENGTH]; - char *wr, *result, *seg; + char *result, *seg; int step; - void append_string(const char *text) - { - const char *s = text; - - while (*s) { - if (wr >= buf + sizeof(buf) - 1) - break; - *wr++ = *s++; - } - } - - void append_modifier(int modifier_index, const char *fallback_name) - { - if (wPreferences.modifier_labels[modifier_index]) - append_string(wPreferences.modifier_labels[modifier_index]); - else - append_string(fallback_name); - } - - Bool build_token(unsigned int mod, KeyCode kcode) - { - const char *kname = XKeysymToString(W_KeycodeToKeysym(dpy, kcode, 0)); - - if (!kname) - return False; - - wr = buf; - if (mod & ControlMask) append_modifier(1, "Control+"); - if (mod & ShiftMask) append_modifier(0, "Shift+"); - if (mod & Mod1Mask) append_modifier(2, "Mod1+"); - if (mod & Mod2Mask) append_modifier(3, "Mod2+"); - if (mod & Mod3Mask) append_modifier(4, "Mod3+"); - if (mod & Mod4Mask) append_modifier(5, "Mod4+"); - if (mod & Mod5Mask) append_modifier(6, "Mod5+"); - append_string(kname); - *wr = '\0'; - - return True; - } - - if (!build_token(key.modifier, key.keycode)) + if (!GetCanonicalShortcutLabel(key.modifier, + W_KeycodeToKeysym(dpy, key.keycode, 0), + buf, sizeof(buf))) return NULL; /* Convert the leader token to its display string */ @@ -938,7 +928,9 @@ char *GetShortcutKey(WShortKey key) if (key.chain_keycodes[step] == 0) break; - if (!build_token(key.chain_modifiers[step], key.chain_keycodes[step])) + if (!GetCanonicalShortcutLabel(key.chain_modifiers[step], + W_KeycodeToKeysym(dpy, key.chain_keycodes[step], 0), + buf, sizeof(buf))) break; seg = GetShortcutString(buf); diff --git a/src/misc.h b/src/misc.h index 5fc15a8f..288a8bb4 100644 --- a/src/misc.h +++ b/src/misc.h @@ -50,6 +50,7 @@ char *ExpandOptions(WScreen * scr, const char *cmdline); void ExecuteInputCommand(WScreen *scr, const char *cmdline); void ExecuteExitCommand(WScreen *scr, long quickmode); char *GetShortcutString(const char *text); +Bool GetCanonicalShortcutLabel(unsigned int modifiers, KeySym ksym, char *buf, size_t bufsz); char *GetShortcutKey(WShortKey key); char *EscapeWM_CLASS(const char *name, const char *class); char *StrConcatDot(const char *a, const char *b); diff --git a/src/screen.h b/src/screen.h index c3100b0b..9ecb1253 100644 --- a/src/screen.h +++ b/src/screen.h @@ -59,6 +59,14 @@ typedef struct WDrawerChain { struct WDrawerChain *next; } WDrawerChain; +typedef enum { + MARK_CAPTURE_IDLE = 0, + MARK_CAPTURE_SET = 1, + MARK_CAPTURE_BRING = 2, + MARK_CAPTURE_JUMP = 3, + MARK_CAPTURE_SWAP = 4 +} WMarkCaptureMode; + /* * each WScreen is saved into a context associated with its root window */ @@ -332,6 +340,8 @@ typedef struct _WScreen { unsigned int jump_back_pending:1; unsigned int ignore_focus_events:1; unsigned int in_hot_corner:3; + unsigned int mark_capture_mode:3; /* one of WMarkCaptureMode */ + } flags; } WScreen; diff --git a/src/session.c b/src/session.c index 89df5b54..9d66b218 100644 --- a/src/session.c +++ b/src/session.c @@ -97,6 +97,7 @@ static WMPropList *sMaximized; static WMPropList *sHidden; static WMPropList *sGeometry; static WMPropList *sShortcutMask; +static WMPropList *sMarkKey; static WMPropList *sDock; static WMPropList *sYes, *sNo; @@ -118,6 +119,7 @@ static void make_keys(void) sGeometry = WMCreatePLString("Geometry"); sDock = WMCreatePLString("Dock"); sShortcutMask = WMCreatePLString("ShortcutMask"); + sMarkKey = WMCreatePLString("MarkKey"); sYes = WMCreatePLString("Yes"); sNo = WMCreatePLString("No"); @@ -165,6 +167,7 @@ static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp) WMPropList *win_state, *cmd, *name, *workspace; WMPropList *shaded, *miniaturized, *maximized, *hidden, *geometry; WMPropList *dock, *shortcut; + WMPropList *mark_key_pl = NULL; if (wwin->orig_main_window != None && wwin->orig_main_window != wwin->client_win) win = wwin->orig_main_window; @@ -215,6 +218,9 @@ static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp) snprintf(buffer, sizeof(buffer), "%u", mask); shortcut = WMCreatePLString(buffer); + if (wwin->mark_key_label) + mark_key_pl = WMCreatePLString(wwin->mark_key_label); + win_state = WMCreatePLDictionary(sName, name, sCommand, cmd, sWorkspace, workspace, @@ -224,6 +230,11 @@ static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp) sHidden, hidden, sShortcutMask, shortcut, sGeometry, geometry, NULL); + if (mark_key_pl) { + WMPutInPLDictionary(win_state, sMarkKey, mark_key_pl); + WMReleasePropList(mark_key_pl); + } + WMReleasePropList(name); WMReleasePropList(cmd); WMReleasePropList(workspace); @@ -418,6 +429,13 @@ static WSavedState *getWindowState(WScreen * scr, WMPropList * win_state) state->window_shortcuts = mask; } + value = WMGetFromPLDictionary(win_state, sMarkKey); + if (value != NULL && WMIsPLString(value)) { + char *s = WMGetFromPLString(value); + if (s && *s) + state->mark_key = wstrdup(s); + } + value = WMGetFromPLDictionary(win_state, sGeometry); if (value && WMIsPLString(value)) { if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i", @@ -531,6 +549,8 @@ void wSessionRestoreState(WScreen *scr) } else if ((pid = execCommand(scr, command)) > 0) { wWindowAddSavedState(instance, class, command, pid, state); } else { + if (state->mark_key) + wfree(state->mark_key); wfree(state); } diff --git a/src/startup.c b/src/startup.c index ed992901..b0e676b2 100644 --- a/src/startup.c +++ b/src/startup.c @@ -387,6 +387,7 @@ static char *atomNames[] = { "_WINDOWMAKER_COMMAND", "_WINDOWMAKER_ICON_SIZE", "_WINDOWMAKER_ICON_TILE", + "_WINDOWMAKER_MARK_KEY", GNUSTEP_WM_ATTR_NAME, GNUSTEP_WM_MINIATURIZE_WINDOW, @@ -474,6 +475,7 @@ void StartUp(Bool defaultScreenOnly) w_global.atom.desktop.gtk_object_path = atom[20]; w_global.atom.wm.ignore_focus_events = atom[21]; + w_global.atom.wmaker.mark_key = atom[22]; #ifdef USE_DOCK_XDND wXDNDInitializeAtoms(); diff --git a/src/switchmenu.c b/src/switchmenu.c index a8c3937f..0f1746f6 100644 --- a/src/switchmenu.c +++ b/src/switchmenu.c @@ -44,6 +44,8 @@ ((w)->wm_gnustep_attr->window_level == WMMainMenuWindowLevel || \ (w)->wm_gnustep_attr->window_level == WMSubmenuWindowLevel)) +#define MAX_RTEXT_LENGTH (MAX_WORKSPACENAME_WIDTH + MAX_SHORTCUT_LENGTH + 16) + static int initialized = 0; static void observer(void *self, WMNotification * notif); static void wsobserver(void *self, WMNotification * notif); @@ -214,6 +216,26 @@ static int menuIndexForWindow(WMenu * menu, WWindow * wwin, int old_pos) return idx; } +static void fillRtext(char *buf, size_t bufsz, WWindow *wwin, WScreen *scr) +{ + char *mlbl = wwin->mark_key_label ? GetShortcutString(wwin->mark_key_label) : NULL; + + if (IS_OMNIPRESENT(wwin)) { + if (mlbl) + snprintf(buf, bufsz, "[%s] [*]", mlbl); + else + snprintf(buf, bufsz, "[*]"); + } else { + if (mlbl) + snprintf(buf, bufsz, "[%s] [%s]", mlbl, + scr->workspaces[wwin->frame->workspace]->name); + else + snprintf(buf, bufsz, "[%s]", + scr->workspaces[wwin->frame->workspace]->name); + } + wfree(mlbl); +} + /* * Update switch menu */ @@ -263,12 +285,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) entry->icon = switchMenuIconForWindow(scr, wwin); entry->flags.indicator = 1; - entry->rtext = wmalloc(MAX_WORKSPACENAME_WIDTH + 8); - if (IS_OMNIPRESENT(wwin)) - snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[*]"); - else - snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]", - scr->workspaces[wwin->frame->workspace]->name); + entry->rtext = wmalloc(MAX_RTEXT_LENGTH); + fillRtext(entry->rtext, MAX_RTEXT_LENGTH, wwin, scr); if (wwin->flags.hidden) { entry->flags.indicator_type = MI_HIDDEN; @@ -311,6 +329,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) t = ShrinkString(scr->menu_entry_font, title, MAX_WINDOWLIST_WIDTH); entry->text = t; + fillRtext(entry->rtext, MAX_RTEXT_LENGTH, wwin, scr); + wMenuRealize(switchmenu); checkVisibility = 1; break; @@ -322,13 +342,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action) WPixmap *ipix; int it, ion; - if (IS_OMNIPRESENT(wwin)) { - snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[*]"); - } else { - snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, - "[%s]", - scr->workspaces[wwin->frame->workspace]->name); - } + fillRtext(entry->rtext, MAX_RTEXT_LENGTH, wwin, scr); rt = entry->rtext; entry->rtext = NULL; @@ -404,11 +418,7 @@ static void UpdateSwitchMenuWorkspace(WScreen *scr, int workspace) wwin = (WWindow *) menu->entries[i]->clientdata; if (wwin->frame->workspace == workspace && !IS_OMNIPRESENT(wwin)) { - if (IS_OMNIPRESENT(wwin)) - snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[*]"); - else - snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]", - scr->workspaces[wwin->frame->workspace]->name); + fillRtext(menu->entries[i]->rtext, MAX_RTEXT_LENGTH, wwin, scr); menu->flags.realized = 0; } } diff --git a/src/window.c b/src/window.c index 9cb11882..470cfcab 100644 --- a/src/window.c +++ b/src/window.c @@ -75,6 +75,7 @@ #include "startup.h" #include "winmenu.h" #include "osdep.h" +#include "switchmenu.h" #ifdef USE_MWM_HINTS # include "motif.h" @@ -200,6 +201,10 @@ void wWindowDestroy(WWindow *wwin) } } + /* clean up any mark assigned to this window */ + if (wwin->mark_key_label != NULL) + wWindowUnsetMark(wwin); + if (wwin->fake_group && wwin->fake_group->retainCount > 0) { wwin->fake_group->retainCount--; if (wwin->fake_group->retainCount == 0 && wwin->fake_group->leader != None) { @@ -1006,8 +1011,17 @@ WWindow *wManageWindow(WScreen *scr, Window window) } } - if (wstate != NULL) + /* restore mark: prefer session state, fall back to warm-restart hint */ + if (win_state != NULL && win_state->state->mark_key != NULL) + wWindowSetMark(wwin, win_state->state->mark_key); + else if (wstate != NULL && wstate->mark_key != NULL) + wWindowSetMark(wwin, wstate->mark_key); + + if (wstate != NULL) { + if (wstate->mark_key != NULL) + wfree(wstate->mark_key); wfree(wstate); + } } /* don't let transients start miniaturized if their owners are not */ @@ -2527,6 +2541,14 @@ void wWindowSaveState(WWindow *wwin) XChangeProperty(dpy, wwin->client_win, w_global.atom.wmaker.state, w_global.atom.wmaker.state, 32, PropModeReplace, (unsigned char *)data, 10); + + if (wwin->mark_key_label != NULL) + XChangeProperty(dpy, wwin->client_win, w_global.atom.wmaker.mark_key, + XA_STRING, 8, PropModeReplace, + (unsigned char *)wwin->mark_key_label, + strlen(wwin->mark_key_label)); + else + XDeleteProperty(dpy, wwin->client_win, w_global.atom.wmaker.mark_key); } static int getSavedState(Window window, WSavedState ** state) @@ -2536,6 +2558,7 @@ static int getSavedState(Window window, WSavedState ** state) unsigned long nitems_ret; unsigned long bytes_after_ret; long *data; + unsigned char *mk_data = NULL; if (XGetWindowProperty(dpy, window, w_global.atom.wmaker.state, 0, 10, True, w_global.atom.wmaker.state, @@ -2563,6 +2586,15 @@ static int getSavedState(Window window, WSavedState ** state) XFree(data); + (*state)->mark_key = NULL; + if (XGetWindowProperty(dpy, window, w_global.atom.wmaker.mark_key, 0, 256, + True, XA_STRING, &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret, + &mk_data) == Success && mk_data && nitems_ret > 0 && type_ret == XA_STRING) + (*state)->mark_key = wstrdup((char *)mk_data); + + if (mk_data) + XFree(mk_data); + return 1; } @@ -2889,6 +2921,9 @@ static void release_wwindowstate(WWindowState *wstate) if (wstate->command) wfree(wstate->command); + if (wstate->state && wstate->state->mark_key) + wfree(wstate->state->mark_key); + wfree(wstate->state); wfree(wstate); } @@ -2902,6 +2937,29 @@ void wWindowSetOmnipresent(WWindow *wwin, Bool flag) WMPostNotificationName(WMNChangedState, wwin, "omnipresent"); } +void wWindowSetMark(WWindow *wwin, const char *label) +{ + /* Remove any previous mark first */ + if (wwin->mark_key_label != NULL) + wWindowUnsetMark(wwin); + + wwin->mark_key_label = wstrdup(label); + + UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE); +} + +void wWindowUnsetMark(WWindow *wwin) +{ + if (wwin->mark_key_label == NULL) + return; + + wfree(wwin->mark_key_label); + wwin->mark_key_label = NULL; + + UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE); +} + + static void resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event) { WWindow *wwin = data; diff --git a/src/window.h b/src/window.h index 8b14564e..1229eed3 100644 --- a/src/window.h +++ b/src/window.h @@ -299,6 +299,7 @@ typedef struct WWindow { int icon_w, icon_h; RImage *net_icon_image; /* Window Image */ Atom type; + char *mark_key_label; /* Vim-like Window Marking */ } WWindow; #define HAS_TITLEBAR(w) (!(WFLAGP((w), no_titlebar) || (w)->flags.fullscreen)) @@ -323,6 +324,7 @@ typedef struct WSavedState { unsigned int w; unsigned int h; unsigned window_shortcuts; /* mask like 1<