Compare commits

...

10 Commits

Author SHA1 Message Date
d91ea574df My custom dwl 2026-05-18 13:16:16 +02:00
a2d03cf618 Remove trailing whitespace from README 2026-03-02 02:13:43 +01:00
cb4cc3ae8f Fix typos 2026-02-25 02:38:09 -03:00
9b76ee8e90 Updated README to clarify branch roles
This change effectively reverts DWL to the prior development model, where development of DWL itself happens against the current wlroots release, and changes in anticipation of the next wlroots release are made on a separate branch.
2026-02-21 19:50:17 +01:00
908a73da82 Corrected README 2026-02-21 16:53:19 +01:00
227cdf0128 Added Matrix to README 2026-02-21 16:46:08 +01:00
ca4123072d add support for the data control protocol
Enables the protocol documented at
https://wayland.app/protocols/ext-data-control-v1

It is the upstreamed version of the old wlr_data_control.

It is used e.g. by mpv to read and write the clipboard.
2026-02-12 01:47:40 +01:00
a8915224e8 Remove "unmaintained" notice from README
User @thanatos has offered to take on the lead developer role for dwl.
2026-02-09 08:06:25 +01:00
9b11a49cb7 config.def.h clarify Rule and MonitorRule comments closes #660 2025-12-27 17:08:32 -06:00
53e3e60d4d config.def.h whitespace cleanup 2025-12-27 16:47:38 -06:00
7 changed files with 4434 additions and 161 deletions

View File

@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
-Wfloat-conversion
# CFLAGS / LDFLAGS
PKGS = wayland-server xkbcommon libinput $(XLIBS)
PKGS = wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS)
DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)

View File

@ -1,15 +1,8 @@
# dwl - dwm for Wayland
2025-08-16:
dwl IS CURRENTLY UN-MAINTAINED.
AT THE PRESENT TIME, I (@fauxmight) DO NOT HAVE
THE TIME OR CAPACITY TO KEEP UP WITH [wlroots] CHANGES.
IF YOU ARE INTERESTED IN TAKING ON LEAD DEVELOPER RESPONSIBILITIES,
SEE ISSUE [#1166](https://codeberg.org/dwl/dwl/issues/1166).
---
Join us on our IRC channel: [#dwl on Libera Chat]
Or on the community-maintained [Discord server].
Join us on our [Discord server]
Or Matrix: [#dwl-official:matrix.org]
Or on our IRC channel: [#dwl on Libera Chat]
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,
@ -31,12 +24,14 @@ with each release on the [release] page
### 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.
against the latest release of [wlroots]. PRs should target this branch unless they
depend on functionality that is not in the current release of `wlroots`.
### Preview branch [wlroots-next]
The `wlroots-next` branch is built against the git version of [wlroots], which
is unstable and changes frequently. PRs requiring functionality from the git
version of `wlroots` should target this branch.
### Building dwl
dwl has the following dependencies:
- libinput
@ -210,6 +205,7 @@ inspiration, and to the various contributors to the project, including:
[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
[wlroots-next]: https://codeberg.org/dwl/dwl/src/branch/wlroots-next
[release]: https://codeberg.org/dwl/dwl/releases
[runit]: http://smarden.org/runit/faq.html#userservices
[s6]: https://skarnet.org/software/s6/
@ -217,3 +213,4 @@ inspiration, and to the various contributors to the project, including:
[wiki]: https://codeberg.org/dwl/dwl/wiki/Home#compatible-status-bars
[Discord server]: https://discord.gg/jJxZnrGPWN
[Wayland]: https://wayland.freedesktop.org/
[#dwl-official:matrix.org]: https://matrix.to/#/#dwl-official:matrix.org

View File

@ -7,25 +7,30 @@
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[] = {"JetBrainsMono Nerd Font Mono:style=Bold:size=16"};
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.0f, 0.0f, 0.0f, 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)
/* tagging */
static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
/* logging */
static int log_level = WLR_ERROR;
/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */
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" */
/* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */
};
/* layout(s) */
@ -38,17 +43,14 @@ static const Layout layouts[] = {
/* monitors */
/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator
* WARNING: negative values other than (-1, -1) cause problems with Xwayland clients
* https://gitlab.freedesktop.org/xorg/xserver/-/issues/899
*/
/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */
* WARNING: negative values other than (-1, -1) cause problems with Xwayland clients due to
* https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 */
static const MonitorRule monrules[] = {
/* name mfact nmaster scale layout rotate/reflect x y */
/* example of a HiDPI laptop monitor:
{ "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
*/
/* defaults */
/* name mfact nmaster scale layout rotate/reflect x y
* example of a HiDPI laptop monitor:
{ "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, */
{ NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
/* default monitor rule: can be changed but cannot be eliminated; at least one monitor rule must exist */
};
/* keyboard */
@ -60,8 +62,8 @@ static const struct xkb_rule_names xkb_rules = {
.options = NULL,
};
static const int repeat_rate = 25;
static const int repeat_delay = 600;
static const int repeat_rate = 35;
static const int repeat_delay = 200;
/* Trackpad */
static const int tap_to_click = 1;
@ -107,7 +109,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,45 +121,45 @@ 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 *menucmd[] = { "wmenu-run", NULL };
static const char *termcmd[] = { "alacritty", NULL };
static const char *menucmd[] = { "wmenu-run", "-f", "JetBrainsMono Nerd Font 16", "-l", "10", NULL };
static const Key keys[] = {
/* Note that Shift changes certain key codes: 2 -> at, etc. */
/* modifier key function argument */
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
{ MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
{ MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} },
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
/* modifier key function argument */
{ MODKEY, XKB_KEY_d, spawn, {.v = menucmd} },
{ MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
{ MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
{ MODKEY, XKB_KEY_p, incnmaster, {.i = -1} },
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY, XKB_KEY_q, killclient, {0} },
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
@ -170,7 +172,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} },
};

172
config.def.h.orig Normal file
View File

@ -0,0 +1,172 @@
/* Taken from https://github.com/djpohly/dwl/issues/466 */
#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \
((hex >> 16) & 0xFF) / 255.0f, \
((hex >> 8) & 0xFF) / 255.0f, \
(hex & 0xFF) / 255.0f }
/* appearance */
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);
/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
/* logging */
static int log_level = WLR_ERROR;
static const Rule rules[] = {
/* app_id title tags mask isfloating monitor */
{ "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" */
/* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */
};
/* layout(s) */
static const Layout layouts[] = {
/* symbol arrange function */
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
};
/* monitors */
/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator
* WARNING: negative values other than (-1, -1) cause problems with Xwayland clients due to
* https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 */
static const MonitorRule monrules[] = {
/* name mfact nmaster scale layout rotate/reflect x y
* example of a HiDPI laptop monitor:
{ "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, */
{ NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
/* default monitor rule: can be changed but cannot be eliminated; at least one monitor rule must exist */
};
/* keyboard */
static const struct xkb_rule_names xkb_rules = {
/* can specify fields: rules, model, layout, variant, options */
/* example:
.options = "ctrl:nocaps",
*/
.options = NULL,
};
static const int repeat_rate = 35;
static const int repeat_delay = 200;
/* Trackpad */
static const int tap_to_click = 1;
static const int tap_and_drag = 1;
static const int drag_lock = 1;
static const int natural_scrolling = 0;
static const int disable_while_typing = 1;
static const int left_handed = 0;
static const int middle_button_emulation = 0;
/* You can choose between:
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
LIBINPUT_CONFIG_SCROLL_2FG
LIBINPUT_CONFIG_SCROLL_EDGE
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
*/
static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
/* You can choose between:
LIBINPUT_CONFIG_CLICK_METHOD_NONE
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
*/
static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
/* You can choose between:
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
*/
static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
/* You can choose between:
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
*/
static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
static const double accel_speed = 0.0;
/* You can choose between:
LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
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_LOGO
#define TAGKEYS(KEY,SKEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
/* commands */
static const char *termcmd[] = { "foot", NULL };
static const char *menucmd[] = { "wmenu-run", NULL };
static const Key keys[] = {
/* Note that Shift changes certain key codes: 2 -> at, etc. */
/* modifier key function argument */
{ MODKEY, XKB_KEY_d, spawn, {.v = menucmd} },
{ MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
{ MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
{ MODKEY, XKB_KEY_p, incnmaster, {.i = -1} },
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY, XKB_KEY_q, killclient, {0} },
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
/* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
* do not remove them.
*/
#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
};
static const Button buttons[] = {
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
};

310
drwl.h Normal file
View File

@ -0,0 +1,310 @@
/*
* drwl - https://codeberg.org/sewn/drwl
*
* Copyright (c) 2023-2025 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)
{
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();
}

457
dwl.c
View File

@ -1,10 +1,12 @@
/*
* See LICENSE file for copyright and license details.
*/
#include <fcntl.h>
#include <getopt.h>
#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>
@ -24,6 +26,7 @@
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_ext_data_control_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
@ -59,6 +62,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>
@ -69,6 +73,7 @@
#endif
#include "util.h"
#include "drwl.h"
/* macros */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
@ -77,14 +82,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 { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _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 */
typedef union {
int i;
@ -94,6 +102,7 @@ typedef union {
} Arg;
typedef struct {
unsigned int click;
unsigned int mod;
unsigned int button;
void (*func)(const Arg *);
@ -183,10 +192,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;
@ -194,6 +212,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];
@ -205,6 +228,9 @@ struct Monitor {
int nmaster;
char ltsymbol[16];
int asleep;
Drwl *drw;
Buffer *pool[2];
int lrpad;
};
typedef struct {
@ -247,6 +273,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);
@ -282,6 +315,8 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data);
static void destroysessionlock(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);
@ -310,7 +345,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);
@ -331,9 +365,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);
@ -342,6 +378,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);
@ -406,6 +443,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,
};
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
static struct wl_listener cursor_button = {.notify = buttonpress};
@ -521,7 +567,7 @@ arrange(Monitor *m)
wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
(c = focustop(m)) && c->isfullscreen);
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
/* We move all clients (except fullscreen and unmanaged) to LyrTile while
* in floating layout to avoid "real" floating clients be always on top */
@ -576,6 +622,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);
@ -585,7 +636,7 @@ arrangelayers(Monitor *m)
arrange(m);
}
/* Arrange non-exlusive surfaces from top->bottom */
/* Arrange non-exclusive surfaces from top->bottom */
for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 0);
@ -618,17 +669,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, occ = 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;
@ -636,17 +772,41 @@ 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;
wl_list_for_each(c, &clients, link) {
if (c->mon != selmon)
continue;
occ |= c->tags == TAGMASK ? 0 : c->tags;
}
do {
if (!(occ & 1 << i || selmon->tagset[selmon->seltags] & 1 << i))
continue;
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;
}
}
@ -721,6 +881,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
@ -736,6 +898,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);
@ -748,6 +916,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);
}
@ -814,7 +983,7 @@ closemon(Monitor *m)
setmon(c, selmon, c->tags);
}
focusclient(focustop(selmon), 1);
printstatus();
drawbars();
}
void
@ -1066,7 +1235,7 @@ createmon(struct wl_listener *listener, void *data)
m->nmaster = r->nmaster;
m->lt[0] = r->lt;
m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
wlr_output_state_set_scale(&state, r->scale);
wlr_output_state_set_transform(&state, r->rr);
break;
@ -1088,8 +1257,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:
*
@ -1399,6 +1575,78 @@ 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 == TAGMASK ? 0 : c->tags;
if (c->isurgent)
urg |= c->tags;
}
x = 0;
c = focustop(m);
for (i = 0; i < LENGTH(tags); i++) {
if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
continue;
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);
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)
{
@ -1433,13 +1681,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)
@ -1450,12 +1698,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 */
@ -1769,7 +2016,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;
}
@ -1792,7 +2039,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);
@ -2085,44 +2332,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;
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))) {
printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));
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)
{
@ -2133,7 +2342,7 @@ powermgrsetmode(struct wl_listener *listener, void *data)
if (!m)
return;
m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */
m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
wlr_output_state_set_enabled(&state, event->mode);
wlr_output_commit_state(m->wlr_output, &state);
@ -2251,22 +2460,14 @@ 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 the startup script
@ -2276,7 +2477,7 @@ run(char *startup_cmd)
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 */
@ -2342,7 +2543,7 @@ setfloating(Client *c, int floating)
(p && p->isfullscreen) ? LyrFS
: c->isfloating ? LyrFloat : LyrTile]);
arrange(c->mon);
printstatus();
drawbars();
}
void
@ -2365,7 +2566,7 @@ setfullscreen(Client *c, int fullscreen)
resize(c, c->prev, 0);
}
arrange(c->mon);
printstatus();
drawbars();
}
void
@ -2377,9 +2578,9 @@ setlayout(const Arg *arg)
selmon->sellt ^= 1;
if (arg && arg->v)
selmon->lt[selmon->sellt] = (Layout *)arg->v;
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof(selmon->ltsymbol));
arrange(selmon);
printstatus();
drawbar(selmon);
}
/* arg > 1.0 will set mfact absolutely */
@ -2452,10 +2653,11 @@ 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
* clients from the Unix socket, manging Wayland globals, and so on. */
* clients from the Unix socket, managing Wayland globals, and so on. */
dpy = wl_display_create();
event_loop = wl_display_get_event_loop(dpy);
@ -2518,6 +2720,7 @@ setup(void)
wlr_export_dmabuf_manager_v1_create(dpy);
wlr_screencopy_manager_v1_create(dpy);
wlr_data_control_manager_v1_create(dpy);
wlr_ext_data_control_manager_v1_create(dpy, 1);
wlr_primary_selection_v1_device_manager_create(dpy);
wlr_viewporter_create(dpy);
wlr_single_pixel_buffer_manager_v1_create(dpy);
@ -2646,6 +2849,11 @@ setup(void)
wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
wl_signal_add(&output_mgr->events.test, &output_mgr_test);
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 */
@ -2670,6 +2878,8 @@ void
spawn(const Arg *arg)
{
if (fork() == 0) {
close(STDIN_FILENO);
open("/dev/null", O_RDWR);
dup2(STDERR_FILENO, STDOUT_FILENO);
setsid();
execvp(((char **)arg->v)[0], (char **)arg->v);
@ -2688,6 +2898,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[256];
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)
{
@ -2698,7 +2932,7 @@ tag(const Arg *arg)
sel->tags = arg->ui & TAGMASK;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -2743,6 +2977,15 @@ tile(Monitor *m)
}
}
void
togglebar(const Arg *arg)
{
wlr_scene_node_set_enabled(&selmon->scene_buffer->node,
!selmon->scene_buffer->node.enabled);
arrangelayers(selmon);
drawbars();
}
void
togglefloating(const Arg *arg)
{
@ -2771,7 +3014,7 @@ toggletag(const Arg *arg)
sel->tags = newtags;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -2784,7 +3027,7 @@ toggleview(const Arg *arg)
selmon->tagset[selmon->seltags] = newtagset;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -2832,7 +3075,7 @@ unmapnotify(struct wl_listener *listener, void *data)
}
wlr_scene_node_destroy(&c->scene->node);
printstatus();
drawbars();
motionnotify(0, NULL, 0, 0, 0, 0);
}
@ -2932,6 +3175,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
@ -2942,12 +3192,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
@ -2960,10 +3243,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
@ -2976,7 +3259,7 @@ view(const Arg *arg)
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
focusclient(focustop(selmon), 1);
arrange(selmon);
printstatus();
drawbars();
}
void
@ -3017,6 +3300,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;
@ -3025,9 +3309,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;
@ -3160,10 +3447,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

3497
dwl.c.orig Normal file

File diff suppressed because it is too large Load Diff