This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 1.2.1 instead.
Reason given by maintainers: broken on Python 3.12/3.13 (SyntaxError on import)
Gnome Window Controller
Control GNOME window focus.
Focus changing between monitors, windows, and apps, includes multiple options
This extension uses Python, and intends to be used by mapping shortcuts or terminal commands.
Designed to use with Wayland window system protocol and GNOME desktop environment.
This extension adds terminal commands and extra features to existing extensions, but is designed to use a python backend, therefore requiring the python part install. If your usage is directional focus changing (left/right/up/down), there is the standalone gnome extension Focus Control, give it a look.
5.1 Queries
5.2 Focus
5.3 Focus Highlight
5.4 Colored Output
Features
-
Terminal commands support for every command/feature
-
Directional focus changing between monitors (left/right/up/down)
-
Highlight border uppon focusing, allowing configuration (this extension focus|any|disabled)
-
Given app name focusing
-
Same monitor app focus changing
-
Same app focus changing
-
Excluding given app name from focus changing
-
Flash highlight border on currently focused window
Requirements
-
GNOME Shell 45+ (Tested on 50.4, Wayland)
-
gnome-window-controller@eduardotcampos.hotmail.comextension (see Install) -
glib-compile-schemas(glib2-devel) -
Python 3.11+ (
requires-python = ">=3.11").
[!NOTE]
PyGObjectis also a dependency, but is normally supplied by the distribution, case not, the better option is to install it from distro packages (dnf, apt, etc.) rather than letting pip to compile it.
Install
Python Package
python -m pip install gnome-window-controller
or from a checkout:
git clone https://github.com/eduardotlc/gnome_window_controller
cd gnome_window_controller
python -m pip install .
Highlight Extension
gnome-window-controller --highlight install
Log out and back in afterwards. GNOME Shell only scans for extensions at startup, and Wayland cannot restart the shell in place.
run/map the command either by
python -m gnome_window_controller [command] [flags]
or
gnome-window-controller [command] [flags]
[!NOTE] The Python package and the GNOME Shell extension are installed separately — the extension has to live under
~/.local/share/gnome-shell/extensions/
[!NOTE] python pip install gets the
gnome-window-controllercommand in~/.local/bin:
Shell Completions
Completion files for both shells ship inside the package, under
src/gnome_window_controller/completions/ in a checkout, and beside the installed package
otherwise.
Bash
mkdir -p ~/.local/share/bash-completion/completions
cp src/gnome_window_controller/completions/gnome-window-controller.bash \
~/.local/share/bash-completion/completions/gnome-window-controller
The target file has to be named after the command — that is how bash-completion finds and
loads it on first use.
zsh
mkdir -p ~/.local/share/zsh/site-functions
cp src/gnome_window_controller/completions/_gnome-window-controller \
~/.local/share/zsh/site-functions/
and, in ~/.zshrc before compinit runs:
fpath=(~/.local/share/zsh/site-functions $fpath)
Both complete every flag, and the values each flag takes: --chfocus offers monitor, win,
right, left, up, down and last, then narrows the second word to whatever the first one
allows.
--focus and --exclude complete against the wm_class of the windows currently open.
[!NOTE] Installed with pip instead of from a clone? The files are next to the installed package:
python -c 'import gnome_window_controller as m, pathlib as p;\ print(p.Path(m.__file__).parent / "completions")'
Shortcuts
Settings → Keyboard → View and Customize Shortcuts → Custom Shortcuts.
Then map commands like:
Focus right:
/home/eduardotc/.local/bin/gnome-window-controller --chfocus right
Focus left:
/home/eduardotc/.local/bin/gnome-window-controller --chfocus left
[!IMPORTANT] gnome-shell's
PATHdoes not include~/.local/bin, so give the absolute path (substitute/home/eduardotcabove with your user home)
Back up and restore all custom shortcuts with:
dconf dump /org/gnome/settings-daemon/plugins/media-keys/ > keybindings.conf
dconf load /org/gnome/settings-daemon/plugins/media-keys/ < keybindings.conf
Example Keybindings
| Keys | Command |
|---|---|
Super + L / Super + H |
--chfocus right / --chfocus left |
Super + J / Super + K |
--chfocus win same_monitor / --chfocus win same_monitor_up |
Super + R |
--chfocus last |
Super + C |
--chfocus monitor top |
Super + X |
--chfocus win same_app |
Super + I |
--show-focus — outline the focused window |
Usage
Running with no arguments or -h/--help prints the full help
gnome-window-controller
Queries
gnome-window-controller --list-windows # listing
gnome-window-controller --list-windows --json # machine readable
gnome-window-controller --details-windows # full Details(id) per window
gnome-window-controller --list-monitors # rectangles, order and neighbors
gnome-window-controller --list-monitors --json # machine readable
Focus
--focus
--focus <name>
--focus <name> --scope [other-monitor|current-monitor|any]
gnome-window-controller --focus kitty
gnome-window-controller --focus kitty --scope any
--scope picks where --focus searches: other-monitor (default), current-monitor, or any.
--exclude
--exclude <name> [<name> ...]
gnome-window-controller --chfocus win --exclude floorp
gnome-window-controller --chfocus win --exclude floorp --exclude Slack
Windows whose wm_class, instance or title contains a given name are never focused — the focus
command steps over them and takes the next candidate instead. Matching is a case-insensitive
substring, the same rule --focus uses, so --exclude floorp covers every Floorp window and
--exclude Picture-in-Picture covers one window of an app without touching its siblings.
It applies to --focus and to every --chfocus command. Listings are untouched: --list-windows
still shows excluded windows, and so does the "which monitor am I on" lookup, which is what keeps
things correct while an excluded app happens to hold focus.
Picture-in-Picture is excluded out of the box — a floating video overlay is virtually never what
a focus shortcut is reaching for — and --exclude adds to that list rather than replacing it. To
start from nothing, use the Python API:
GnomeWindowController(exclude_apps=()) # nothing excluded
GnomeWindowController(exclude_apps=("floorp", "Slack")) # exactly these
--chfocus
--chfocus <command> [option]
| command | option | effect |
|---|---|---|
monitor |
top (default) / bottom |
focus the top- or bottom-most window of another monitor |
win |
same_app |
next window of the focused app |
win |
same_monitor, same_monitor_down |
next window on this monitor |
win |
same_monitor_up (or none) |
previous window on this monitor |
right / left |
— | move focus one monitor right/left, wrapping |
up / down |
— | move focus one monitor up/down, wrapping |
last |
— | focus the previously focused window |
Every one of them honours --exclude.
Monitor layouts
The four directions follow the actual monitor rectangles read from
org.gnome.Mutter.DisplayConfig, so any arrangement works — a row, a vertical stack, an L, a
grid, mixed resolutions, mixed scales, a screen rotated onto its side. Nothing is hard-coded to a
particular desk, and Mutter's monitor indices are used as they come: they follow no geometric
order of their own.
A direction with no monitor that way leaves focus alone and exits 1, rather than jumping
somewhere arbitrary — on a single row, up means nothing. At the end of a row or column the
search wraps, which is what makes repeated --chfocus right cycle. Where a direction has no
aligned monitor but does have an off-axis one, it goes there instead, so no screen on an L-shaped
desk is unreachable.
--list-monitors prints exactly what the directions will follow:
left-to-right order: [2, 1, 0]
[2] HDMI-1 1920x1080 @ 0,0 scale 1.0
[1] DP-1 2560x1440 @ 1920,0 scale 1.0 (primary) <
[0] DP-2 1920x1080 @ 4480,0 scale 1.0
neighbors (what --chfocus <direction> follows, wrapping at the edges):
from left right up down
2 0 1 - -
1 2 0 - -
0 1 2 - -
Focus Highlight
gnome-window-controller --highlight # status + current options
gnome-window-controller --highlight on
gnome-window-controller --highlight off
gnome-window-controller --highlight flash # pulse the focused window now
gnome-window-controller --show-focus # outline the focused window now
gnome-window-controller --highlight clear # remove the border
gnome-window-controller --highlight uninstall
Options
gnome-window-controller --highlight on \
--highlight-color '#fb4934' \
--highlight-width 4 \
--highlight-radius 8 \
--highlight-duration 900
| Flag | Default | Meaning |
|---|---|---|
--highlight-color |
#993c5a |
any CSS color St accepts (#rgb, red, rgba(...)) |
--highlight-width |
3 |
border thickness, px |
--highlight-radius |
12 |
corner radius, px |
--highlight-inset |
2 |
how far outside the frame the border sits, px |
--highlight-duration |
0 |
ms to stay visible; 0 keeps it until focus moves |
--highlight-mode |
always |
always, commands or off (see above) |
[!NOTE] Any
--highlight-*flag applies on its own — no--highlight ACTIONneeded.
Settings persist to ~/.config/gnome-window-controller/highlight.json and are reloaded when the
shell restarts.
Every window focused by this CLI is also flashed automatically. Pass --no-highlight to suppress
that for a single invocation. When the extension is not installed the flash is skipped silently
(~0.3 ms), so nothing else breaks.
Colored Output
Colors printing can be turned off with:
gnome-window-controller --list-windows --color never # plain text
gnome-window-controller --list-windows --color always # color even into a pipe
gnome-window-controller --list-windows # auto: color only a real terminal
GWC_COLORS=False gnome-window-controller --list-windows # same, from the environment
Acknowledgments
The gnome extensions from which this one wouldn't exist, huge thanks to:
TODO
-
Bundle PyPI package
-
Upload gnome-extension
-
Create documentation page
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gnome_window_controller-1.2.0.tar.gz.
File metadata
- Download URL: gnome_window_controller-1.2.0.tar.gz
- Upload date:
- Size: 73.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8194e1e5153a69f03cda048df0c28228c7cc9f48fa526172e2376f181a310da
|
|
| MD5 |
72e6776c2cdeb758c6bd4ac2357c3a27
|
|
| BLAKE2b-256 |
2a611be663a4195e2d7cd8bf4341e561f69fd5f4047a127ce8f1b46fd3c036d6
|
File details
Details for the file gnome_window_controller-1.2.0-py3-none-any.whl.
File metadata
- Download URL: gnome_window_controller-1.2.0-py3-none-any.whl
- Upload date:
- Size: 60.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66f00a791439261a6c4e11120eb34658400117b8d17a0496ded33eac49fdea12
|
|
| MD5 |
c22dc04240a0c157eeaea6727ed89b1a
|
|
| BLAKE2b-256 |
1c916eaf8fe1d2dea0300b4c2eb87430c902e4ae0e68805078897b0de93b45c9
|