diff --git a/src/WindowMaker.h b/src/WindowMaker.h index dffa9167..14392c4d 100644 --- a/src/WindowMaker.h +++ b/src/WindowMaker.h @@ -474,6 +474,11 @@ extern struct WPreferences { char show_clip_title; + char hot_corners; /* let corners execute actions */ + int hot_corner_delay; /* Delay after which the hot corner is triggered */ + int hot_corner_edge; /* Hot corner edge size */ + char *hot_corner_actions[4]; /* Action of each corner */ + struct { #ifdef USE_ICCCM_WMREPLACE unsigned int replace:1; /* replace existing window manager */ diff --git a/src/defaults.c b/src/defaults.c index f5cbf46f..3b848b15 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -151,6 +151,7 @@ static WDECallbackUpdate setSwPOptions; static WDECallbackUpdate updateUsableArea; static WDECallbackUpdate setModifierKeyLabels; +static WDECallbackUpdate setHotCornerActions; static WDECallbackConvert getCursor; static WDECallbackUpdate setCursor; @@ -525,6 +526,14 @@ WDefaultEntry optionList[] = { {"KeepDockOnPrimaryHead", "NO", NULL, &wPreferences.keep_dock_on_primary_head, getBool, updateDock, NULL, NULL}, + {"HotCorners", "NO", NULL, + &wPreferences.hot_corners, getBool, NULL, NULL, NULL}, + {"HotCornerDelay", "250", (void *)&wPreferences.hot_corner_delay, + &wPreferences.hot_corner_delay, getInt, NULL, NULL, NULL}, + {"HotCornerEdge", "2", (void *)&wPreferences.hot_corner_edge, + &wPreferences.hot_corner_edge, getInt, NULL, NULL, NULL}, + {"HotCornerActions", "(\"None\", \"None\", \"None\", \"None\")", &wPreferences, + NULL, getPropList, setHotCornerActions, NULL, NULL}, /* style options */ @@ -3461,6 +3470,43 @@ static int setModifierKeyLabels(WScreen * scr, WDefaultEntry * entry, void *tdat return 0; } +static int setHotCornerActions(WScreen * scr, WDefaultEntry * entry, void *tdata, void *foo) +{ + WMPropList *array = tdata; + int i; + struct WPreferences *prefs = foo; + + if (!WMIsPLArray(array) || WMGetPropListItemCount(array) != 4) { + wwarning(_("Value for option \"%s\" must be an array of 4 strings"), entry->key); + WMReleasePropList(array); + return 0; + } + + DestroyWindowMenu(scr); + + for (i = 0; i < 4; i++) { + if (prefs->hot_corner_actions[i]) + wfree(prefs->hot_corner_actions[i]); + + if (WMIsPLString(WMGetFromPLArray(array, i))) { + const char *val; + val = WMGetFromPLString(WMGetFromPLArray(array, i)); + if (strcasecmp(val, "NONE") != 0) + prefs->hot_corner_actions[i] = wstrdup(val); + else + prefs->hot_corner_actions[i] = NULL; + } else { + wwarning(_("Invalid argument for option \"%s\" item %d"), entry->key, i); + prefs->hot_corner_actions[i] = NULL; + } + } + + WMReleasePropList(array); + + return 0; +} + + static int setDoubleClick(WScreen *scr, WDefaultEntry *entry, void *tdata, void *foo) { int *value = tdata; diff --git a/src/event.c b/src/event.c index 69f284ac..c843b0c0 100644 --- a/src/event.c +++ b/src/event.c @@ -1928,27 +1928,104 @@ static void handleKeyPress(XEvent * event) } } -static void handleMotionNotify(XEvent * event) -{ - WScreen *scr = wScreenForRootWindow(event->xmotion.root); +#define CORNER_NONE 0 +#define CORNER_TOPLEFT 1 +#define CORNER_TOPRIGHT 2 +#define CORNER_BOTTOMLEFT 3 +#define CORNER_BOTTOMRIGHT 4 - if (wPreferences.scrollable_menus) { +static int get_corner(WMRect rect, WMPoint p) +{ + if (p.x <= (rect.pos.x + wPreferences.hot_corner_edge) && p.y <= (rect.pos.y + wPreferences.hot_corner_edge)) + return CORNER_TOPLEFT; + if (p.x >= (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge) && p.y <= (rect.pos.y + wPreferences.hot_corner_edge)) + return CORNER_TOPRIGHT; + if (p.x <= (rect.pos.x + wPreferences.hot_corner_edge) && p.y >= (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge)) + return CORNER_BOTTOMLEFT; + if (p.x >= (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge) && p.y >= (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge)) + return CORNER_BOTTOMRIGHT; + return CORNER_NONE; +} + +static void hotCornerDelay(void *data) +{ + WScreen *scr = (WScreen *) data; + if (scr->flags.in_hot_corner && wPreferences.hot_corner_actions[scr->flags.in_hot_corner - 1]) + ExecuteShellCommand(scr, wPreferences.hot_corner_actions[scr->flags.in_hot_corner - 1]); + WMDeleteTimerHandler(scr->hot_corner_timer); + scr->hot_corner_timer = NULL; +} + +static void handleMotionNotify(XEvent *event) +{ + if (wPreferences.scrollable_menus || wPreferences.hot_corners) { + WScreen *scr = wScreenForRootWindow(event->xmotion.root); WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root); WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p)); - if (scr->flags.jump_back_pending || - p.x <= (rect.pos.x + 1) || - p.x >= (rect.pos.x + rect.size.width - 2) || - p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) { - WMenu *menu; + if (wPreferences.hot_corners) { + if (!scr->flags.in_hot_corner) { + scr->flags.in_hot_corner = get_corner(rect, p); + if (scr->flags.in_hot_corner && !scr->hot_corner_timer) + scr->hot_corner_timer = WMAddTimerHandler(wPreferences.hot_corner_delay, hotCornerDelay, scr); + } else { + int out_hot_corner = 0; - menu = wMenuUnderPointer(scr); - if (menu != NULL) - wMenuScroll(menu); + switch (scr->flags.in_hot_corner) { + case CORNER_TOPLEFT: + if ((p.x > (rect.pos.x + wPreferences.hot_corner_edge)) || + (p.y > (rect.pos.y + wPreferences.hot_corner_edge))) + out_hot_corner = 1; + break; + case CORNER_TOPRIGHT: + if ((p.x < (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge)) || + (p.y > (rect.pos.y + wPreferences.hot_corner_edge))) + out_hot_corner = 1; + break; + case CORNER_BOTTOMLEFT: + if ((p.x > (rect.pos.x + wPreferences.hot_corner_edge)) || + (p.y < (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge))) + out_hot_corner = 1; + break; + case CORNER_BOTTOMRIGHT: + if ((p.x < (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge)) || + (p.y < (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge))) + out_hot_corner = 1; + break; + + } + if (out_hot_corner) { + scr->flags.in_hot_corner = CORNER_NONE; + if (scr->hot_corner_timer) { + WMDeleteTimerHandler(scr->hot_corner_timer); + scr->hot_corner_timer = NULL; + } + } + + } + } + + if (wPreferences.scrollable_menus) { + if (scr->flags.jump_back_pending || + p.x <= (rect.pos.x + 1) || + p.x >= (rect.pos.x + rect.size.width - 2) || + p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) { + WMenu *menu; + + menu = wMenuUnderPointer(scr); + if (menu != NULL) + wMenuScroll(menu); + } } } } +#undef CORNER_NONE +#undef CORNER_TOPLEFT +#undef CORNER_TOPRIGHT +#undef CORNER_BOTTOMLEFT +#undef CORNER_BOTTOMRIGHT + static void handleVisibilityNotify(XEvent * event) { WWindow *wwin; diff --git a/src/screen.h b/src/screen.h index 6b2a5603..20e14a8c 100644 --- a/src/screen.h +++ b/src/screen.h @@ -295,6 +295,8 @@ typedef struct _WScreen { WMHandlerID *autoRaiseTimer; Window autoRaiseWindow; /* window that is scheduled to be * raised */ + /* for hot-corners delay */ + WMHandlerID *hot_corner_timer; /* for window shortcuts */ WMArray *shortcutWindows[MAX_WINDOW_SHORTCUTS]; @@ -323,6 +325,7 @@ typedef struct _WScreen { unsigned int doing_alt_tab:1; unsigned int jump_back_pending:1; unsigned int ignore_focus_events:1; + unsigned int in_hot_corner:3; } flags; } WScreen;