Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ ENV DEBIAN_FRONTEND=noninteractive
# Use UTF-8 encoding, otherwise swipl throws "Illegal multibyte Sequence" errors
ENV LANG=en_US.UTF-8

# Set user config directory
ENV XDG_CONFIG_HOME=/home/testuser/.config

# Update package list and install required packages
RUN apt-get update && \
apt-get install -y \
Expand Down
19 changes: 6 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,11 @@ LDFLAGS = -shared -lX11 -lXft -lXrandr
SWIFLAGS = -p foreign=$(INSTALLDIR_LIB) \
--goal=main --toplevel=halt --stand_alone=true -O -o plwm -c src/plwm.pl

INSTALLDIR = /usr/local/bin
INSTALLDIR_BIN = /usr/local/bin
INSTALLDIR_LIB = /usr/local/lib
INSTALLDIR_CNF = /etc/plwm
INSTALLDIR_MAN = /usr/local/share/man/man1

ifdef XDG_CONFIG_HOME
CONFIG_PATH = $(XDG_CONFIG_HOME)/plwm
else
CONFIG_PATH = $(HOME)/.config/plwm
endif

#================================== Build =====================================

plwm: plx.so src/*.pl
Expand Down Expand Up @@ -67,18 +62,16 @@ test:
VERSION = ${shell sed -n 's/^version(\([0-9.]\+\))\.$$/\1/p' src/plwm.pl}

install: plwm
install -D --mode=755 plwm $(INSTALLDIR)/plwm
install -D --mode=755 plwm $(INSTALLDIR_BIN)/plwm
install -D --mode=755 plx.so $(INSTALLDIR_LIB)/plx.so
install -D --mode=644 -C --backup=numbered config/config.pl $(INSTALLDIR_CNF)/config.pl
mkdir -p $(INSTALLDIR_MAN)
sed 's/VERSION/$(VERSION)/' < docs/plwm.1 > $(INSTALLDIR_MAN)/plwm.1
chmod 644 $(INSTALLDIR_MAN)/plwm.1

uninstall:
rm -f $(INSTALLDIR)/plwm \
rm -f $(INSTALLDIR_BIN)/plwm \
$(INSTALLDIR_LIB)/plx.so \
$(INSTALLDIR_MAN)/plwm.1

mkconfig:
install -D src/config.pl $(CONFIG_PATH)/config.pl
sed -i 's/module(config/module(runtime_config/' $(CONFIG_PATH)/config.pl
[ -d $(INSTALLDIR_CNF) ] && echo "Note: $(INSTALLDIR_CNF) is kept" || true

56 changes: 34 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,24 +181,16 @@ then you'll have a triple stack layout where your windows will be evenly spread

## Configuration

Configuration is done by modifying [config.pl](src/config.pl) then recompiling with `make && sudo make install`.
`sudo make install` installs the [default configuration](config/config.pl) to `/etc/plwm/config.pl`. This file can be copied to user config directories.

plwm will also attempt reading configuration when it starts from the first file among
plwm attempts reading configuration when it starts from the first file among
- `$XDG_CONFIG_HOME/plwm/config.pl`
- `$HOME/.config/plwm/config.pl`
- `/etc/plwm/config.pl`

if any exists, so users don't have to recompile each time if they don't wish to. Any values read from the runtime config will override the compiled in settings.

A custom path can be specified with the `-c` flag.

**Attention:** the module name in the runtime config must be `runtime_config`!

You can run:

`$ make mkconfig`

which takes `src/config.pl` and generates the runtime config from it. Then keep modifying the latter while leaving the former in the default state. Or you can use the source one as a stable config and the runtime one for experimental overrides...
**Note:** a reinstall will overwrite `/etc/plwm/config.pl`, however a backup is always created if there is any difference.

While cooking your config, you can use the `-C` flag to quickly and easily check its validity.

Expand All @@ -223,7 +215,7 @@ While cooking your config, you can use the `-C` flag to quickly and easily check
| `ws_format` | string with a \~w **or**<br/>string with a \~d followed by a \~w<br>**Default:** "\~w" | Format of empty workspaces on bars (~d = index, ~w = name) |
| `ws_format_occupied` | string with a \~w **or**<br/>string with a \~d followed by a \~w<br>**Default:** "▘\~w" | Format of occupied workspaces on bars |
| `layout_default_overrides` | list of (Monitor, Workspace -> Nmaster, Mfact, Layout)<br>**Default:** [] | Overrides of the 3 values to specific monitors and/or workspaces (explained [here](#layout-overrides)) |
| `bar_class` | the two strings from bar's WM_CLASS,<br/>query with [xprop(1)](https://linux.die.net/man/1/xprop)<br>**Default:** "polybar", "Polybar" | Space will be reserved for matching windows and they cannot be focused, resized, etc. |
| `bar_classes` | list of string pairs from bar's WM_CLASS,<br/>query with [xprop(1)](https://linux.die.net/man/1/xprop)<br>**Default:** ["polybar"-"Polybar"] | Space will be reserved for matching windows and they cannot be focused, resized, etc. |
| `bar_placement` | follow_focus, static<br>**Default:** follow_focus | Determines placement of external bars (explained [here](#layout-overrides)) |
| `fifo_enabled` | true or false<br>**Default:** false | Whether to spawn a command FIFO<br>(explained [here](#scriptability)) |
| `fifo_path` | string<br>**Default:** "/tmp/plwm_fifo" | Path of command FIFO |
Expand All @@ -238,13 +230,31 @@ While cooking your config, you can use the `-C` flag to quickly and easily check
| `rules` | list of (Name, Class, Title -> Monitor, Workspace, Mode)<br>**Default:** [] | Auto place and configure matching windows (explained [here](#rules)) |
| `hooks` | list of (Event -> Action)<br>**Default:** `[start -> writeln("plwm starting"), quit -> writeln("plwm quitting")]` | Run custom logic on certain events (explained [here](#hooks)) |

**Notes**
**Tips**

In `keymaps/1`, the callback predicates can be arbitrary shell commands using `shellcmd/1`, even whole commandlines (some examples are included in the [default config](src/config.pl)).
* You can safely remove any setting from your config file, plwm will use the default value for those.
* In `keymaps/1`, the callback predicates can be arbitrary shell commands using `shellcmd/1`, even whole commandlines (some examples are included in the [default config](config/config.pl)).
* If you wish to stick to default keymaps mostly, with only a few changes and feel redundant to list the whole table in your config, you can simply omit the `keymaps/1` setting and add your changes as a `start` hook like this:

```Prolog
hooks([
start -> (
add(keymaps, super + g -> shellcmd("gcolor2")), % add new
add(keymaps, super + l -> switch_workspace(next)), % overwrite existing
add(keymaps, super + f -> none) % remove existing
)
]).
```

**Changing settings during runtime**

* `set/2` and `add/2` can be used to overwrite or append to existing settings, respectively. You can invoke them via a keymap, the [command fifo](#scriptability) or the [command menu](#menus).
* The whole configuration file can be reloaded by calling `reload_config/0`.
* You can use `dump_settings/1` to dump all current settings to a file.

## External bars

First, you must specify `bar_class/2` based on the WM_CLASS properties of your bars, which you can find out using [xprop(1)](https://linux.die.net/man/1/xprop). Then you can both:
First, you must specify `bar_classes/1` based on the WM_CLASS properties of your bars, which you can find out using [xprop(1)](https://linux.die.net/man/1/xprop). Then you can both:
* manually start/close bars while plwm is already running
* automatically start bars using `hooks/1` and its `start` event in the config

Expand Down Expand Up @@ -275,7 +285,7 @@ alt + b -> shellcmd("pkill -fx 'polybar top' || polybar top"),
alt + shift + b -> shellcmd("pkill -fx 'polybar bot' || polybar bot")
```

**Note:** if you are using polybar, **do not enable** its `override-redirect = true` setting (it can even crash plwm in some cases)! Reasoning: plwm itself handles all bars (anything that matches `bar_class`, not just polybar) the intended way: bars cannot be focused, grabbed, moved or resized; tiling windows will never cover them (but you can drag floating windows above them); fullscreen windows will always cover them.
**Note:** if you are using polybar, **do not enable** its `override-redirect = true` setting (it can even crash plwm in some cases)! Reasoning: plwm itself handles all bars (anything that matches `bar_classes`, not just polybar) the intended way: bars cannot be focused, grabbed, moved or resized; tiling windows will never cover them (but you can drag floating windows above them); fullscreen windows will always cover them.

## Multi-monitor

Expand Down Expand Up @@ -538,9 +548,9 @@ For known problems, see [the Issues with bug labels](https://github.com/Seeker04

First and foremost, if you find any bugs, please [create a GitHub issue](https://github.com/Seeker04/plwm/issues/new), preferably, with all details you can provide. (First, please check if it's not reported already).

If you have a feature request, please do the same.
If you have a feature request or questions, feel free to [open discussions](https://github.com/Seeker04/plwm/discussions).

Any code contribution is also welcome. Especially if it solves some known issue. For brand new ideas, I recommend creating an issue first, so we can discuss it.
Any code contribution is also welcome. Especially if it solves some known issue. For brand new ideas, I recommend creating a discussion first.

Please read the [Development Guide](docs/development_guide.md).

Expand Down Expand Up @@ -570,15 +580,17 @@ export _JAVA_AWT_WM_NONREPARENTING=1
```
to your `.xinitrc` should solve this problem.

**plwm doesn't start! What's going on?**
**My configuration doesn't work!**

Most likely your configuration is faulty. Run `plwm --check`, then you should see the problem.
Run `plwm --check`, then you should see the problem. Consult the [table here](#configuration) to see the proper type for each setting. E.g. make sure you use double quotes for _strings_ and single quotes for _atoms_.

If you don't see a config error, then please report it as an issue. Preferably by attaching any message plwm dumps to stderr or to its logfile with `-l`.
If you don't see any error, then please report it as an issue by attaching your config and any message plwm dumps to stderr or to its logfile with `-l`.

**Something is missing...**

plwm is minimal in the sense that it doesn't try to solve problems outside of a window manager's domain, especially if they are easily served by other programs (see [here](https://en.wikipedia.org/wiki/Unix_philosophy)):
tl;dr plwm is a window manager, not a full-fledged desktop environment.

plwm is minimal in the sense that it doesn't try to solve problems outside of a wm's domain, especially if they are easily served by other programs (see [here](https://en.wikipedia.org/wiki/Unix_philosophy)):

* Don't want a status bar? You're set. Want one (or more)? Here are a few: [polybar](https://polybar.github.io/), [lemonbar](https://github.com/LemonBoy/bar), [xmobar](https://codeberg.org/xmobar/xmobar)
* Want transparent windows or other effects? Use a compositor like [picom](https://wiki.archlinux.org/title/Picom)
Expand Down
45 changes: 26 additions & 19 deletions src/config.pl → config/config.pl
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
% MIT License, Copyright (c) 2023-2025 Barnabás Zahorán, see LICENSE
%
% This is plwm's default configuration installed under /etc
%
% You can revert this file by reinstalling plwm (if it differs, a backup will be kept)
%
% Users can create their own configs under
% $XDG_CONFIG_HOME/plwm/config.pl
% or
% $HOME/.config/plwm/config.pl
% to overrule this file
%
% Note: there is extensive commenting in this file, as well as arbitrary examples
% for easier understanding. Feel free to remove them once you're familiar with the settings

:- module(config, []).

%********************************* Layout ***********************************

default_nmaster(1). % initial number of master windows for master-stack layouts
Expand Down Expand Up @@ -79,10 +87,9 @@
%********************************** Bars ************************************

% Use xprop(1) or a similar tool to find out the WM_CLASS property of your bar
% You may specify only the name: ("name", _) or only the class: (_, "class")
% List multiple bar_class lines below if you use multiple different bars
% You can delete the line, if you don't use any bar
bar_class("polybar", "Polybar").
% You may specify only the name: "name"-_ or only the class: _-"class"
% List multiple bar_classes if you use multiple different bars
bar_classes(["polybar"-"Polybar"]).

% Possible values:
% follow_focus: space will be reserved for bars on all monitors and bars will
Expand Down Expand Up @@ -243,22 +250,22 @@

%%%%% Custom mapping examples %%%%%

% Launch applications
ctrl + shift + space -> shellcmd("alacritty") ,
alt + a -> shellcmd("dmenu_run -l 20 -p run")

% Toggle status bar
alt + b -> shellcmd("pkill polybar || polybar top") ,
%alt + b -> shellcmd("pkill polybar || polybar top") ,
%alt + b -> shellcmd("pkill polybar || (polybar top & polybar bot)") ,
%alt + b -> shellcmd("pkill -fx 'polybar top' || polybar top") ,
%alt + shift + b -> shellcmd("pkill -fx 'polybar bot' || polybar bot") ,

% Launch applications
ctrl + shift + space -> shellcmd("alacritty") ,
alt + a -> shellcmd("dmenu_run -c -l 20") ,

% Special keys (see xf86names.pl for all such keys)
"AudioRaiseVolume" -> shellcmd("pulseaudio-ctl up") ,
"AudioLowerVolume" -> shellcmd("pulseaudio-ctl down") ,
"AudioMute" -> shellcmd("pulseaudio-ctl mute") ,
"MonBrightnessUp" -> shellcmd("xbacklight -inc 1") ,
"MonBrightnessDown" -> shellcmd("xbacklight -dec 1")
%"AudioRaiseVolume" -> shellcmd("pulseaudio-ctl up") ,
%"AudioLowerVolume" -> shellcmd("pulseaudio-ctl down") ,
%"AudioMute" -> shellcmd("pulseaudio-ctl mute") ,
%"MonBrightnessUp" -> shellcmd("xbacklight -inc 1") ,
%"MonBrightnessDown" -> shellcmd("xbacklight -dec 1")
]).


Expand Down Expand Up @@ -288,9 +295,9 @@

rules([
% name class title monitor wspace mode
(_ , _ , exact("gcolor2") -> _ , _ , [center, center, 1/3, 1/3]),
(_ , _ , "Firefox" -> "eDP-1" , 'www' , fullscreen ),
("Bar" , "Baz" , _ -> "HDMI-1" , '1' , [700, 250, _, _] )
% ( _ , _ , exact("gcolor2") -> _ , _ , [center, center, 1/3, 1/3] ),
% ( _ , _ , "Firefox" -> "eDP-1" , 'www' , fullscreen ),
% ( "Bar" , "Baz" , _ -> "HDMI-1" , '1' , [700, 250, _, _] )
]).

% You can find out the name, class and title values of windows using xprop(1):
Expand Down
2 changes: 1 addition & 1 deletion docs/development_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ User interaction is possible by three methods:
| ----------------------------------- | ----------------------------------------- |
| [plwm.pl](../src/plwm.pl) | entry point, event loop and wm logic |
| [animation.pl](../src/animation.pl) | window animations |
| [config.pl](../src/config.pl) | default configuration |
| [fifo.pl](../src/fifo.pl) | command fifo implementation |
| [layout.pl](../src/layout.pl) | layout definitions |
| [menu.pl](../src/menu.pl) | menu commands |
| [setting.pl](../src/setting.pl) | settings related predicates |
| [utils.pl](../src/utils.pl) | common utilities used by multiple modules |
| [xf86names.pl](../src/xf86names.pl) | definitions for special keys |
| [plx.c](../src/plx.c) | libX11 bindings |
Expand Down
6 changes: 3 additions & 3 deletions src/fifo.pl
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

%! setup_fifo() is det
%
% If config:fifo_enabled/1 and config:fifo_path/1 are set, attempts to create
% If fifo_enabled/1 and fifo_path/1 are set, attempts to create
% a named pipe with mkfifo(1).
% If the fifo is created, its path is passed to fifo:process_fifo/1 on a detached thread.
setup_fifo() :-
optcnf_then(fifo_enabled(true), optcnf_then(fifo_path(FifoPath), (
(fifo_enabled(true), fifo_path(FifoPath) ->
catch(delete_file(FifoPath), _, true), % cleanup from previous execution
string_concat("mkfifo ", FifoPath, MkFifoCmd), % no swipl predicate for this
shell(MkFifoCmd, ExitCode),
(ExitCode == 0 ->
thread_create(fifo:process_fifo(FifoPath), _, [detached(true)])
; writeln(user_error, "Could not spawn command fifo!"))
)))
; true)
.

%! process_fifo(++FifoPath:string) is det
Expand Down
Loading