1
0
mirror of https://github.com/gryf/wmaker.git synced 2026-04-05 14:53:34 +02:00

wmaker: add directional window focus

This patch is adding directional window focus
(like in openbox), where the focus can be passed to
the current window neighboors with a shortcut key.
Comparisons are performed using window centres.

New options are FocusWindowLeftKey, FocusWindowRightKey,
FocusWindowUpKey, FocusWindowDownKey.
Other WM are setting those to SUPER+LEFT/RIGHT/UP/DOWN keys
but currently those are not set by default.

CirculateRaise option which is described in WPrefs as
'Raise window when switching focus with keyboard' is also
used to raise/not raise windows that are fully covered by
other windows.
This commit is contained in:
David Maciejak
2026-04-01 20:06:40 -04:00
committed by Carlos R. Mafra
parent a3c02a22bd
commit 33fba87ed1
8 changed files with 159 additions and 0 deletions

View File

@@ -103,6 +103,11 @@ static struct keyOption {
{ "SelectKey", N_("Select active window") }, { "SelectKey", N_("Select active window") },
{ "FocusNextKey", N_("Focus next window") }, { "FocusNextKey", N_("Focus next window") },
{ "FocusPrevKey", N_("Focus previous window") }, { "FocusPrevKey", N_("Focus previous window") },
/* Directional window focus */
{ "FocusWindowLeftKey", N_("Focus the window to the left") },
{ "FocusWindowRightKey", N_("Focus the window to the right") },
{ "FocusWindowUpKey", N_("Focus the window above") },
{ "FocusWindowDownKey", N_("Focus the window below") },
{ "GroupNextKey", N_("Focus next group window") }, { "GroupNextKey", N_("Focus next group window") },
{ "GroupPrevKey", N_("Focus previous group window") }, { "GroupPrevKey", N_("Focus previous group window") },

View File

@@ -240,6 +240,100 @@ void wSetFocusTo(WScreen *scr, WWindow *wwin)
old_scr = scr; old_scr = scr;
} }
/*
*----------------------------------------------------------------------
* wSetFocusToDirection--
* Moves focus to the nearest window in the given cardinal
* direction (DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_UP,
* DIRECTION_DOWN from xinerama.h).
*
* Selection algorithm: candidate windows are scored by
* (primary_distance + perpendicular_offset). A large penalty is
* added when the perpendicular offset exceeds the primary distance
* (i.e. the candidate is more than 45 degrees off-axis). The window
* with the lowest score wins. If no candidate exists in the requested
* direction the call is silently ignored.
*----------------------------------------------------------------------
*/
void wSetFocusToDirection(WScreen *scr, int direction)
{
WWindow *focused = scr->focused_window;
WWindow *best = NULL;
WWindow *candidate = NULL;
int my_cx, my_cy;
long best_score = -1;
if (!focused || !focused->flags.mapped)
return;
/* centre of the focused window */
my_cx = focused->frame_x + (int)focused->frame->core->width / 2;
my_cy = focused->frame_y + (int)focused->frame->core->height / 2;
/* Iterate from most-recently-focused to least-recently-focused */
for (candidate = focused->prev; candidate != NULL; candidate = candidate->prev) {
int his_cx, his_cy, distance, offset;
long score;
if (!candidate->flags.mapped)
continue;
if (WFLAGP(candidate, no_focusable))
continue;
if (!candidate->frame || candidate->frame->workspace != scr->current_workspace)
continue;
/* ignore fully covered windows if cannot raised them */
if (!wPreferences.circ_raise && wWindowIsFullyCovered(candidate))
continue;
/* relative centre of candidate */
his_cx = (candidate->frame_x - my_cx) + (int)candidate->frame->core->width / 2;
his_cy = (candidate->frame_y - my_cy) + (int)candidate->frame->core->height / 2;
switch (direction) {
case DIRECTION_RIGHT:
distance = his_cx;
offset = his_cy < 0 ? -his_cy : his_cy;
break;
case DIRECTION_LEFT:
distance = -his_cx;
offset = his_cy < 0 ? -his_cy : his_cy;
break;
case DIRECTION_DOWN:
distance = his_cy;
offset = his_cx < 0 ? -his_cx : his_cx;
break;
case DIRECTION_UP:
distance = -his_cy;
offset = his_cx < 0 ? -his_cx : his_cx;
break;
default:
continue;
}
/* candidate must be strictly in the requested direction */
if (distance <= 0)
continue;
score = distance + offset;
/* heavy penalty for windows more than 45 degrees off-axis */
if (offset > distance)
score += 1000000L;
if (best_score < 0 || score < best_score) {
best = candidate;
best_score = score;
}
}
if (best) {
if (wPreferences.circ_raise)
wRaiseFrame(best->frame->core);
wSetFocusTo(scr, best);
}
}
void wShadeWindow(WWindow *wwin) void wShadeWindow(WWindow *wwin)
{ {

View File

@@ -41,6 +41,7 @@
#define SAVE_GEOMETRY_ALL SAVE_GEOMETRY_X | SAVE_GEOMETRY_Y | SAVE_GEOMETRY_WIDTH | SAVE_GEOMETRY_HEIGHT #define SAVE_GEOMETRY_ALL SAVE_GEOMETRY_X | SAVE_GEOMETRY_Y | SAVE_GEOMETRY_WIDTH | SAVE_GEOMETRY_HEIGHT
void wSetFocusTo(WScreen *scr, WWindow *wwin); void wSetFocusTo(WScreen *scr, WWindow *wwin);
void wSetFocusToDirection(WScreen *scr, int direction);
int wMouseMoveWindow(WWindow *wwin, XEvent *ev); int wMouseMoveWindow(WWindow *wwin, XEvent *ev);
int wKeyboardMoveResizeWindow(WWindow *wwin); int wKeyboardMoveResizeWindow(WWindow *wwin);

View File

@@ -721,6 +721,15 @@ WDefaultEntry optionList[] = {
NULL, getKeybind, setKeyGrab, NULL, NULL}, NULL, getKeybind, setKeyGrab, NULL, NULL},
{"FocusPrevKey", "Mod1+Shift+Tab", (void *)WKBD_FOCUSPREV, {"FocusPrevKey", "Mod1+Shift+Tab", (void *)WKBD_FOCUSPREV,
NULL, getKeybind, setKeyGrab, NULL, NULL}, NULL, getKeybind, setKeyGrab, NULL, NULL},
/* Directional window focus */
{"FocusWindowLeftKey", "None", (void *)WKBD_FOCUSLEFT,
NULL, getKeybind, setKeyGrab, NULL, NULL},
{"FocusWindowRightKey", "None", (void *)WKBD_FOCUSRIGHT,
NULL, getKeybind, setKeyGrab, NULL, NULL},
{"FocusWindowUpKey", "None", (void *)WKBD_FOCUSUP,
NULL, getKeybind, setKeyGrab, NULL, NULL},
{"FocusWindowDownKey", "None", (void *)WKBD_FOCUSDOWN,
NULL, getKeybind, setKeyGrab, NULL, NULL},
{"GroupNextKey", "None", (void *)WKBD_GROUPNEXT, {"GroupNextKey", "None", (void *)WKBD_GROUPNEXT,
NULL, getKeybind, setKeyGrab, NULL, NULL}, NULL, getKeybind, setKeyGrab, NULL, NULL},
{"GroupPrevKey", "None", (void *)WKBD_GROUPPREV, {"GroupPrevKey", "None", (void *)WKBD_GROUPPREV,

View File

@@ -1732,6 +1732,22 @@ static void dispatchWKBDCommand(int command, WScreen *scr, WWindow *wwin, XEvent
StartWindozeCycle(wwin, event, False, False); StartWindozeCycle(wwin, event, False, False);
break; break;
case WKBD_FOCUSLEFT:
wSetFocusToDirection(scr, DIRECTION_LEFT);
break;
case WKBD_FOCUSRIGHT:
wSetFocusToDirection(scr, DIRECTION_RIGHT);
break;
case WKBD_FOCUSUP:
wSetFocusToDirection(scr, DIRECTION_UP);
break;
case WKBD_FOCUSDOWN:
wSetFocusToDirection(scr, DIRECTION_DOWN);
break;
case WKBD_GROUPNEXT: case WKBD_GROUPNEXT:
StartWindozeCycle(wwin, event, True, True); StartWindozeCycle(wwin, event, True, True);
break; break;

View File

@@ -77,6 +77,10 @@ enum {
WKBD_WORKSPACEMAP, WKBD_WORKSPACEMAP,
WKBD_FOCUSNEXT, WKBD_FOCUSNEXT,
WKBD_FOCUSPREV, WKBD_FOCUSPREV,
WKBD_FOCUSLEFT,
WKBD_FOCUSRIGHT,
WKBD_FOCUSUP,
WKBD_FOCUSDOWN,
WKBD_GROUPNEXT, WKBD_GROUPNEXT,
WKBD_GROUPPREV, WKBD_GROUPPREV,
WKBD_CENTRAL, WKBD_CENTRAL,

View File

@@ -470,6 +470,35 @@ void wWindowSetupInitialAttributes(WWindow *wwin, int *level, int *workspace)
wwin->client_flags.no_focusable = 1; wwin->client_flags.no_focusable = 1;
} }
/*
* Returns True if every pixel of 'wwin' is covered by at least one other
* mapped window on the same workspace, making it invisible to the user
*/
Bool wWindowIsFullyCovered(WWindow *wwin)
{
WScreen *scr = wwin->screen_ptr;
int cx = wwin->frame_x;
int cy = wwin->frame_y;
int cright = cx + (int)wwin->frame->core->width;
int cbottom = cy + (int)wwin->frame->core->height;
WWindow *w;
for (w = scr->focused_window; w != NULL; w = w->prev) {
if (w == wwin)
continue;
if (!w->flags.mapped)
continue;
if (!w->frame || w->frame->workspace != scr->current_workspace)
continue;
if (w->frame_x <= cx &&
w->frame_y <= cy &&
w->frame_x + (int)w->frame->core->width >= cright &&
w->frame_y + (int)w->frame->core->height >= cbottom)
return True;
}
return False;
}
Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured) Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured)
{ {
int w1, h1, w2, h2; int w1, h1, w2, h2;

View File

@@ -405,6 +405,7 @@ WMagicNumber wWindowGetSavedState(Window win);
void wWindowDeleteSavedState(WMagicNumber id); void wWindowDeleteSavedState(WMagicNumber id);
Bool wWindowIsFullyCovered(WWindow *wwin);
Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured); Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured);
void wWindowSetOmnipresent(WWindow *wwin, Bool flag); void wWindowSetOmnipresent(WWindow *wwin, Bool flag);