_ _
__ _(_)_ __ _ __ ___ _ __ ___ ___ _ __ ___ | |__ ___ _ __
\ \ /\ / / | '_ \ _____| '__/ _ \ '_ ` _ \ / _ \ '_ ` _ \| '_ \ / _ \ '__|
\ V V /| | | | |_____| | | __/ | | | | | __/ | | | | | |_) | __/ |
\_/\_/ |_|_| |_| |_| \___|_| |_| |_|\___|_| |_| |_|_.__/ \___|_|
Every star counts! Consider it! ⭐
Per-application window position & size memory for X11/XFCE — reopen every app right where you left it.
- Overview
- Quick start
- Usage
- Directory structure
- How win-remember works
- Customization
- Limitations
- Contributing
- Acknowledgements
- Meta
win-remember is a small background helper that remembers, per application, the exact window position, size, and maximized state you last used, and restores it the next time that application opens. It fills a real gap: xfwm4 (the XFCE window manager) places new windows by a fixed policy (e.g. centered) and has no per-app memory of where you like each app — so everything reopens "tidy" in the middle of the screen instead of where you put it.
Highlights
- Remembers each app's outer window geometry (
x,y,width,height) and maximized state when its window closes; restores it on the next open - Identifies apps by
WM_CLASS(per-application); ignores dialogs, popups, and transient windows - One-command install via
make install(symlinks onto yourPATH+ wires up XDG autostart);git pullupdates the running tool instantly - Pure Python on
python-xlib, speaking EWMH directly (_NET_WM_MOVERESIZE_WINDOW); no compiled C, no runtime to install statusandresetcommands, plusmake uninstall
You don't need to
- change your window manager or its placement policy — win-remember works alongside whatever xfwm4 is doing
- launch anything by hand — the XDG autostart entry runs it at login
- reposition apps on every launch — arrange them once, close them once, done
git clone https://github.com/giuliocsr/win-remember
cd win-remember
make installmake install installs the python3-xlib dependency for you — it auto-detects your package manager (apt / dnf / pacman / zypper) and may ask for your sudo password — then symlinks the command onto your PATH and wires up login autostart.
Then open your apps, arrange them once, and close them. From then on, each app reopens exactly where you left it. Run win-remember status to see what's been remembered.
win-remember # run the daemon (normally started at login by the autostart entry)
win-remember status # list remembered apps and their saved geometry
win-remember reset # forget all remembered positions
make uninstall # remove the command and the autostart entry (keeps your state/ignore)win-remember: The helper — a single-file Python daemonwin-remember.desktop: XDG autostart entry template (path is patched in on install)ignore: Default ignore list — apps win-remember should not manageMakefile:install(symlink onto PATH + autostart) anduninstall.github/workflows/: CI lint workflow
This section is for the curious. You don't need any of it to use the tool.
The moving parts. win-remember is a single Python script. It opens the X display, reads the EWMH client list (_NET_CLIENT_LIST), and polls it a couple of times per second. That is the whole loop — no X server grabs, no fragile event plumbing.
Recording geometry. On each scan it reads every managed top-level window's WM_CLASS (to know which app it is), its outer geometry (the WM frame's position and size, in root coordinates), and whether it is maximized (_NET_WM_STATE_MAXIMIZED_*). When a window leaves the list — i.e. you closed it — its last-seen geometry is written to ~/.config/win-remember/state.json, keyed by application class. Live geometry is also saved continuously (debounced), so logging out without explicitly closing apps is still captured.
Restoring geometry. When a brand-new window appears for a class we have a record of, win-remember sends an EWMH _NET_WM_MOVERESIZE_WINDOW client message (for position/size) or a _NET_WM_STATE message (to re-maximize) to the root window, and the window manager — which owns the decorations — carries it out. The geometry is applied once on detection and again one scan later, so apps that reposition themselves shortly after mapping still end up in the right place.
Skipped windows. Transient windows (those with WM_TRANSIENT_FOR) and _NET_WM_WINDOW_TYPE_DIALOG windows are ignored, so only real application windows are managed.
- Tell win-remember to ignore an app by adding its
WM_CLASSto~/.config/win-remember/ignore(one per line). Find the class withxprop | grep WM_CLASS, then click the app's window — it is the second quoted word. - State lives in
~/.config/win-remember/state.json; delete it (or runwin-remember reset) to start fresh. - A runtime log is written to
~/.config/win-remember/win-remember.log.
- X11 only. It will not work on a Wayland session (there is no global window access).
- Per application, not per window: one record per
WM_CLASS. If you keep two windows of the same app in different spots, only the last one closed is remembered. - A just-opened window may briefly flash at the WM's default spot before snapping to its remembered place (poll interval, ~0.5 s).
- It depends on
python3-xlib;make installauto-installs it (apt / dnf / pacman / zypper).
Pull requests and issues are welcome!
win-remember relies on the following:
- python-xlib: Pure-Python binding to the X11 protocol (python-xlib.github.io)
- EWMH /
_NETspecifications: The window-manager hints it speaks (specifications.freedesktop.org/wm-spec/)
giuliocsr
win-remember by giuliocsr is licensed under CC BY-NC-SA 4.0