Compare commits

...

28 commits
main ... 0.7

Author SHA1 Message Date
389d476f5b fix monitor scaling 2024-12-13 05:38:32 -08:00
94a804e3d9 libdrm 2024-12-13 05:34:06 -08:00
aad4181ca7 nix footgun fix 2024-12-13 05:15:08 -08:00
132dc5ad5c fix overrideAttrs 2024-12-13 05:04:43 -08:00
e801bcbdb6 flake overlay 2024-12-13 04:57:04 -08:00
7e2bbdbfaf add flake dependencies 2024-12-13 04:52:16 -08:00
8eb6cc467c monitor rules 2024-12-13 04:48:08 -08:00
7d25c6650d start discord on 2 2024-12-13 04:48:08 -08:00
dc6f1cab59 bar patch 2024-12-13 04:48:08 -08:00
49888cdcbb kitty term 2024-12-13 01:25:09 -08:00
70a90ac7b5 super modkey 2024-12-13 01:25:05 -08:00
8cd12ccb2d remove unused flake stuff 2024-12-12 15:46:00 -08:00
1ea7c6d404 remove flake build artifacts 2024-12-12 15:44:57 -08:00
1957bc6e55 flake 2024-12-12 15:42:46 -08:00
57b7b87596 compile_commands 2024-12-12 15:42:35 -08:00
4c6e81631c shell.fish 2024-12-12 15:42:28 -08:00
Leonardo Hernández Hernández
cfc80c8f44
fix crash when a client is created while all outputs are disabled 2024-08-27 23:07:21 -06:00
Leonardo Hernández Hernández
0e0c97db56
changelog: add new 'unreleased' section 2024-08-27 23:07:13 -06:00
Leonardo Hernández Hernández
74e45c4014
bump version to 0.7 2024-08-06 12:03:27 -06:00
Leonardo Hernández Hernández
464dddc110
fix crash when a virtual pointer is destroyed
Fixes: https://codeberg.org/dwl/dwl/issues/680
(cherry picked from commit a634e3f527)
2024-08-06 12:03:27 -06:00
Leonardo Hernández Hernández
cb01023db6
fix crash when re-mapping a client
Fixes: ab5c554d09
(cherry picked from commit b5abbc37d8)
2024-08-01 22:35:10 -06:00
Lennart Jablonka
5bbb0ab404
dwl(1): use correct special characters for - and '
The hyphen-minus <-> and apostrophe-quote <'> are interpreted by troff
as hyphen and right single quotation mark.  See groff_char(7).

Fixes: 0db6f3c5b5 ("add dwl(1)")
(cherry picked from commit f2c5023a3a)
2024-08-01 22:34:51 -06:00
Lennart Jablonka
ed74960373
add myself to .mailmap
(cherry picked from commit 4bbbb4907e)
2024-08-01 22:34:51 -06:00
A Frederick Christensen
cc622d4dd5
Specify that the 0.7 branch builds against wlroots 0.18 2024-08-01 22:31:06 -06:00
A Frederick Christensen
3256ae0679
README.md Fix links formatting issue after re-flow text to 80 columns
(cherry picked from commit ea6a450121)
2024-08-01 22:29:29 -06:00
A Frederick Christensen
e3f1aab770
Documentation restructuring
Modified documentation to make clear the change in development (main) branch versus releases.

(cherry picked from commit ad30ca910b)
2024-08-01 22:29:29 -06:00
Leonardo Hernández Hernández
57153fd557
update README.md to mention the main branch now requires wlroots-git
Closes: https://codeberg.org/dwl/dwl/issues/646
(cherry picked from commit 452a314faa)
2024-08-01 22:29:29 -06:00
Leonardo Hernández Hernández
a9bcdff683
send scale on initial commit to layer surfaces
Signed-off-by: Leonardo Hernández Hernández <leohdz172@proton.me>
(cherry picked from commit cd216908a7)
2024-07-24 07:03:57 -05:00
14 changed files with 2197 additions and 219 deletions

1
.mailmap Normal file
View file

@ -0,0 +1 @@
Lennart Jablonka <humm@ljabl.com> <hummsmith42@gmail.com>

View file

@ -1,13 +1,27 @@
# Changelog
* [Unreleased](#unreleased)
* [0.7](#0.7)
* [0.6](#0.6)
* [0.5](#0.5)
## Unreleased
### Added
### Changed
### Deprecated
### Removed
### Fixed
* Crash when a client is created while all outputs are disabled.
### Security
### Contributors
## 0.7
This version is just 0.6 with wlroots 0.18 compatibility.
See also [0.6](#0.6) release notes. 0.7 builds against wlroots 0.18.x.
### Added
@ -18,9 +32,15 @@ This version is just 0.6 with wlroots 0.18 compatibility.
[601]: https://codeberg.org/dwl/dwl/issues/601
### Fixed
* Crash when re-mapping unmapped clients.
### Contributors
Guido Cella
Lennart Jablonka
## 0.6

View file

@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement \
-Wfloat-conversion
# CFLAGS / LDFLAGS
PKGS = wlroots-0.18 wayland-server xkbcommon libinput $(XLIBS)
PKGS = wlroots-0.18 wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS)
DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS)
@ -50,6 +50,7 @@ config.h:
cp config.def.h $@
clean:
rm -f dwl *.o *-protocol.h
rm -f result result-man
dist: clean
mkdir -p dwl-$(VERSION)

241
README.md
View file

@ -5,21 +5,134 @@ Or on our [Discord server].
dwl is a compact, hackable compositor for [Wayland] based on [wlroots]. It is
intended to fill the same space in the Wayland world that dwm does in X11,
primarily in terms of functionality, and secondarily in terms of philosophy.
Like dwm, dwl is:
primarily in terms of functionality, and secondarily in terms of
philosophy. Like dwm, dwl is:
- Easy to understand, hack on, and extend with patches
- One C source file (or a very small number) configurable via `config.h`
- Tied to as few external dependencies as possible
## Getting Started:
### **dwl branch 0.7 and releases based upon 0.7 build against [wlroots] 0.18**
### Latest semi-stable [release]
This is probably where you want to start. This builds against the dependent
packages' versions currently shipping in major distributions. If your
distribution's wlroots version is older, use an earlier dwl [release] or [0.x
branch].
### Development branch [main]
Active development progresses on the `main` branch. The `main` branch is built
against a late (and often changing) git commit of wlroots. While the adventurous
are welcome to use `main`, it is a rocky road. Using `main` requires that the
user be willing to chase git commits of wlroots. Testing development pull
requests may involve merging unmerged pull requests in [wlroots]' git repository
and/or git commits of wayland.
### Building dwl
dwl has the following dependencies:
- libinput
- wayland
- wlroots (compiled with the libinput backend)
- xkbcommon
- wayland-protocols (compile-time only)
- pkg-config (compile-time only)
dwl has the following additional dependencies if XWayland support is enabled:
- libxcb
- libxcb-wm
- wlroots (compiled with X11 support)
- Xwayland (runtime only)
Install these (and their `-devel` versions if your distro has separate
development packages) and run `make`. If you wish to build against a released
version of wlroots (*you probably do*), use a [release] or a [0.x branch]. If
you want to use the unstable development `main` branch, you need to use the git
version of [wlroots].
To enable XWayland, you should uncomment its flags in `config.mk`.
## Configuration
All configuration is done by editing `config.h` and recompiling, in the same
manner as dwm. There is no way to separately restart the window manager in
Wayland without restarting the entire display server, so any changes will take
effect the next time dwl is executed.
As in the dwm community, we encourage users to share patches they have
created. Check out the [dwl-patches] repository!
## Running dwl
dwl can be run on any of the backends supported by wlroots. This means you can
run it as a separate window inside either an X11 or Wayland session, as well as
directly from a VT console. Depending on your distro's setup, you may need to
add your user to the `video` and `input` groups before you can run dwl on a
VT. If you are using `elogind` or `systemd-logind` you need to install polkit;
otherwise you need to add yourself in the `seat` group and enable/start the
seatd daemon.
When dwl is run with no arguments, it will launch the server and begin handling
any shortcuts configured in `config.h`. There is no status bar or other
decoration initially; these are instead clients that can be run within the
Wayland session. Do note that the default background color is black. This can be
modified in `config.h`.
If you would like to run a script or command automatically at startup, you can
specify the command using the `-s` option. This command will be executed as a
shell command using `/bin/sh -c`. It serves a similar function to `.xinitrc`,
but differs in that the display server will not shut down when this process
terminates. Instead, dwl will send this process a SIGTERM at shutdown and wait
for it to terminate (if it hasn't already). This makes it ideal for execing into
a user service manager like [s6], [anopa], [runit], [dinit], or [`systemd
--user`].
Note: The `-s` command is run as a *child process* of dwl, which means that it
does not have the ability to affect the environment of dwl or of any processes
that it spawns. If you need to set environment variables that affect the entire
dwl session, these must be set prior to running dwl. For example, Wayland
requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager
such as `elogind` or `systemd-logind`. If your system doesn't do this
automatically, you will need to configure it prior to launching `dwl`, e.g.:
export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u)
mkdir -p $XDG_RUNTIME_DIR
dwl
### Status information
Information about selected layouts, current window title, app-id, and
selected/occupied/urgent tags is written to the stdin of the `-s` command (see
the `printstatus()` function for details). This information can be used to
populate an external status bar with a script that parses the
information. Failing to read this information will cause dwl to block, so if you
do want to run a startup command that does not consume the status information,
you can close standard input with the `<&-` shell redirection, for example:
dwl -s 'foot --server <&-'
If your startup command is a shell script, you can achieve the same inside the
script with the line
exec <&-
To get a list of status bars that work with dwl consult our [wiki].
## Replacements for X applications
You can find a [list of useful resources on our wiki].
## Background
dwl is not meant to provide every feature under the sun. Instead, like dwm, it
sticks to features which are necessary, simple, and straightforward to implement
given the base on which it is built. Implemented default features are:
- Any features provided by dwm/Xlib: simple window borders, tags, keybindings,
client rules, mouse move/resize. Providing a built-in status bar is an
exception to this goal, to avoid dependencies on font rendering and/or
drawing libraries when an external bar could work well.
exception to this goal, to avoid dependencies on font rendering and/or drawing
libraries when an external bar could work well.
- Configurable multi-monitor layout support, including position and rotation
- Configurable HiDPI/multi-DPI support
- Idle-inhibit protocol which lets applications such as mpv disable idle
@ -53,100 +166,6 @@ Feature *non-goals* for the main codebase include:
be done through the compositor
- Animations and visual effects
## Building dwl
dwl has the following dependencies:
```
libinput
wayland
wlroots (compiled with the libinput backend)
xkbcommon
wayland-protocols (compile-time only)
pkg-config (compile-time only)
```
If you enable X11 support:
```
libxcb
libxcb-wm
wlroots (compiled with X11 support)
Xwayland (runtime only)
```
Simply install these (and their `-devel` versions if your distro has separate
development packages) and run `make`. If you wish to build against a Git
version of wlroots, check out the [wlroots-next branch].
To enable XWayland, you should uncomment its flags in `config.mk`.
## Configuration
All configuration is done by editing `config.h` and recompiling, in the same
manner as dwm. There is no way to separately restart the window manager in
Wayland without restarting the entire display server, so any changes will take
effect the next time dwl is executed.
As in the dwm community, we encourage users to share patches they have created.
Check out the dwl [patches repository]!
## Running dwl
dwl can be run on any of the backends supported by wlroots. This means you can
run it as a separate window inside either an X11 or Wayland session, as well
as directly from a VT console. Depending on your distro's setup, you may need
to add your user to the `video` and `input` groups before you can run dwl on
a VT. If you are using `elogind` or `systemd-logind` you need to install
polkit; otherwise you need to add yourself in the `seat` group and
enable/start the seatd daemon.
When dwl is run with no arguments, it will launch the server and begin handling
any shortcuts configured in `config.h`. There is no status bar or other
decoration initially; these are instead clients that can be run within
the Wayland session.
Do note that the background color is black.
If you would like to run a script or command automatically at startup, you can
specify the command using the `-s` option. This command will be executed as a
shell command using `/bin/sh -c`. It serves a similar function to `.xinitrc`,
but differs in that the display server will not shut down when this process
terminates. Instead, dwl will send this process a SIGTERM at shutdown and wait
for it to terminate (if it hasn't already). This makes it ideal for execing into
a user service manager like [s6], [anopa], [runit], [dinit], or [`systemd --user`].
Note: The `-s` command is run as a *child process* of dwl, which means that it
does not have the ability to affect the environment of dwl or of any processes
that it spawns. If you need to set environment variables that affect the entire
dwl session, these must be set prior to running dwl. For example, Wayland
requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager
such as `elogind` or `systemd-logind`. If your system doesn't do this
automatically, you will need to configure it prior to launching `dwl`, e.g.:
export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u)
mkdir -p $XDG_RUNTIME_DIR
dwl
### Status information
Information about selected layouts, current window title, app-id, and
selected/occupied/urgent tags is written to the stdin of the `-s` command (see
the `printstatus()` function for details). This information can be used to
populate an external status bar with a script that parses the information.
Failing to read this information will cause dwl to block, so if you do want to
run a startup command that does not consume the status information, you can
close standard input with the `<&-` shell redirection, for example:
dwl -s 'foot --server <&-'
If your startup command is a shell script, you can achieve the same inside the
script with the line
exec <&-
To get a list of status bars that work with dwl consult our [wiki].
## Replacements for X applications
You can find a [list of useful resources on our wiki].
## Acknowledgements
dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots
@ -164,17 +183,19 @@ inspiration, and to the various contributors to the project, including:
- Stivvo for output management and fullscreen support, and patch maintenance
[Discord server]: https://discord.gg/jJxZnrGPWN
[#dwl on Libera Chat]: https://web.libera.chat/?channels=#dwl
[Wayland]: https://wayland.freedesktop.org/
[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots/
[wlroots-next branch]: https://codeberg.org/dwl/dwl/src/branch/wlroots-next
[patches repository]: https://codeberg.org/dwl/dwl-patches
[s6]: https://skarnet.org/software/s6/
[anopa]: https://jjacky.com/anopa/
[runit]: http://smarden.org/runit/faq.html#userservices
[dinit]: https://davmac.org/projects/dinit/
[`systemd --user`]: https://wiki.archlinux.org/title/Systemd/User
[#dwl on Libera Chat]: https://web.libera.chat/?channels=#dwl
[0.7-rc1]: https://codeberg.org/dwl/dwl/releases/tag/v0.7-rc1
[0.x branch]: https://codeberg.org/dwl/dwl/branches
[anopa]: https://jjacky.com/anopa/
[dinit]: https://davmac.org/projects/dinit/
[dwl-patches]: https://codeberg.org/dwl/dwl-patches
[list of useful resources on our wiki]: https://codeberg.org/dwl/dwl/wiki/Home#migrating-from-x
[main]: https://codeberg.org/dwl/dwl/src/branch/main
[release]: https://codeberg.org/dwl/dwl/releases
[runit]: http://smarden.org/runit/faq.html#userservices
[s6]: https://skarnet.org/software/s6/
[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots/
[wiki]: https://codeberg.org/dwl/dwl/wiki/Home#compatible-status-bars
[list of useful resources on our wiki]:
https://codeberg.org/dwl/dwl/wiki/Home#migrating-from-x
[Discord server]: https://discord.gg/jJxZnrGPWN
[Wayland]: https://wayland.freedesktop.org/

1
compile_commands.json Normal file
View file

@ -0,0 +1 @@
[]

View file

@ -7,15 +7,21 @@
static const int sloppyfocus = 1; /* focus follows mouse */
static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const float rootcolor[] = COLOR(0x222222ff);
static const float bordercolor[] = COLOR(0x444444ff);
static const float focuscolor[] = COLOR(0x005577ff);
static const float urgentcolor[] = COLOR(0xff0000ff);
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = {"monospace:size=10"};
static const float rootcolor[] = COLOR(0x000000ff);
/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
static uint32_t colors[][3] = {
/* fg bg border */
[SchemeNorm] = { 0xbbbbbbff, 0x222222ff, 0x444444ff },
[SchemeSel] = { 0xeeeeeeff, 0x005577ff, 0x005577ff },
[SchemeUrg] = { 0, 0, 0x770000ff },
};
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
/* logging */
static int log_level = WLR_ERROR;
@ -25,7 +31,7 @@ static const Rule rules[] = {
/* app_id title tags mask isfloating monitor */
/* examples: */
{ "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
{ "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
{ "discord", NULL, 1 << 1, 0, 1 }, /* Start on ONLY tag "9" */
};
/* layout(s) */
@ -47,6 +53,8 @@ static const MonitorRule monrules[] = {
/* example of a HiDPI laptop monitor:
{ "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
*/
{ "HDMI-A-1", 0.5f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 },
{ "DP-1", 0.5f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 1920, 0 },
/* defaults */
{ NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
};
@ -107,7 +115,7 @@ LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT
#define MODKEY WLR_MODIFIER_LOGO
#define TAGKEYS(KEY,SKEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
@ -119,7 +127,7 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
static const char *termcmd[] = { "foot", NULL };
static const char *termcmd[] = { "kitty", NULL };
static const char *menucmd[] = { "wmenu-run", NULL };
static const Key keys[] = {
@ -127,6 +135,7 @@ static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
{ MODKEY, XKB_KEY_b, togglebar, {0} },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
{ MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
@ -170,7 +179,15 @@ static const Key keys[] = {
};
static const Button buttons[] = {
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
{ ClkLtSymbol, 0, BTN_LEFT, setlayout, {.v = &layouts[0]} },
{ ClkLtSymbol, 0, BTN_RIGHT, setlayout, {.v = &layouts[2]} },
{ ClkTitle, 0, BTN_MIDDLE, zoom, {0} },
{ ClkStatus, 0, BTN_MIDDLE, spawn, {.v = termcmd} },
{ ClkClient, MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ ClkClient, MODKEY, BTN_MIDDLE, togglefloating, {0} },
{ ClkClient, MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
{ ClkTagBar, 0, BTN_LEFT, view, {0} },
{ ClkTagBar, 0, BTN_RIGHT, toggleview, {0} },
{ ClkTagBar, MODKEY, BTN_LEFT, tag, {0} },
{ ClkTagBar, MODKEY, BTN_RIGHT, toggletag, {0} },
};

View file

@ -1,4 +1,4 @@
_VERSION = 0.7-rc1
_VERSION = 0.7
VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)`
PKG_CONFIG = pkg-config

311
drwl.h Normal file
View file

@ -0,0 +1,311 @@
/*
* drwl - https://codeberg.org/sewn/drwl
*
* Copyright (c) 2023-2024 sewn <sewn@disroot.org>
* Copyright (c) 2024 notchoc <notchoc@disroot.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* The UTF-8 Decoder included is from Bjoern Hoehrmann:
* Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
*/
#pragma once
#include <stdlib.h>
#include <fcft/fcft.h>
#include <pixman-1/pixman.h>
enum { ColFg, ColBg, ColBorder }; /* colorscheme index */
typedef struct fcft_font Fnt;
typedef pixman_image_t Img;
typedef struct {
Img *image;
Fnt *font;
uint32_t *scheme;
} Drwl;
#define UTF8_ACCEPT 0
#define UTF8_REJECT 12
#define UTF8_INVALID 0xFFFD
static const uint8_t utf8d[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
12,36,12,12,12,12,12,12,12,12,12,12,
};
static inline uint32_t
utf8decode(uint32_t *state, uint32_t *codep, uint8_t byte)
{
uint32_t type = utf8d[byte];
*codep = (*state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte);
*state = utf8d[256 + *state + type];
return *state;
}
static int
drwl_init(void)
{
fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3);
return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR);
}
static Drwl *
drwl_create(void)
{
Drwl *drwl;
if (!(drwl = calloc(1, sizeof(Drwl))))
return NULL;
return drwl;
}
static void
drwl_setfont(Drwl *drwl, Fnt *font)
{
if (drwl)
drwl->font = font;
}
static void
drwl_setimage(Drwl *drwl, Img *image)
{
if (drwl)
drwl->image = image;
}
static Fnt *
drwl_font_create(Drwl *drwl, size_t count,
const char *names[static count], const char *attributes)
{
Fnt *font = fcft_from_name(count, names, attributes);
if (drwl)
drwl_setfont(drwl, font);
return font;
}
static void
drwl_font_destroy(Fnt *font)
{
fcft_destroy(font);
}
static inline pixman_color_t
convert_color(uint32_t clr)
{
return (pixman_color_t){
((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
(clr & 0xFF) * 0x101
};
}
static void
drwl_setscheme(Drwl *drwl, uint32_t *scm)
{
if (drwl)
drwl->scheme = scm;
}
static Img *
drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits)
{
Img *image;
pixman_region32_t clip;
image = pixman_image_create_bits_no_clear(
PIXMAN_a8r8g8b8, w, h, bits, w * 4);
if (!image)
return NULL;
pixman_region32_init_rect(&clip, 0, 0, w, h);
pixman_image_set_clip_region32(image, &clip);
pixman_region32_fini(&clip);
if (drwl)
drwl_setimage(drwl, image);
return image;
}
static void
drwl_rect(Drwl *drwl,
int x, int y, unsigned int w, unsigned int h,
int filled, int invert)
{
pixman_color_t clr;
if (!drwl || !drwl->scheme || !drwl->image)
return;
clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
if (filled)
pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1,
&(pixman_rectangle16_t){x, y, w, h});
else
pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 4,
(pixman_rectangle16_t[4]){
{ x, y, w, 1 },
{ x, y + h - 1, w, 1 },
{ x, y, 1, h },
{ x + w - 1, y, 1, h }});
}
static int
drwl_text(Drwl *drwl,
int x, int y, unsigned int w, unsigned int h,
unsigned int lpad, const char *text, int invert)
{
int ty;
int render = x || y || w || h;
long x_kern;
uint32_t cp = 0, last_cp = 0, state;
pixman_color_t clr;
pixman_image_t *fg_pix = NULL;
int noellipsis = 0;
const struct fcft_glyph *glyph, *eg = NULL;
int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT;
if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !text || !drwl->font)
return 0;
if (!render) {
w = invert ? invert : ~invert;
} else {
clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
fg_pix = pixman_image_create_solid_fill(&clr);
drwl_rect(drwl, x, y, w, h, 1, !invert);
x += lpad;
w -= lpad;
}
if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF)
fcft_subpixel_mode = FCFT_SUBPIXEL_NONE;
if (render)
eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode);
for (const char *p = text, *pp; pp = p, *p; p++) {
for (state = UTF8_ACCEPT; *p &&
utf8decode(&state, &cp, *p) > UTF8_REJECT; p++)
;
if (!*p || state == UTF8_REJECT) {
cp = UTF8_INVALID;
if (p > pp)
p--;
}
glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode);
if (!glyph)
continue;
x_kern = 0;
if (last_cp)
fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL);
last_cp = cp;
ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent;
if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w &&
*(p + 1) != '\0') {
/* cannot fit ellipsis after current codepoint */
if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) {
noellipsis = 1;
} else {
w -= eg->advance.x;
pixman_image_composite32(
PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 0, 0, 0, 0,
x + eg->x, ty - eg->y, eg->width, eg->height);
}
}
if ((x_kern + glyph->advance.x) > w)
break;
x += x_kern;
if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)
/* pre-rendered glyphs (eg. emoji) */
pixman_image_composite32(
PIXMAN_OP_OVER, glyph->pix, NULL, drwl->image, 0, 0, 0, 0,
x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
else if (render)
pixman_image_composite32(
PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->image, 0, 0, 0, 0,
x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
x += glyph->advance.x;
w -= glyph->advance.x;
}
if (render)
pixman_image_unref(fg_pix);
return x + (render ? w : 0);
}
static unsigned int
drwl_font_getwidth(Drwl *drwl, const char *text)
{
if (!drwl || !drwl->font || !text)
return 0;
return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0);
}
static void
drwl_image_destroy(Img *image)
{
pixman_image_unref(image);
}
static void
drwl_destroy(Drwl *drwl)
{
if (drwl->font)
drwl_font_destroy(drwl->font);
if (drwl->image)
drwl_image_destroy(drwl->image);
free(drwl);
}
static void
drwl_fini(void)
{
fcft_fini();
}

2
dwl.1
View file

@ -140,7 +140,7 @@ server.
Start
.Nm
with s6 in the background:
.Dl dwl -s 's6-svscan <&-'
.Dl dwl \-s \(aqs6\-svscan <&\-\(aq
.Sh SEE ALSO
.Xr foot 1 ,
.Xr wmenu 1 ,

461
dwl.c
View file

@ -5,6 +5,7 @@
#include <libinput.h>
#include <linux/input-event-codes.h>
#include <math.h>
#include <libdrm/drm_fourcc.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@ -58,6 +59,7 @@
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include <xkbcommon/xkbcommon.h>
@ -68,6 +70,7 @@
#endif
#include "util.h"
#include "drwl.h"
/* macros */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
@ -76,14 +79,17 @@
#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
#define LENGTH(X) (sizeof X / sizeof X[0])
#define END(A) ((A) + LENGTH(A))
#define TAGMASK ((1u << TAGCOUNT) - 1)
#define TAGMASK ((1u << LENGTH(tags)) - 1)
#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
#define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0)
#define TEXTW(mon, text) (drwl_font_getwidth(mon->drw, text) + mon->lrpad)
/* enums */
enum { SchemeNorm, SchemeSel, SchemeUrg }; /* color schemes */
enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
enum { XDGShell, LayerShell, X11 }; /* client types */
enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
enum { ClkTagBar, ClkLtSymbol, ClkStatus, ClkTitle, ClkClient, ClkRoot }; /* clicks */
#ifdef XWAYLAND
enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
@ -97,6 +103,7 @@ typedef union {
} Arg;
typedef struct {
unsigned int click;
unsigned int mod;
unsigned int button;
void (*func)(const Arg *);
@ -186,10 +193,19 @@ typedef struct {
void (*arrange)(Monitor *);
} Layout;
typedef struct {
struct wlr_buffer base;
struct wl_listener release;
bool busy;
Img *image;
uint32_t data[];
} Buffer;
struct Monitor {
struct wl_list link;
struct wlr_output *wlr_output;
struct wlr_scene_output *scene_output;
struct wlr_scene_buffer *scene_buffer; /* bar buffer */
struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
struct wl_listener frame;
struct wl_listener destroy;
@ -197,6 +213,11 @@ struct Monitor {
struct wl_listener destroy_lock_surface;
struct wlr_session_lock_surface_v1 *lock_surface;
struct wlr_box m; /* monitor area, layout-relative */
struct {
int width, height;
int real_width, real_height; /* non-scaled */
float scale;
} b; /* bar area */
struct wlr_box w; /* window area, layout-relative */
struct wl_list layers[4]; /* LayerSurface.link */
const Layout *lt[2];
@ -208,6 +229,9 @@ struct Monitor {
int nmaster;
char ltsymbol[16];
int asleep;
Drwl *drw;
Buffer *pool[2];
int lrpad;
};
typedef struct {
@ -250,6 +274,13 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
struct wlr_box *usable_area, int exclusive);
static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data);
static bool baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy);
static void bufdestroy(struct wlr_buffer *buffer);
static bool bufdatabegin(struct wlr_buffer *buffer, uint32_t flags,
void **data, uint32_t *format, size_t *stride);
static void bufdataend(struct wlr_buffer *buffer);
static Buffer *bufmon(Monitor *m);
static void bufrelease(struct wl_listener *listener, void *data);
static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude);
@ -285,6 +316,8 @@ static void destroysessionlock(struct wl_listener *listener, void *data);
static void destroysessionmgr(struct wl_listener *listener, void *data);
static void destroykeyboardgroup(struct wl_listener *listener, void *data);
static Monitor *dirtomon(enum wlr_direction dir);
static void drawbar(Monitor *m);
static void drawbars(void);
static void focusclient(Client *c, int lift);
static void focusmon(const Arg *arg);
static void focusstack(const Arg *arg);
@ -313,7 +346,6 @@ static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int
static void outputmgrtest(struct wl_listener *listener, void *data);
static void pointerfocus(Client *c, struct wlr_surface *surface,
double sx, double sy, uint32_t time);
static void printstatus(void);
static void powermgrsetmode(struct wl_listener *listener, void *data);
static void quit(const Arg *arg);
static void rendermon(struct wl_listener *listener, void *data);
@ -335,9 +367,11 @@ static void setsel(struct wl_listener *listener, void *data);
static void setup(void);
static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data);
static int statusin(int fd, unsigned int mask, void *data);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *m);
static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
static void toggletag(const Arg *arg);
@ -346,6 +380,7 @@ static void unlocksession(struct wl_listener *listener, void *data);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
static void unmapnotify(struct wl_listener *listener, void *data);
static void updatemons(struct wl_listener *listener, void *data);
static void updatebar(Monitor *m);
static void updatetitle(struct wl_listener *listener, void *data);
static void urgent(struct wl_listener *listener, void *data);
static void view(const Arg *arg);
@ -413,6 +448,15 @@ static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
static char stext[256];
static struct wl_event_source *status_event_source;
static const struct wlr_buffer_impl buffer_impl = {
.destroy = bufdestroy,
.begin_data_ptr_access = bufdatabegin,
.end_data_ptr_access = bufdataend,
};
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data);
@ -553,6 +597,11 @@ arrangelayers(Monitor *m)
if (!m->wlr_output->enabled)
return;
if (m->scene_buffer->node.enabled) {
usable_area.height -= m->b.real_height;
usable_area.y += topbar ? m->b.real_height : 0;
}
/* Arrange exclusive surfaces from top->bottom */
for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 1);
@ -595,17 +644,102 @@ axisnotify(struct wl_listener *listener, void *data)
event->delta_discrete, event->source, event->relative_direction);
}
bool
baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy)
{
return true;
}
void
bufdestroy(struct wlr_buffer *wlr_buffer)
{
Buffer *buf = wl_container_of(wlr_buffer, buf, base);
if (buf->busy)
wl_list_remove(&buf->release.link);
drwl_image_destroy(buf->image);
free(buf);
}
bool
bufdatabegin(struct wlr_buffer *wlr_buffer, uint32_t flags,
void **data, uint32_t *format, size_t *stride)
{
Buffer *buf = wl_container_of(wlr_buffer, buf, base);
if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false;
*data = buf->data;
*stride = wlr_buffer->width * 4;
*format = DRM_FORMAT_ARGB8888;
return true;
}
void
bufdataend(struct wlr_buffer *wlr_buffer)
{
}
Buffer *
bufmon(Monitor *m)
{
size_t i;
Buffer *buf = NULL;
for (i = 0; i < LENGTH(m->pool); i++) {
if (m->pool[i]) {
if (m->pool[i]->busy)
continue;
buf = m->pool[i];
break;
}
buf = ecalloc(1, sizeof(Buffer) + (m->b.width * 4 * m->b.height));
buf->image = drwl_image_create(NULL, m->b.width, m->b.height, buf->data);
wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height);
m->pool[i] = buf;
break;
}
if (!buf)
return NULL;
buf->busy = true;
LISTEN(&buf->base.events.release, &buf->release, bufrelease);
wlr_buffer_lock(&buf->base);
drwl_setimage(m->drw, buf->image);
return buf;
}
void
bufrelease(struct wl_listener *listener, void *data)
{
Buffer *buf = wl_container_of(listener, buf, release);
buf->busy = false;
wl_list_remove(&buf->release.link);
}
void
buttonpress(struct wl_listener *listener, void *data)
{
unsigned int i = 0, x = 0;
double cx;
unsigned int click;
struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard;
struct wlr_scene_node *node;
struct wlr_scene_buffer *buffer;
uint32_t mods;
Arg arg = {0};
Client *c;
const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
click = ClkRoot;
xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
if (c)
click = ClkClient;
switch (event->state) {
case WL_POINTER_BUTTON_STATE_PRESSED:
cursor_mode = CurPressed;
@ -613,17 +747,34 @@ buttonpress(struct wl_listener *listener, void *data)
if (locked)
break;
if (!c && !exclusive_focus &&
(node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) &&
(buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) {
cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale;
do
x += TEXTW(selmon, tags[i]);
while (cx >= x && ++i < LENGTH(tags));
if (i < LENGTH(tags)) {
click = ClkTagBar;
arg.ui = 1 << i;
} else if (cx < x + TEXTW(selmon, selmon->ltsymbol))
click = ClkLtSymbol;
else if (cx > selmon->b.width - (TEXTW(selmon, stext) - selmon->lrpad + 2)) {
click = ClkStatus;
} else
click = ClkTitle;
}
/* Change focus if the button was _pressed_ over a client */
xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
if (click == ClkClient && (!client_is_unmanaged(c) || client_wants_focus(c)))
focusclient(c, 1);
keyboard = wlr_seat_get_keyboard(seat);
mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
event->button == b->button && b->func) {
b->func(&b->arg);
if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && click == b->click && b->func) {
b->func(click == ClkTagBar && b->arg.i == 0 ? &arg : &b->arg);
return;
}
}
@ -697,6 +848,8 @@ cleanup(void)
/* Destroy after the wayland display (when the monitors are already destroyed)
to avoid destroying them with an invalid scene output. */
wlr_scene_node_destroy(&scene->tree.node);
drwl_fini();
}
void
@ -712,6 +865,12 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_layer_surface_v1_destroy(l->layer_surface);
}
for (i = 0; i < LENGTH(m->pool); i++)
wlr_buffer_drop(&m->pool[i]->base);
drwl_setimage(m->drw, NULL);
drwl_destroy(m->drw);
wl_list_remove(&m->destroy.link);
wl_list_remove(&m->frame.link);
wl_list_remove(&m->link);
@ -722,6 +881,7 @@ cleanupmon(struct wl_listener *listener, void *data)
closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node);
wlr_scene_node_destroy(&m->scene_buffer->node);
free(m);
}
@ -751,7 +911,7 @@ closemon(Monitor *m)
setmon(c, selmon, c->tags);
}
focusclient(focustop(selmon), 1);
printstatus();
drawbars();
}
void
@ -763,6 +923,9 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
struct wlr_layer_surface_v1_state old_state;
if (l->layer_surface->initial_commit) {
wlr_fractional_scale_v1_notify_scale(layer_surface->surface, l->mon->wlr_output->scale);
wlr_surface_set_preferred_buffer_scale(layer_surface->surface, (int32_t)ceilf(l->mon->wlr_output->scale));
/* Temporarily set the layer's current state to pending
* so that we can easily arrange it */
old_state = l->layer_surface->current;
@ -800,8 +963,10 @@ commitnotify(struct wl_listener *listener, void *data)
* a wrong monitor.
*/
applyrules(c);
wlr_surface_set_preferred_buffer_scale(client_surface(c), (int)ceilf(c->mon->wlr_output->scale));
wlr_fractional_scale_v1_notify_scale(client_surface(c), c->mon->wlr_output->scale);
if (c->mon) {
wlr_surface_set_preferred_buffer_scale(client_surface(c), (int)ceilf(c->mon->wlr_output->scale));
wlr_fractional_scale_v1_notify_scale(client_surface(c), c->mon->wlr_output->scale);
}
setmon(c, NULL, 0); /* Make sure to reapply rules in mapnotify() */
wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
@ -945,8 +1110,6 @@ createlayersurface(struct wl_listener *listener, void *data)
wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link);
wlr_surface_send_enter(surface, layer_surface->output);
wlr_fractional_scale_v1_notify_scale(surface, l->mon->wlr_output->scale);
wlr_surface_set_preferred_buffer_scale(surface, (int32_t)ceilf(l->mon->wlr_output->scale));
}
void
@ -1021,8 +1184,15 @@ createmon(struct wl_listener *listener, void *data)
wlr_output_commit_state(wlr_output, &state);
wlr_output_state_finish(&state);
if (!(m->drw = drwl_create()))
die("failed to create drwl context");
m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL);
m->scene_buffer->point_accepts_input = baracceptsinput;
updatebar(m);
wl_list_insert(&mons, &m->link);
printstatus();
drawbars();
/* The xdg-protocol specifies:
*
@ -1335,6 +1505,80 @@ dirtomon(enum wlr_direction dir)
return selmon;
}
void
drawbar(Monitor *m)
{
int x, w, tw = 0;
int boxs = m->drw->font->height / 9;
int boxw = m->drw->font->height / 6 + 2;
uint32_t i, occ = 0, urg = 0;
Client *c;
Buffer *buf;
if (!m->scene_buffer->node.enabled)
return;
if (!(buf = bufmon(m)))
return;
/* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */
drwl_setscheme(m->drw, colors[SchemeNorm]);
tw = TEXTW(m, stext) - m->lrpad + 2; /* 2px right padding */
drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0);
}
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
occ |= c->tags;
if (c->isurgent)
urg |= c->tags;
}
x = 0;
c = focustop(m);
for (i = 0; i < LENGTH(tags); i++) {
w = TEXTW(m, tags[i]);
drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, tags[i], urg & 1 << i);
if (occ & 1 << i)
drwl_rect(m->drw, x + boxs, boxs, boxw, boxw,
m == selmon && c && c->tags & 1 << i,
urg & 1 << i);
x += w;
}
w = TEXTW(m, m->ltsymbol);
drwl_setscheme(m->drw, colors[SchemeNorm]);
x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0);
if ((w = m->b.width - tw - x) > m->b.height) {
if (c) {
drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]);
drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0);
if (c && c->isfloating)
drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0);
} else {
drwl_setscheme(m->drw, colors[SchemeNorm]);
drwl_rect(m->drw, x, 0, w, m->b.height, 1, 1);
}
}
wlr_scene_buffer_set_dest_size(m->scene_buffer,
m->b.real_width, m->b.real_height);
wlr_scene_node_set_position(&m->scene_buffer->node, m->m.x,
m->m.y + (topbar ? 0 : m->m.height - m->b.real_height));
wlr_scene_buffer_set_buffer(m->scene_buffer, &buf->base);
wlr_buffer_unlock(&buf->base);
}
void
drawbars(void)
{
Monitor *m = NULL;
wl_list_for_each(m, &mons, link)
drawbar(m);
}
void
focusclient(Client *c, int lift)
{
@ -1370,13 +1614,13 @@ focusclient(Client *c, int lift)
/* Don't change border color if there is an exclusive focus or we are
* handling a drag operation */
if (!exclusive_focus && !seat->drag)
client_set_border_color(c, focuscolor);
client_set_border_color(c, (float[])COLOR(colors[SchemeSel][ColBorder]));
}
/* Deactivate old client if focus is changing */
if (old && (!c || client_surface(c) != old)) {
/* If an overlay is focused, don't focus or activate the client,
* but only update its position in fstack to render its border with focuscolor
* but only update its position in fstack to render its border with its color
* and focus it after the overlay is closed. */
if (old_client_type == LayerShell && wlr_scene_node_coords(
&old_l->scene->node, &unused_lx, &unused_ly)
@ -1387,12 +1631,11 @@ focusclient(Client *c, int lift)
/* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
* and probably other clients */
} else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
client_set_border_color(old_c, bordercolor);
client_set_border_color(old_c, (float[])COLOR(colors[SchemeNorm][ColBorder]));
client_activate_surface(old, 0);
}
}
printstatus();
drawbars();
if (!c) {
/* With no client, all we have left is to clear focus */
@ -1714,7 +1957,7 @@ mapnotify(struct wl_listener *listener, void *data)
for (i = 0; i < 4; i++) {
c->border[i] = wlr_scene_rect_create(c->scene, 0, 0,
c->isurgent ? urgentcolor : bordercolor);
(float[])COLOR(colors[c->isurgent ? SchemeUrg : SchemeNorm][ColBorder]));
c->border[i]->node.data = c;
}
@ -1737,7 +1980,7 @@ mapnotify(struct wl_listener *listener, void *data)
} else {
applyrules(c);
}
printstatus();
drawbars();
unset_fullscreen:
m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y);
@ -2031,46 +2274,6 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
wlr_seat_pointer_notify_motion(seat, time, sx, sy);
}
void
printstatus(void)
{
Monitor *m = NULL;
Client *c;
uint32_t occ, urg, sel;
const char *appid, *title;
wl_list_for_each(m, &mons, link) {
occ = urg = 0;
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
occ |= c->tags;
if (c->isurgent)
urg |= c->tags;
}
if ((c = focustop(m))) {
title = client_get_title(c);
appid = client_get_appid(c);
printf("%s title %s\n", m->wlr_output->name, title ? title : broken);
printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken);
printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen);
printf("%s floating %d\n", m->wlr_output->name, c->isfloating);
sel = c->tags;
} else {
printf("%s title \n", m->wlr_output->name);
printf("%s appid \n", m->wlr_output->name);
printf("%s fullscreen \n", m->wlr_output->name);
printf("%s floating \n", m->wlr_output->name);
sel = 0;
}
printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n",
m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
}
fflush(stdout);
}
void
powermgrsetmode(struct wl_listener *listener, void *data)
@ -2182,7 +2385,7 @@ resize(Client *c, struct wlr_box geo, int interact)
struct wlr_box *bbox;
struct wlr_box clip;
if (!c->mon || !c->scene)
if (!c->mon || !client_surface(c)->mapped)
return;
bbox = interact ? &sgeom : &c->mon->w;
@ -2225,30 +2428,17 @@ run(char *startup_cmd)
/* Now that the socket exists and the backend is started, run the startup command */
if (startup_cmd) {
int piperw[2];
if (pipe(piperw) < 0)
die("startup: pipe:");
if ((child_pid = fork()) < 0)
die("startup: fork:");
if (child_pid == 0) {
close(STDIN_FILENO);
setsid();
dup2(piperw[0], STDIN_FILENO);
close(piperw[0]);
close(piperw[1]);
execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
die("startup: execl:");
}
dup2(piperw[1], STDOUT_FILENO);
close(piperw[1]);
close(piperw[0]);
}
/* Mark stdout as non-blocking to avoid people who does not close stdin
* nor consumes it in their startup script getting dwl frozen */
if (fd_set_nonblock(STDOUT_FILENO) < 0)
close(STDOUT_FILENO);
printstatus();
drawbars();
/* At this point the outputs are initialized, choose initial selmon based on
* cursor position, and set default cursor image */
@ -2314,7 +2504,7 @@ setfloating(Client *c, int floating)
(p && p->isfullscreen) ? LyrFS
: c->isfloating ? LyrFloat : LyrTile]);
arrange(c->mon);
printstatus();
drawbars();
}
void
@ -2337,7 +2527,7 @@ setfullscreen(Client *c, int fullscreen)
resize(c, c->prev, 0);
}
arrange(c->mon);
printstatus();
drawbars();
}
void
@ -2362,7 +2552,7 @@ setlayout(const Arg *arg)
selmon->lt[selmon->sellt] = (Layout *)arg->v;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
arrange(selmon);
printstatus();
drawbar(selmon);
}
/* arg > 1.0 will set mfact absolutely */
@ -2435,6 +2625,7 @@ setup(void)
for (i = 0; i < (int)LENGTH(sig); i++)
sigaction(sig[i], &sa, NULL);
wlr_log_init(log_level, NULL);
/* The Wayland display is managed by libwayland. It handles accepting
@ -2624,6 +2815,11 @@ setup(void)
LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
drwl_init();
status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy),
STDIN_FILENO, WL_EVENT_READABLE, statusin, NULL);
/* Make sure XWayland clients don't connect to the parent X server,
* e.g when running in the x11 backend or the wayland backend and the
* compositor has Xwayland support */
@ -2648,6 +2844,7 @@ void
spawn(const Arg *arg)
{
if (fork() == 0) {
close(STDIN_FILENO);
dup2(STDERR_FILENO, STDOUT_FILENO);
setsid();
execvp(((char **)arg->v)[0], (char **)arg->v);
@ -2666,6 +2863,30 @@ startdrag(struct wl_listener *listener, void *data)
LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
}
int
statusin(int fd, unsigned int mask, void *data)
{
char status[1024];
ssize_t n;
if (mask & WL_EVENT_ERROR)
die("status in event error");
if (mask & WL_EVENT_HANGUP)
wl_event_source_remove(status_event_source);
n = read(fd, status, sizeof(status) - 1);
if (n < 0 && errno != EWOULDBLOCK)
die("read:");
status[n] = '\0';
status[strcspn(status, "\n")] = '\0';
strncpy(stext, status, sizeof(stext));
drawbars();
return 0;
}
void
tag(const Arg *arg)
{
@ -2676,7 +2897,7 @@ tag(const Arg *arg)
sel->tags = arg->ui & TAGMASK;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -2721,6 +2942,14 @@ tile(Monitor *m)
}
}
void
togglebar(const Arg *arg)
{
wlr_scene_node_set_enabled(&selmon->scene_buffer->node,
!selmon->scene_buffer->node.enabled);
arrangelayers(selmon);
}
void
togglefloating(const Arg *arg)
{
@ -2749,7 +2978,7 @@ toggletag(const Arg *arg)
sel->tags = newtags;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -2762,7 +2991,7 @@ toggleview(const Arg *arg)
selmon->tagset[selmon->seltags] = newtagset;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -2810,7 +3039,7 @@ unmapnotify(struct wl_listener *listener, void *data)
}
wlr_scene_node_destroy(&c->scene->node);
printstatus();
drawbars();
motionnotify(0, NULL, 0, 0, 0, 0);
}
@ -2910,6 +3139,13 @@ updatemons(struct wl_listener *listener, void *data)
}
}
if (stext[0] == '\0')
strncpy(stext, "dwl-"VERSION, sizeof(stext));
wl_list_for_each(m, &mons, link) {
updatebar(m);
drawbar(m);
}
/* FIXME: figure out why the cursor image is at 0,0 after turning all
* the monitors on.
* Move the cursor image where it used to be. It does not generate a
@ -2920,12 +3156,45 @@ updatemons(struct wl_listener *listener, void *data)
wlr_output_manager_v1_set_configuration(output_mgr, config);
}
void
updatebar(Monitor *m)
{
size_t i;
int rw, rh;
char fontattrs[12];
wlr_output_transformed_resolution(m->wlr_output, &rw, &rh);
m->b.width = rw;
m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale);
wlr_scene_node_set_enabled(&m->scene_buffer->node, m->wlr_output->enabled ? showbar : 0);
for (i = 0; i < LENGTH(m->pool); i++)
if (m->pool[i]) {
wlr_buffer_drop(&m->pool[i]->base);
m->pool[i] = NULL;
}
if (m->b.scale == m->wlr_output->scale && m->drw)
return;
drwl_font_destroy(m->drw->font);
snprintf(fontattrs, sizeof(fontattrs), "dpi=%.2f", 96. * m->wlr_output->scale);
if (!(drwl_font_create(m->drw, LENGTH(fonts), fonts, fontattrs)))
die("Could not load font");
m->b.scale = m->wlr_output->scale;
m->lrpad = m->drw->font->height;
m->b.height = m->drw->font->height + 2;
m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale);
}
void
updatetitle(struct wl_listener *listener, void *data)
{
Client *c = wl_container_of(listener, c, set_title);
if (c == focustop(c->mon))
printstatus();
drawbars();
}
void
@ -2938,10 +3207,10 @@ urgent(struct wl_listener *listener, void *data)
return;
c->isurgent = 1;
printstatus();
drawbars();
if (client_surface(c)->mapped)
client_set_border_color(c, urgentcolor);
client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
}
void
@ -2954,7 +3223,7 @@ view(const Arg *arg)
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -2975,11 +3244,11 @@ void
virtualpointer(struct wl_listener *listener, void *data)
{
struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
struct wlr_pointer pointer = event->new_pointer->pointer;
struct wlr_input_device *device = &event->new_pointer->pointer.base;
wlr_cursor_attach_input_device(cursor, &pointer.base);
wlr_cursor_attach_input_device(cursor, device);
if (event->suggested_output)
wlr_cursor_map_input_to_output(cursor, &pointer.base, event->suggested_output);
wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
}
Monitor *
@ -2995,6 +3264,7 @@ xytonode(double x, double y, struct wlr_surface **psurface,
{
struct wlr_scene_node *node, *pnode;
struct wlr_surface *surface = NULL;
struct wlr_scene_surface *scene_surface = NULL;
Client *c = NULL;
LayerSurface *l = NULL;
int layer;
@ -3003,9 +3273,12 @@ xytonode(double x, double y, struct wlr_surface **psurface,
if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
continue;
if (node->type == WLR_SCENE_NODE_BUFFER)
surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node))->surface;
if (node->type == WLR_SCENE_NODE_BUFFER) {
scene_surface = wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node));
if (!scene_surface) continue;
surface = scene_surface->surface;
}
/* Walk the tree to find a node that knows the client */
for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
c = pnode->data;
@ -3144,10 +3417,10 @@ sethints(struct wl_listener *listener, void *data)
return;
c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
printstatus();
drawbars();
if (c->isurgent && surface && surface->mapped)
client_set_border_color(c, urgentcolor);
client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
}
void

58
flake.lock Normal file
View file

@ -0,0 +1,58 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1733759999,
"narHash": "sha256-463SNPWmz46iLzJKRzO3Q2b0Aurff3U1n0nYItxq7jU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1733096140,
"narHash": "sha256-1qRH7uAUsyQI7R1Uwl4T+XvdNv778H0Nb5njNrqvylY=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

28
flake.nix Normal file
View file

@ -0,0 +1,28 @@
{
description = "dwm for Wayland (requires dwl 0.7)";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
};
outputs = inputs@{ flake-parts, nixpkgs, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
flake-parts.flakeModules.easyOverlay
];
systems = nixpkgs.lib.platforms.unix;
perSystem = { config, self', inputs', pkgs, system, ... }: {
overlayAttrs = {
zdwl = config.packages.default;
};
packages.default = pkgs.dwl.overrideAttrs (old: {
buildInputs = old.buildInputs ++ (with pkgs; [
fcft
libdrm.dev
]);
src = ./.;
});
};
};
}

1245
patches/bar-0.7.patch Normal file

File diff suppressed because it is too large Load diff

2
shell.fish Executable file
View file

@ -0,0 +1,2 @@
nix-shell -p bear --command fish
nix develop nixpkgs#dwl --command fish