new-tab chord is Alt+Shift+T (prefix-based) and grabbed
Change Ctrl+Shift+T to Alt+Shift+T (Super+Shift+T under --modifier super), matching the other tab-management chords. It is now grabbed: process_key_event returns true so the focused client never sees the chord, instead of passing it through.
This commit is contained in:
parent
e3cba0f7d7
commit
5d840a09dd
3 changed files with 34 additions and 35 deletions
19
README.md
19
README.md
|
|
@ -77,7 +77,7 @@ as children of tabbed-wlroots and connect to its private Wayland socket, so
|
||||||
`DISPLAY` is unset for them and their `WAYLAND_DISPLAY` points at the tabbed
|
`DISPLAY` is unset for them and their `WAYLAND_DISPLAY` points at the tabbed
|
||||||
compositor (clients are launched with the GDK and Qt Wayland backends forced).
|
compositor (clients are launched with the GDK and Qt Wayland backends forced).
|
||||||
|
|
||||||
`Ctrl+Shift+T` opens the terminal named by the `$TERMINAL` environment variable
|
`Alt+Shift+T` opens the terminal named by the `$TERMINAL` environment variable
|
||||||
when it is set (see Key bindings); otherwise it falls back to `alacritty`. The
|
when it is set (see Key bindings); otherwise it falls back to `alacritty`. The
|
||||||
value is used verbatim, so it may carry arguments, e.g.
|
value is used verbatim, so it may carry arguments, e.g.
|
||||||
`TERMINAL="foot -e htop" tabbed-wlroots ...`.
|
`TERMINAL="foot -e htop" tabbed-wlroots ...`.
|
||||||
|
|
@ -107,10 +107,10 @@ startup is always the one from the last argument.
|
||||||
|
|
||||||
If a command never connects — e.g. `sleep 999` — the rest of the command line
|
If a command never connects — e.g. `sleep 999` — the rest of the command line
|
||||||
never starts and the window stays black; the compositor itself keeps running
|
never starts and the window stays black; the compositor itself keeps running
|
||||||
normally (`Ctrl+Shift+T` still works) until you close it.
|
normally (`Alt+Shift+T` still works) until you close it.
|
||||||
|
|
||||||
Later tab openings follow the same no-steal rule: a brand-new tab never yanks
|
Later tab openings follow the same no-steal rule: a brand-new tab never yanks
|
||||||
the active surface on its own. `Ctrl+Shift+T` still focuses its fresh
|
the active surface on its own. `Alt+Shift+T` still focuses its fresh
|
||||||
terminal, and a sub-window of the tab you are currently looking at becomes
|
terminal, and a sub-window of the tab you are currently looking at becomes
|
||||||
active; every other new window or tab just appears in the list, hidden, until
|
active; every other new window or tab just appears in the list, hidden, until
|
||||||
you cycle to it.
|
you cycle to it.
|
||||||
|
|
@ -125,12 +125,13 @@ you cycle to it.
|
||||||
| `Alt+Shift+Up` | Move the active tab earlier in the list |
|
| `Alt+Shift+Up` | Move the active tab earlier in the list |
|
||||||
| `Alt+Shift+Down` | Move the active tab later in the list |
|
| `Alt+Shift+Down` | Move the active tab later in the list |
|
||||||
| `Alt+Delete` | Close the active tab (SIGTERM, SIGKILL fallback) |
|
| `Alt+Delete` | Close the active tab (SIGTERM, SIGKILL fallback) |
|
||||||
| `Ctrl+Shift+T` | Open a new terminal tab next to the current one |
|
| `Alt+Shift+T` | Open a new terminal tab next to the current one |
|
||||||
|
|
||||||
The tab-management prefix is `Alt` by default. Pass `--modifier super` to
|
The tab-management prefix is `Alt` by default. Pass `--modifier super` to
|
||||||
use `Super` (Windows/Meta key, Mod4) instead: `Super` hold shows the
|
use `Super` (Windows/Meta key, Mod4) instead: `Super` hold shows the
|
||||||
overlay, `Super+Up/Down` cycle, `Super+Shift+Up/Down` reorder, and
|
overlay, `Super+Up/Down` cycle, `Super+Shift+Up/Down` reorder,
|
||||||
`Super+Delete` closes. `Ctrl+Shift+T` always uses Ctrl and is unchanged.
|
`Super+Delete` closes, and `Super+Shift+T` opens a new terminal -- the
|
||||||
|
new-tab chord follows the prefix like every other binding.
|
||||||
The `Super` variant is meant for standalone/DRM and labwc-style hosts
|
The `Super` variant is meant for standalone/DRM and labwc-style hosts
|
||||||
(including the Raspberry Pi, where nothing reserves it); under GNOME the
|
(including the Raspberry Pi, where nothing reserves it); under GNOME the
|
||||||
host grabs most `Super` chords before the nested compositor ever sees them
|
host grabs most `Super` chords before the nested compositor ever sees them
|
||||||
|
|
@ -148,7 +149,7 @@ tab's app icon, resolved from its Wayland `app_id` via the desktop
|
||||||
files and icon themes in `XDG_DATA_DIRS` (PNG via libpng, SVG via
|
files and icon themes in `XDG_DATA_DIRS` (PNG via libpng, SVG via
|
||||||
librsvg); tabs without a resolvable icon get a generic placeholder.
|
librsvg); tabs without a resolvable icon get a generic placeholder.
|
||||||
|
|
||||||
`Ctrl+Shift+T` launches a fresh terminal (`$TERMINAL`, default
|
`Alt+Shift+T` launches a fresh terminal (`$TERMINAL`, default
|
||||||
`alacritty`) as a new tab placed immediately to the right of the current tab,
|
`alacritty`) as a new tab placed immediately to the right of the current tab,
|
||||||
and it becomes the active tab once it maps (the one exception to the
|
and it becomes the active tab once it maps (the one exception to the
|
||||||
no-map-steal rule at startup, along with sub-windows of the shown tab).
|
no-map-steal rule at startup, along with sub-windows of the shown tab).
|
||||||
|
|
@ -162,7 +163,7 @@ active tab stays active, it just moves one position earlier or later in the
|
||||||
order. Moving off either end wraps around and the whole list shifts — a
|
order. Moving off either end wraps around and the whole list shifts — a
|
||||||
rightmost tab moved past the end lands first, with every other tab pushed
|
rightmost tab moved past the end lands first, with every other tab pushed
|
||||||
one position, not merely swapped with the first. Holding the chord keeps
|
one position, not merely swapped with the first. Holding the chord keeps
|
||||||
moving it (same synthesized repeat as cycling). A `Ctrl+Shift+T` inside the
|
moving it (same synthesized repeat as cycling). An `Alt+Shift+T` inside the
|
||||||
moved tab opens exactly where the reordered list says it should — immediately
|
moved tab opens exactly where the reordered list says it should — immediately
|
||||||
right of the tab's new position.
|
right of the tab's new position.
|
||||||
|
|
||||||
|
|
@ -207,7 +208,7 @@ pass instead of being drawn into a host window.
|
||||||
- `WAYLAND_DISPLAY` is ignored in standalone mode (it never connects to an
|
- `WAYLAND_DISPLAY` is ignored in standalone mode (it never connects to an
|
||||||
outer compositor), except as part of the requested `WLR_BACKENDS`.
|
outer compositor), except as part of the requested `WLR_BACKENDS`.
|
||||||
- Real keyboard/pointer devices are attached to the tabbed seat directly
|
- Real keyboard/pointer devices are attached to the tabbed seat directly
|
||||||
(native input); the `Ctrl-Shift-T` and `Alt`-overlay hotkeys still work off
|
(native input); the `Alt+Shift+T` and `Alt`-overlay hotkeys still work off
|
||||||
those devices.
|
those devices.
|
||||||
- `Ctrl-Alt-F1..F12` switch virtual terminals while running on the DRM
|
- `Ctrl-Alt-F1..F12` switch virtual terminals while running on the DRM
|
||||||
backend (via libseat/logind); the terminal sees nothing of the chord.
|
backend (via libseat/logind); the terminal sees nothing of the chord.
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ struct tabbed_app {
|
||||||
struct wlr_texture *icon_wlr;
|
struct wlr_texture *icon_wlr;
|
||||||
bool icon_resolved; /* true once a load attempt has been made */
|
bool icon_resolved; /* true once a load attempt has been made */
|
||||||
|
|
||||||
/* Slot created by add_tab() (Ctrl+Shift+T) awaiting its toplevel. Lets
|
/* Slot created by add_tab() (Alt+Shift+T) awaiting its toplevel. Lets
|
||||||
handle_new_toplevel claim exactly this slot instead of an earlier
|
handle_new_toplevel claim exactly this slot instead of an earlier
|
||||||
stale free one (a closed tab's hole), so a new terminal keeps its
|
stale free one (a closed tab's hole), so a new terminal keeps its
|
||||||
"right of the current tab" position. */
|
"right of the current tab" position. */
|
||||||
|
|
@ -137,7 +137,7 @@ struct tabbed_app {
|
||||||
|
|
||||||
/* Where a tab's slot came from. ORIGIN_CLI slots hold the command-line
|
/* Where a tab's slot came from. ORIGIN_CLI slots hold the command-line
|
||||||
commands and are forked one at a time (see maybe_launch_next), so the
|
commands and are forked one at a time (see maybe_launch_next), so the
|
||||||
tab order mirrors the command line. ORIGIN_ADD_TAB is a Ctrl+Shift+T
|
tab order mirrors the command line. ORIGIN_ADD_TAB is an Alt+Shift+T
|
||||||
terminal. ORIGIN_SUB is a second toplevel from an already-connected
|
terminal. ORIGIN_SUB is a second toplevel from an already-connected
|
||||||
client and is glued right of its parent tab. ORIGIN_EXTERNAL is a
|
client and is glued right of its parent tab. ORIGIN_EXTERNAL is a
|
||||||
client we did not launch (e.g. started from our own shell). */
|
client we did not launch (e.g. started from our own shell). */
|
||||||
|
|
@ -306,12 +306,12 @@ static volatile sig_atomic_t needs_redraw;
|
||||||
static bool frame_pending;
|
static bool frame_pending;
|
||||||
static bool shutting_down;
|
static bool shutting_down;
|
||||||
|
|
||||||
/* Terminal launched for the Ctrl+Shift+T "new tab" binding. Defaults to
|
/* Terminal launched for the Alt+Shift+T "new tab" binding. Defaults to
|
||||||
alacritty (sane no-config font size, unlike foot's 8pt); overridable via
|
alacritty (sane no-config font size, unlike foot's 8pt); overridable via
|
||||||
the $TERMINAL environment variable. The value is used verbatim by sh -c,
|
the $TERMINAL environment variable. The value is used verbatim by sh -c,
|
||||||
so it may carry arguments. */
|
so it may carry arguments. */
|
||||||
static const char *term_cmd = "alacritty";
|
static const char *term_cmd = "alacritty";
|
||||||
static bool term_key_held; /* Ctrl+Shift+T currently down (repeat latch) */
|
static bool term_key_held; /* Alt+Shift+T currently down (repeat latch) */
|
||||||
static bool delete_key_held; /* prefix+Delete currently down (repeat latch) */
|
static bool delete_key_held; /* prefix+Delete currently down (repeat latch) */
|
||||||
|
|
||||||
/* The modifier prefix for the tab-management chords: hold = show overlay,
|
/* The modifier prefix for the tab-management chords: hold = show overlay,
|
||||||
|
|
@ -2236,7 +2236,7 @@ static void handle_toplevel_map(struct wl_listener *listener, void *data) {
|
||||||
tabbed_log(TABBED_LOG_DEBUG, "[diag] map app=%d\n", app_index(app));
|
tabbed_log(TABBED_LOG_DEBUG, "[diag] map app=%d\n", app_index(app));
|
||||||
|
|
||||||
/* A new tab never steals focus on its own. The exceptions: a
|
/* A new tab never steals focus on its own. The exceptions: a
|
||||||
Ctrl+Shift+T terminal (add_tab), a sub-window whose parent tab is the
|
Alt+Shift+T terminal (add_tab), a sub-window whose parent tab is the
|
||||||
one currently shown, and the one-shot startup crown of the last
|
one currently shown, and the one-shot startup crown of the last
|
||||||
command-line command (only while nothing is focused yet). Every other
|
command-line command (only while nothing is focused yet). Every other
|
||||||
map stays hidden -- the overlay and cycling reach it, but it does not
|
map stays hidden -- the overlay and cycling reach it, but it does not
|
||||||
|
|
@ -2266,7 +2266,7 @@ static void handle_new_toplevel(struct wl_listener *listener, void *data) {
|
||||||
&cpid, NULL, NULL);
|
&cpid, NULL, NULL);
|
||||||
|
|
||||||
struct tabbed_app *app = NULL;
|
struct tabbed_app *app = NULL;
|
||||||
/* Prefer a slot prepared by add_tab() (Ctrl+Shift+T): its position was
|
/* Prefer a slot prepared by add_tab() (Alt+Shift+T): its position was
|
||||||
chosen to sit right next to the tab the user launched it from, so an
|
chosen to sit right next to the tab the user launched it from, so an
|
||||||
earlier stale free slot (a closed tab's hole) must not steal it. */
|
earlier stale free slot (a closed tab's hole) must not steal it. */
|
||||||
for (int i = 0; i < server.num_apps && !app; i++)
|
for (int i = 0; i < server.num_apps && !app; i++)
|
||||||
|
|
@ -3962,18 +3962,16 @@ static bool process_key_event(uint32_t time, uint32_t key, uint32_t state,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Ctrl+Shift+T opens a new terminal tab ($TERMINAL, default alacritty).
|
/* Alt+Shift+T opens a new terminal tab ($TERMINAL, default alacritty).
|
||||||
The tab is inserted right after the current one and becomes active when
|
The tab is inserted right after the current one and becomes active when
|
||||||
it maps. Grabbed here so the focused client never sees it; a latch
|
it maps. Grabbed so the focused client never sees the chord; a latch
|
||||||
drops host-synthesized repeats so one hold can't open a terminal per
|
drops host-synthesized repeats so one hold can't open a terminal per
|
||||||
repeat. */
|
repeat. */
|
||||||
{
|
{
|
||||||
bool ctrl = (mods_depressed & (1u << 2)) != 0;
|
|
||||||
bool shift = (mods_depressed & (1u << 0)) != 0;
|
|
||||||
bool is_t = server.xkb_state
|
bool is_t = server.xkb_state
|
||||||
? (sym == XKB_KEY_t || sym == XKB_KEY_T)
|
? (sym == XKB_KEY_t || sym == XKB_KEY_T)
|
||||||
: key == 20;
|
: key == 20;
|
||||||
if (ctrl && shift && is_t) {
|
if (alt && shift && is_t) {
|
||||||
kbd_repeat_cancel();
|
kbd_repeat_cancel();
|
||||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||||
if (!term_key_held) {
|
if (!term_key_held) {
|
||||||
|
|
@ -3990,7 +3988,7 @@ static bool process_key_event(uint32_t time, uint32_t key, uint32_t state,
|
||||||
} else {
|
} else {
|
||||||
term_key_held = false;
|
term_key_held = false;
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* any other key activity stops the tab-switch repeat */
|
/* any other key activity stops the tab-switch repeat */
|
||||||
|
|
@ -5158,7 +5156,7 @@ int main(int argc, char *argv[]) {
|
||||||
synth_input_only = v && v[0] && v[0] != '0';
|
synth_input_only = v && v[0] && v[0] != '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ctrl+Shift+T opens a terminal named by $TERMINAL when set, else the
|
/* Alt+Shift+T opens a terminal named by $TERMINAL when set, else the
|
||||||
alacritty default. Every command-line argument is one tab launcher,
|
alacritty default. Every command-line argument is one tab launcher,
|
||||||
except --screen-mode WxH[@Hz], which selects the native output mode and
|
except --screen-mode WxH[@Hz], which selects the native output mode and
|
||||||
is consumed here, and --modifier alt|super, which picks the modifier
|
is consumed here, and --modifier alt|super, which picks the modifier
|
||||||
|
|
@ -5255,7 +5253,7 @@ int main(int argc, char *argv[]) {
|
||||||
int total_apps = napps;
|
int total_apps = napps;
|
||||||
bool default_tab = false;
|
bool default_tab = false;
|
||||||
if (total_apps == 0) {
|
if (total_apps == 0) {
|
||||||
/* No arguments: open one terminal tab, as Ctrl+Shift+T would. */
|
/* No arguments: open one terminal tab, as Alt+Shift+T would. */
|
||||||
default_tab = true;
|
default_tab = true;
|
||||||
total_apps = 1;
|
total_apps = 1;
|
||||||
}
|
}
|
||||||
|
|
@ -5583,8 +5581,8 @@ int main(int argc, char *argv[]) {
|
||||||
tabbed_log(TABBED_LOG_INFO, "tabbed-wlroots: running. "
|
tabbed_log(TABBED_LOG_INFO, "tabbed-wlroots: running. "
|
||||||
"%s+Up/Down switches tabs; %s+Shift+Up/Down reorders; "
|
"%s+Up/Down switches tabs; %s+Shift+Up/Down reorders; "
|
||||||
"%s+Delete closes the active tab; "
|
"%s+Delete closes the active tab; "
|
||||||
"Ctrl+Shift+T opens a terminal (%s).\n",
|
"%s+Shift+T opens a terminal (%s).\n",
|
||||||
prefix, prefix, prefix, term_cmd);
|
prefix, prefix, prefix, prefix, term_cmd);
|
||||||
|
|
||||||
/* Standalone event loop: no host display, so drive the server loop alone.
|
/* Standalone event loop: no host display, so drive the server loop alone.
|
||||||
The native output's frame signal (or the headless 60Hz timer) wakes the
|
The native output's frame signal (or the headless 60Hz timer) wakes the
|
||||||
|
|
|
||||||
|
|
@ -1300,8 +1300,8 @@ def _nested_session(*tabs, extra_args=()):
|
||||||
return Session(proc)
|
return Session(proc)
|
||||||
|
|
||||||
|
|
||||||
def test_ctrl_shift_t_becomes_active(session):
|
def test_alt_shift_t_becomes_active(session):
|
||||||
"""Ctrl+Shift+T's fresh terminal still becomes the active tab on map
|
"""Alt+Shift+T's fresh terminal still becomes the active tab on map
|
||||||
(the add_tab exception to the no-steal rule), while other new tabs never
|
(the add_tab exception to the no-steal rule), while other new tabs never
|
||||||
steal focus."""
|
steal focus."""
|
||||||
old_term = os.environ.get("TERMINAL")
|
old_term = os.environ.get("TERMINAL")
|
||||||
|
|
@ -1309,16 +1309,16 @@ def test_ctrl_shift_t_becomes_active(session):
|
||||||
try:
|
try:
|
||||||
session.wait_any_active()
|
session.wait_any_active()
|
||||||
before = session.active_count()
|
before = session.active_count()
|
||||||
session.chord(CTRL | SHIFT, 20)
|
session.chord(ALT | SHIFT, 20)
|
||||||
deadline = time.monotonic() + 15.0
|
deadline = time.monotonic() + 15.0
|
||||||
while session.active_count() <= before:
|
while session.active_count() <= before:
|
||||||
assert session.proc.poll() is None, \
|
assert session.proc.poll() is None, \
|
||||||
"compositor exited after Ctrl+Shift+T"
|
"compositor exited after Alt+Shift+T"
|
||||||
assert time.monotonic() < deadline, \
|
assert time.monotonic() < deadline, \
|
||||||
"Ctrl+Shift+T's terminal never became active"
|
"Alt+Shift+T's terminal never became active"
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
assert session.active_count() == before + 1, \
|
assert session.active_count() == before + 1, \
|
||||||
"Ctrl+Shift+T should produce exactly one focus transition"
|
"Alt+Shift+T should produce exactly one focus transition"
|
||||||
assert session.proc.poll() is None
|
assert session.proc.poll() is None
|
||||||
finally:
|
finally:
|
||||||
if old_term is None:
|
if old_term is None:
|
||||||
|
|
@ -1470,7 +1470,7 @@ def test_alt_shift_arrows_reorder_and_wrap():
|
||||||
switching the active surface; moving off an end wraps to the other end and
|
switching the active surface; moving off an end wraps to the other end and
|
||||||
the whole list shifts (a rightmost tab moved off the end lands first with
|
the whole list shifts (a rightmost tab moved off the end lands first with
|
||||||
every other tab pushed one place, not swapped into place). The resulting
|
every other tab pushed one place, not swapped into place). The resulting
|
||||||
order is provable behaviorally: a Ctrl+Shift+T terminal claims the slot
|
order is provable behaviorally: an Alt+Shift+T terminal claims the slot
|
||||||
right after the active tab's new position."""
|
right after the active tab's new position."""
|
||||||
if not os.environ.get("WAYLAND_DISPLAY"):
|
if not os.environ.get("WAYLAND_DISPLAY"):
|
||||||
pytest.skip("needs a live host Wayland session (WAYLAND_DISPLAY)")
|
pytest.skip("needs a live host Wayland session (WAYLAND_DISPLAY)")
|
||||||
|
|
@ -1508,17 +1508,17 @@ def test_alt_shift_arrows_reorder_and_wrap():
|
||||||
assert s.active_count() == before, \
|
assert s.active_count() == before, \
|
||||||
"reorder must not emit an active-tab transition"
|
"reorder must not emit an active-tab transition"
|
||||||
|
|
||||||
# With the active tab wrapped to slot 0, a new Ctrl+Shift+T tab
|
# With the active tab wrapped to slot 0, a new Alt+Shift+T tab
|
||||||
# is anchored right after it: the newest claim must be app=1.
|
# is anchored right after it: the newest claim must be app=1.
|
||||||
claims = re.findall(r"\[claim\] app=(\d+) pid=(\d+)", s.read_log())
|
claims = re.findall(r"\[claim\] app=(\d+) pid=(\d+)", s.read_log())
|
||||||
s.chord(CTRL | SHIFT, 20)
|
s.chord(ALT | SHIFT, 20)
|
||||||
deadline = time.monotonic() + 15.0
|
deadline = time.monotonic() + 15.0
|
||||||
while len(re.findall(r"\[claim\] app=(\d+) pid=(\d+)",
|
while len(re.findall(r"\[claim\] app=(\d+) pid=(\d+)",
|
||||||
s.read_log())) <= len(claims):
|
s.read_log())) <= len(claims):
|
||||||
assert s.proc.poll() is None, \
|
assert s.proc.poll() is None, \
|
||||||
"compositor exited after reorder + new tab"
|
"compositor exited after reorder + new tab"
|
||||||
assert time.monotonic() < deadline, \
|
assert time.monotonic() < deadline, \
|
||||||
"Ctrl+Shift+T never claimed a slot"
|
"Alt+Shift+T never claimed a slot"
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
newest = re.findall(r"\[claim\] app=(\d+) pid=(\d+)",
|
newest = re.findall(r"\[claim\] app=(\d+) pid=(\d+)",
|
||||||
s.read_log())[-1]
|
s.read_log())[-1]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue