Compare commits

..

60 commits

Author SHA1 Message Date
DreamMaoMao
4456f4536a fix: shouldn't configure uninitialized layer_surface 2025-03-13 20:36:18 +00:00
DreamMaoMao
e0f531d508 fix: crash when open some x11 app 2025-03-12 16:27:47 +08:00
korei999
aa69ed81b5
allocate with LISTEN_STATIC
Fixes: https://codeberg.org/dwl/dwl/issues/723
Supersedes: https://codeberg.org/dwl/dwl/pulls/724
2025-02-01 22:34:58 -06:00
Leonardo Hernández Hernández
d1c2f43498
rename some listeners
To keep consistency with the rest of listeners
2025-01-19 17:27:16 -06:00
Leonardo Hernández Hernández
da13a95683
destroy keyboard group after unlinking listeners
Last commit addressing the issue mentioned in
0925fe956a
2025-01-19 17:26:28 -06:00
Leonardo Hernández Hernández
9a9f67db1c
unlink global listeners on destroy
Continuation of 0925fe956a
2025-01-19 17:26:02 -06:00
Leonardo Hernández Hernández
4e7e2999d4
Partially revert "Line saver: LISTEN_STATIC macro"
This reverts commit 33bcd2e4ca.

We keep LISTEN_STATIC for three instances where we use it. We use
simple listeners for the rest of signals.

This is the continuation of 0925fe956a
2025-01-19 17:24:54 -06:00
Leonardo Hernández Hernández
0925fe956a
unlink some destroy listeners
Recently wlroots was updated to assert that signals do not have listeners
attached on destroy.

This is just a preliminar work to fix dwl. At the moment dwl will trigger the
assertions at exit.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4918
2025-01-17 21:03:28 -06:00
Leonardo Hernández Hernández
26504f9a6f
do not call waitid(2) in the signal handler when Xwayland is enabled
waitid(2) is not a async-signal-safe function acording to signal-safety(7)

We can stop doing this because wlroots!4926 allows compositors to install
signal handlers for SIGCHLD.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4926
2025-01-14 12:23:55 -06:00
Leonardo Hernández Hernández
6f34a6d3a6
use wlr_xwayland_surface_has_window_type() (wlroots!4553)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4553
2025-01-14 12:23:55 -06:00
Leonardo Hernández Hernández
30f5063474
manually call updatemons in powermgrsetmode()
Fixes: https://codeberg.org/dwl/dwl/issues/713
2024-12-10 22:49:09 -06:00
Leonardo Hernández Hernández
1d08ade132
remove binary before copying to destination
Since Linux 6.11 is possible overwrite a running executable, possibly making it
crash.

Thanks to: movq42rax
Fixes: https://codeberg.org/dwl/dwl/issues/709
References: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a010c412853
References: https://lore.kernel.org/stable/CACKH++YAtEMYu2nTLUyfmxZoGO37fqogKMDkBpddmNaz5HE6ng@mail.gmail.com/T/#u
2024-11-15 00:26:51 -06:00
Leonardo Hernández Hernández
84245764e2
specify version for presentation-time (wlroots!4858)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4858
2024-10-27 20:37:15 -06:00
Leonardo Hernández Hernández
6ca87210d4
check if the backend supports explicit sync before creating the object (wlroots!4848)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4848
2024-10-27 20:37:15 -06:00
Leonardo Hernández Hernández
002c7d2204
tell xwayland clients they're maximized
like we do to xdg clients when tiled state is not supported.
2024-09-21 21:00:47 -06:00
Guido Cella
8206cc8889 fix a use after free
This line makes dwl crash after closing mpv with the switchtotag patch.
2024-09-12 04:33:19 +00:00
Guido Cella
54f207839f reorder config.mk variables
By placing the default WLR_INCS and WLR_LIBS before the ones for an
alternative wlroots, they don't need to be commented to enable the
alternative ones.
2024-09-08 20:51:41 +02:00
Leonardo Hernández Hernández
9c05b9622c
fix style for client_set_scale() 2024-08-30 22:29:08 -06:00
choc
d34be5d545
remove unused link member from KeyboardGroup
unnecessary since grouping Keyboard wl_list to use wlr_keyboard_group in 023efce

ΔSLOC: -1
2024-08-27 23:12:00 -06:00
Leonardo Hernández Hernández
c49312f084
disable scene node unless it is unmanaged 2024-08-27 23:09:46 -06:00
Leonardo Hernández Hernández
f899060965
send a configure to unmanaged clients when mapping 2024-08-27 23:09:46 -06:00
Leonardo Hernández Hernández
cc72df11d6
configure xdg_toplevels after configuring it's decoration 2024-08-27 23:09:46 -06:00
Leonardo Hernández Hernández
0312720ae8
remove a space before parenthesis in function calls 2024-08-27 23:09:46 -06:00
Leonardo Hernández Hernández
6de87121e2
destroy popups when we can't get it's parent or they don't have monitor 2024-08-27 23:09:46 -06:00
Leonardo Hernández Hernández
bbc00d88a4
remove a redundant check
resize() now does the same check
2024-08-27 23:09:46 -06:00
Leonardo Hernández Hernández
54b546121b
avoid using a else block 2024-08-27 23:09:46 -06:00
Leonardo Hernández Hernández
43016bdad8
introduce client_set_scale() 2024-08-27 23:09:41 -06:00
Leonardo Hernández Hernández
b616476c85
remove unnecessary LayerShell.geom
We only used geom.x and geom.y. We can access those variables directly from the
scene node.
2024-08-27 23:09:08 -06:00
Leonardo Hernández Hernández
d4ad37354e
update comment about first fields of Client and LayerSurface order 2024-08-27 23:09:08 -06:00
Leonardo Hernández Hernández
5db05e82bd
fix style in configurex11() 2024-08-27 23:09:08 -06:00
Leonardo Hernández Hernández
8ec5e52e06
fix crash when a client is created while all outputs are disabled 2024-08-26 21:56:10 -06:00
Leonardo Hernández Hernández
c5275ca571
state that the Discord server is community-maintained
Previously I regularly checked the server but it has been quite a long time
since I was able to do it.
2024-08-18 19:24:04 -06:00
A Frederick Christensen
2c0b889f86
Update CHANGELOG.md 2024-08-18 19:23:56 -06:00
Leonardo Hernández Hernández
554754c9a2
chase xdg_surface geometry changes (wlroots!4788)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4788
2024-08-14 13:37:14 -06:00
Leonardo Hernández Hernández
0caa658276
use wlr_scene_set_gamma_control_manager_v1() (wlroots!4192)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4192
2024-08-14 12:21:27 -06:00
Leonardo Hernández Hernández
07aeef1f7e
guarantee client_get_{title,appid} never return NULL
ΔSLOC: -6
2024-08-14 12:19:37 -06:00
Leonardo Hernández Hernández
e454f7ae81
allow the use of non-system wlroots library
References: https://codeberg.org/dwl/dwl/issues/646#issuecomment-2032644
2024-08-14 12:00:52 -06:00
Leonardo Hernández Hernández
334bbe6f0f
fix potential crash in configurex11()
We can't call resize() on unmanaged clients because they don't have borders and
resize() requires them.

Fixes: 94f4ead7da
2024-08-10 10:47:48 -06:00
Leonardo Hernández Hernández
1b805ddd38
account border width in configurex11()
Fixes: 13925eb1da
2024-08-08 14:46:25 -06:00
Leonardo Hernández Hernández
94f4ead7da
actually move unmanaged clients in configurex11()
only calling wlr_xwayland_surface_configure() may be not enough because we also
need to move the scene node in order to make effective the configure
2024-08-08 14:46:08 -06:00
Leonardo Hernández Hernández
bb21ecda30
improve checking in configurex11()
this avoids a client resizing itself when the user is interactively resizing
the client
2024-08-08 14:33:03 -06:00
Leonardo Hernández Hernández
b25717c939
drop a useless check in configurex11() 2024-08-08 14:19:39 -06:00
Leonardo Hernández Hernández
a4fa954616
do not restack xwayland surfaces (wlroots!4756)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4756
2024-08-07 16:58:16 -06:00
Leonardo Hernández Hernández
35951a8d7e
add support for linux-drm-syncobj-v1 (wlroots!4715)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4262
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4715
2024-08-06 12:20:25 -06:00
Leonardo Hernández Hernández
a634e3f527
fix crash when a virtual pointer is destroyed
Fixes: https://codeberg.org/dwl/dwl/issues/680
2024-08-06 12:01:22 -06:00
Leonardo Hernández Hernández
d136dadf45
-pedantic -> -Wpedantic
Bug: https://codeberg.org/dwl/dwl/issues/584
2024-08-01 22:41:00 -06:00
Sivecano
672b4c405d
fix maximize callback not getting deregisterd 2024-07-27 22:05:53 -06:00
Leonardo Hernández Hernández
b5abbc37d8
fix crash when re-mapping a client
Fixes: ab5c554d09
2024-07-27 21:59:27 -06:00
Leonardo Hernández Hernández
986beef5be
replace spaces with tabs
Fixes: 71f11e6cf6
2024-07-27 00:41:39 -06:00
Leonardo Hernández Hernández
487abc28ba
add myself to .mailmap 2024-07-24 15:51:49 -06:00
Leonardo Hernández Hernández
cd216908a7
send scale on initial commit to layer surfaces
Signed-off-by: Leonardo Hernández Hernández <leohdz172@proton.me>
2024-07-24 06:25:54 -05:00
Lennart Jablonka
f2c5023a3a 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)")
2024-07-23 23:32:15 +00:00
Lennart Jablonka
4bbbb4907e add myself to .mailmap 2024-07-23 23:32:15 +00:00
A Frederick Christensen
ea6a450121
README.md Fix links formatting issue after re-flow text to 80 columns 2024-07-21 14:34:45 -05:00
A Frederick Christensen
ad30ca910b
Documentation restructuring
Modified documentation to make clear the change in development (main) branch versus releases.
2024-07-21 14:21:43 -05:00
Leonardo Hernández Hernández
452a314faa
update README.md to mention the main branch now requires wlroots-git
Closes: https://codeberg.org/dwl/dwl/issues/646
2024-07-14 21:55:58 -06:00
Leonardo Hernández Hernández
da6de7c4d7
update wlr_xwayland_surface names (wlroots!2434)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2434
2024-07-14 21:37:03 -06:00
Leonardo Hernández Hernández
2553111aa3
bump wlroots version 2024-07-14 21:34:44 -06:00
Leonardo Hernández Hernández
51881da27b
bump version to 0.8-dev 2024-07-14 21:34:28 -06:00
Leonardo Hernández Hernández
7328e5691c
changelog: add new 'unreleased' section 2024-07-14 21:33:37 -06:00
19 changed files with 471 additions and 4313 deletions

4
.gitignore vendored
View file

@ -3,6 +3,4 @@ dwl
*-protocol.c *-protocol.c
*-protocol.h *-protocol.h
.ccls-cache .ccls-cache
.clangd config.h
.cache
compile_commands.json

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "patches"]
path = patches
url = git@codeberg.org:dwl/dwl-patches.git

View file

@ -1 +1,3 @@
Lennart Jablonka <humm@ljabl.com> <hummsmith42@gmail.com> Lennart Jablonka <humm@ljabl.com> <hummsmith42@gmail.com>
Leonardo Hernández Hernández <leohdz172@proton.me> <leohdz172@outlook.com>
Leonardo Hernández Hernández <leohdz172@proton.me> <leohdz172@protonmail.com>

View file

@ -1,13 +1,36 @@
# Changelog # Changelog
* [Unreleased](#unreleased)
* [0.7](#0.7) * [0.7](#0.7)
* [0.6](#0.6) * [0.6](#0.6)
* [0.5](#0.5) * [0.5](#0.5)
## Unreleased
### Added
* Support for the linux-drm-syncobj-v1 protocol ([wlroots!4715][wlroots!4715], [#685][685])
* Allow the use of non-system wlroots library ([#646][646])
[wlroots!4715]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4715
[685]: https://codeberg.org/dwl/dwl/pulls/685
[646]: https://codeberg.org/dwl/dwl/pulls/646
### Changed
### Deprecated
### Removed
### Fixed
* Crash when a client is created while all outputs are disabled.
### Security
### Contributors
## 0.7 ## 0.7
See also [0.6](#0.6) release notes. 0.7 builds against wlroots 0.18.x. This version is just 0.6 with wlroots 0.18 compatibility.
### Added ### Added
@ -18,15 +41,9 @@ See also [0.6](#0.6) release notes. 0.7 builds against wlroots 0.18.x.
[601]: https://codeberg.org/dwl/dwl/issues/601 [601]: https://codeberg.org/dwl/dwl/issues/601
### Fixed
* Crash when re-mapping unmapped clients.
### Contributors ### Contributors
Guido Cella Guido Cella
Lennart Jablonka
## 0.6 ## 0.6

View file

@ -6,25 +6,23 @@ include config.mk
# flags for compiling # flags for compiling
DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \ DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \
-DVERSION=\"$(VERSION)\" $(XWAYLAND) -DVERSION=\"$(VERSION)\" $(XWAYLAND)
DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement \ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
-Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \ -Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \
-Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \ -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \
-Wfloat-conversion -Wfloat-conversion
# CFLAGS / LDFLAGS # CFLAGS / LDFLAGS
PKGS = wlroots-0.18 wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS) PKGS = wayland-server xkbcommon libinput $(XLIBS)
DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS) LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
all: dwl all: dwl
dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o dwl: dwl.o util.o
$(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
dwl-ipc-unstable-v2-protocol.h
util.o: util.c util.h util.o: util.c util.h
dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h
# wayland-scanner is a tool which generates C headers and rigging for Wayland # wayland-scanner is a tool which generates C headers and rigging for Wayland
# protocols, which are specified in XML. wlroots requires you to rig these up # protocols, which are specified in XML. wlroots requires you to rig these up
@ -47,12 +45,6 @@ wlr-output-power-management-unstable-v1-protocol.h:
xdg-shell-protocol.h: xdg-shell-protocol.h:
$(WAYLAND_SCANNER) server-header \ $(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
dwl-ipc-unstable-v2-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/dwl-ipc-unstable-v2.xml $@
dwl-ipc-unstable-v2-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/dwl-ipc-unstable-v2.xml $@
config.h: config.h:
cp config.def.h $@ cp config.def.h $@
@ -69,6 +61,7 @@ dist: clean
install: dwl install: dwl
mkdir -p $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(PREFIX)/bin
rm -f $(DESTDIR)$(PREFIX)/bin/dwl
cp -f dwl $(DESTDIR)$(PREFIX)/bin cp -f dwl $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl
mkdir -p $(DESTDIR)$(MANDIR)/man1 mkdir -p $(DESTDIR)$(MANDIR)/man1

View file

@ -1,7 +1,7 @@
# dwl - dwm for Wayland # dwl - dwm for Wayland
Join us on our IRC channel: [#dwl on Libera Chat] Join us on our IRC channel: [#dwl on Libera Chat]
Or on our [Discord server]. Or on the community-maintained [Discord server].
dwl is a compact, hackable compositor for [Wayland] based on [wlroots]. It is 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, intended to fill the same space in the Wayland world that dwm does in X11,
@ -14,8 +14,6 @@ philosophy. Like dwm, dwl is:
## Getting Started: ## Getting Started:
### **dwl branch 0.7 and releases based upon 0.7 build against [wlroots] 0.18**
### Latest semi-stable [release] ### Latest semi-stable [release]
This is probably where you want to start. This builds against the dependent This is probably where you want to start. This builds against the dependent
packages' versions currently shipping in major distributions. If your packages' versions currently shipping in major distributions. If your

563
btrtile.c
View file

@ -1,563 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* btrtile.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */
/* Updated: 2025/02/13 23:22:33 by jmakkone ### ########.fr */
/* */
/* ************************************************************************** */
typedef struct LayoutNode {
unsigned int is_client_node;
unsigned int is_split_vertically;
float split_ratio;
struct LayoutNode *left;
struct LayoutNode *right;
struct LayoutNode *split_node;
Client *client;
} LayoutNode;
static void apply_layout(Monitor *m, LayoutNode *node,
struct wlr_box area, unsigned int is_root);
static void btrtile(Monitor *m);
static LayoutNode *create_client_node(Client *c);
static LayoutNode *create_split_node(unsigned int is_split_vertically,
LayoutNode *left, LayoutNode *right);
static void destroy_node(LayoutNode *node);
static void destroy_tree(Monitor *m);
static LayoutNode *find_client_node(LayoutNode *node, Client *c);
static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert);
static void init_tree(Monitor *m);
static void insert_client(Monitor *m, Client *focused_client, Client *new_client);
static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
static void remove_client(Monitor *m, Client *c);
static void setratio_h(const Arg *arg);
static void setratio_v(const Arg *arg);
static void swapclients(const Arg *arg);
static unsigned int visible_count(LayoutNode *node, Monitor *m);
static Client *xytoclient(double x, double y);
static int resizing_from_mouse = 0;
static double resize_last_update_x, resize_last_update_y;
static uint32_t last_resize_time = 0;
void
apply_layout(Monitor *m, LayoutNode *node,
struct wlr_box area, unsigned int is_root)
{
Client *c;
float ratio;
unsigned int left_count, right_count, mid;
struct wlr_box left_area, right_area;
if (!node)
return;
/* If this node is a client node, check if it is visible. */
if (node->is_client_node) {
c = node->client;
if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
return;
resize(c, area, 0);
c->old_geom = area;
return;
}
/* For a split node, we see how many visible children are on each side: */
left_count = visible_count(node->left, m);
right_count = visible_count(node->right, m);
if (left_count == 0 && right_count == 0) {
return;
} else if (left_count > 0 && right_count == 0) {
apply_layout(m, node->left, area, 0);
return;
} else if (left_count == 0 && right_count > 0) {
apply_layout(m, node->right, area, 0);
return;
}
/* If were here, we have visible clients in both subtrees. */
ratio = node->split_ratio;
if (ratio < 0.05f)
ratio = 0.05f;
if (ratio > 0.95f)
ratio = 0.95f;
memset(&left_area, 0, sizeof(left_area));
memset(&right_area, 0, sizeof(right_area));
if (node->is_split_vertically) {
mid = (unsigned int)(area.width * ratio);
left_area.x = area.x;
left_area.y = area.y;
left_area.width = mid;
left_area.height = area.height;
right_area.x = area.x + mid;
right_area.y = area.y;
right_area.width = area.width - mid;
right_area.height = area.height;
} else {
/* horizontal split */
mid = (unsigned int)(area.height * ratio);
left_area.x = area.x;
left_area.y = area.y;
left_area.width = area.width;
left_area.height = mid;
right_area.x = area.x;
right_area.y = area.y + mid;
right_area.width = area.width;
right_area.height= area.height - mid;
}
apply_layout(m, node->left, left_area, 0);
apply_layout(m, node->right, right_area, 0);
}
void
btrtile(Monitor *m)
{
Client *c, *focused = NULL;
int n = 0;
LayoutNode *found;
struct wlr_box full_area;
if (!m || !m->root)
return;
/* Remove non tiled clients from tree. */
wl_list_for_each(c, &clients, link) {
if (c->mon == m && !c->isfloating && !c->isfullscreen) {
} else {
remove_client(m, c);
}
}
/* If no client is found under cursor, fallback to focustop(m) */
if (!(focused = xytoclient(cursor->x, cursor->y)))
focused = focustop(m);
/* Insert visible clients that are not part of the tree. */
wl_list_for_each(c, &clients, link) {
if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && c->mon == m) {
found = find_client_node(m->root, c);
if (!found) {
insert_client(m, focused, c);
}
n++;
}
}
if (n == 0)
return;
full_area = m->w;
apply_layout(m, m->root, full_area, 1);
}
LayoutNode *
create_client_node(Client *c)
{
LayoutNode *node = calloc(1, sizeof(LayoutNode));
if (!node)
return NULL;
node->is_client_node = 1;
node->split_ratio = 0.5f;
node->client = c;
return node;
}
LayoutNode *
create_split_node(unsigned int is_split_vertically,
LayoutNode *left, LayoutNode *right)
{
LayoutNode *node = calloc(1, sizeof(LayoutNode));
if (!node)
return NULL;
node->is_client_node = 0;
node->split_ratio = 0.5f;
node->is_split_vertically = is_split_vertically;
node->left = left;
node->right = right;
if (left)
left->split_node = node;
if (right)
right->split_node = node;
return node;
}
void
destroy_node(LayoutNode *node)
{
if (!node)
return;
if (!node->is_client_node) {
destroy_node(node->left);
destroy_node(node->right);
}
free(node);
}
void
destroy_tree(Monitor *m)
{
if (!m || !m->root)
return;
destroy_node(m->root);
m->root = NULL;
}
LayoutNode *
find_client_node(LayoutNode *node, Client *c)
{
LayoutNode *res;
if (!node || !c)
return NULL;
if (node->is_client_node) {
return (node->client == c) ? node : NULL;
}
res = find_client_node(node->left, c);
return res ? res : find_client_node(node->right, c);
}
LayoutNode *
find_suitable_split(LayoutNode *start_node, unsigned int need_vertical)
{
LayoutNode *n = start_node;
/* if we started from a client node, jump to its parent: */
if (n && n->is_client_node)
n = n->split_node;
while (n) {
if (!n->is_client_node && n->is_split_vertically == need_vertical &&
visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0)
return n;
n = n->split_node;
}
return NULL;
}
void
init_tree(Monitor *m)
{
if (!m)
return;
m->root = calloc(1, sizeof(LayoutNode));
if (!m->root)
m->root = NULL;
}
void
insert_client(Monitor *m, Client *focused_client, Client *new_client)
{
Client *old_client;
LayoutNode **root = &m->root, *old_root,
*focused_node, *new_client_node, *old_client_node;
unsigned int wider, mid_x, mid_y;
/* If no root , new client becomes the root. */
if (!*root) {
*root = create_client_node(new_client);
return;
}
/* Find the focused_client node,
* if not found split the root. */
focused_node = focused_client ?
find_client_node(*root, focused_client) : NULL;
if (!focused_node) {
old_root = *root;
new_client_node = create_client_node(new_client);
*root = create_split_node(1, old_root, new_client_node);
return;
}
/* Turn focused node from a client node into a split node,
* and attach old_client + new_client. */
old_client = focused_node->client;
old_client_node = create_client_node(old_client);
new_client_node = create_client_node(new_client);
/* Decide split direction. */
wider = (focused_client->geom.width >= focused_client->geom.height);
focused_node->is_client_node = 0;
focused_node->client = NULL;
focused_node->is_split_vertically = (wider ? 1 : 0);
/* Pick new_client side depending on the cursor position. */
mid_x = focused_client->geom.x + focused_client->geom.width / 2;
mid_y = focused_client->geom.y + focused_client->geom.height / 2;
if (wider) {
/* vertical split => left vs right */
if (cursor->x <= mid_x) {
focused_node->left = new_client_node;
focused_node->right = old_client_node;
} else {
focused_node->left = old_client_node;
focused_node->right = new_client_node;
}
} else {
/* horizontal split => top vs bottom */
if (cursor->y <= mid_y) {
focused_node->left = new_client_node;
focused_node->right = old_client_node;
} else {
focused_node->left = old_client_node;
focused_node->right = new_client_node;
}
}
old_client_node->split_node = focused_node;
new_client_node->split_node = focused_node;
focused_node->split_ratio = 0.5f;
}
LayoutNode *
remove_client_node(LayoutNode *node, Client *c)
{
LayoutNode *tmp;
if (!node)
return NULL;
if (node->is_client_node) {
/* If this client_node is the client we're removing,
* return NULL to remove it */
if (node->client == c) {
free(node);
return NULL;
}
return node;
}
node->left = remove_client_node(node->left, c);
node->right = remove_client_node(node->right, c);
/* If one of the client node is NULL after removal and the other is not,
* we "lift" the other client node up to replace this split node. */
if (!node->left && node->right) {
tmp = node->right;
/* Save pointer to split node */
if (tmp)
tmp->split_node = node->split_node;
free(node);
return tmp;
}
if (!node->right && node->left) {
tmp = node->left;
/* Save pointer to split node */
if (tmp)
tmp->split_node = node->split_node;
free(node);
return tmp;
}
/* If both children exist or both are NULL (empty tree),
* return node as is. */
return node;
}
void
remove_client(Monitor *m, Client *c)
{
if (!m->root || !c)
return;
m->root = remove_client_node(m->root, c);
}
void
setratio_h(const Arg *arg)
{
Client *sel = focustop(selmon);
LayoutNode *client_node, *split_node;
float new_ratio;
if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
return;
client_node = find_client_node(selmon->root, sel);
if (!client_node)
return;
split_node = find_suitable_split(client_node, 1);
if (!split_node)
return;
new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
if (new_ratio < 0.05f)
new_ratio = 0.05f;
if (new_ratio > 0.95f)
new_ratio = 0.95f;
split_node->split_ratio = new_ratio;
/* Skip the arrange if done resizing by mouse,
* we call arrange from motionotify */
if (!resizing_from_mouse) {
arrange(selmon);
}
}
void
setratio_v(const Arg *arg)
{
Client *sel = focustop(selmon);
LayoutNode *client_node, *split_node;
float new_ratio;
if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
return;
client_node = find_client_node(selmon->root, sel);
if (!client_node)
return;
split_node = find_suitable_split(client_node, 0);
if (!split_node)
return;
new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
if (new_ratio < 0.05f)
new_ratio = 0.05f;
if (new_ratio > 0.95f)
new_ratio = 0.95f;
split_node->split_ratio = new_ratio;
/* Skip the arrange if done resizing by mouse,
* we call arrange from motionotify */
if (!resizing_from_mouse) {
arrange(selmon);
}
}
void swapclients(const Arg *arg) {
Client *c, *tmp, *target = NULL, *sel = focustop(selmon);
LayoutNode *sel_node, *target_node;
int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y,
cand_center_x, cand_center_y;
if (!sel || sel->isfullscreen ||
!selmon->root || !selmon->lt[selmon->sellt]->arrange)
return;
/* Get the center coordinates of the selected client */
sel_center_x = sel->geom.x + sel->geom.width / 2;
sel_center_y = sel->geom.y + sel->geom.height / 2;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel)
continue;
/* Get the center of candidate client */
cand_center_x = c->geom.x + c->geom.width / 2;
cand_center_y = c->geom.y + c->geom.height / 2;
/* Check that the candidate lies in the requested direction. */
switch (arg->ui) {
case 0:
if (cand_center_x >= sel_center_x)
continue;
break;
case 1:
if (cand_center_x <= sel_center_x)
continue;
break;
case 2:
if (cand_center_y >= sel_center_y)
continue;
break;
case 3:
if (cand_center_y <= sel_center_y)
continue;
break;
default:
continue;
}
/* Get distance between the centers */
dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y);
if (dist < closest_dist) {
closest_dist = dist;
target = c;
}
}
/* If target is found, swap the two clients positions in the layout tree */
if (target) {
sel_node = find_client_node(selmon->root, sel);
target_node = find_client_node(selmon->root, target);
if (sel_node && target_node) {
tmp = sel_node->client;
sel_node->client = target_node->client;
target_node->client = tmp;
arrange(selmon);
}
}
}
unsigned int
visible_count(LayoutNode *node, Monitor *m)
{
Client *c;
if (!node)
return 0;
/* Check if this client is visible. */
if (node->is_client_node) {
c = node->client;
if (c && VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
return 1;
return 0;
}
/* Else its a split node. */
return visible_count(node->left, m) + visible_count(node->right, m);
}
Client *
xytoclient(double x, double y) {
Client *c, *closest = NULL;
double dist, mindist = INT_MAX, dx, dy;
wl_list_for_each_reverse(c, &clients, link) {
if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen &&
x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
return c;
}
}
/* If no client was found at cursor position fallback to closest. */
wl_list_for_each_reverse(c, &clients, link) {
if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) {
dx = 0, dy = 0;
if (x < c->geom.x)
dx = c->geom.x - x;
else if (x > (c->geom.x + c->geom.width))
dx = x - (c->geom.x + c->geom.width);
if (y < c->geom.y)
dy = c->geom.y - y;
else if (y > (c->geom.y + c->geom.height))
dy = y - (c->geom.y + c->geom.height);
dist = sqrt(dx * dx + dy * dy);
if (dist < mindist) {
mindist = dist;
closest = c;
}
}
}
return closest;
}

View file

@ -126,27 +126,14 @@ client_get_appid(Client *c)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
return c->surface.xwayland->class; return c->surface.xwayland->class ? c->surface.xwayland->class : "broken";
#endif #endif
return c->surface.xdg->toplevel->app_id; return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken";
}
static inline int
client_get_pid(Client *c)
{
pid_t pid;
#ifdef XWAYLAND
if (client_is_x11(c))
return c->surface.xwayland->pid;
#endif
wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL);
return pid;
} }
static inline void static inline void
client_get_clip(Client *c, struct wlr_box *clip) client_get_clip(Client *c, struct wlr_box *clip)
{ {
struct wlr_box xdg_geom = {0};
*clip = (struct wlr_box){ *clip = (struct wlr_box){
.x = 0, .x = 0,
.y = 0, .y = 0,
@ -159,9 +146,8 @@ client_get_clip(Client *c, struct wlr_box *clip)
return; return;
#endif #endif
wlr_xdg_surface_get_geometry(c->surface.xdg, &xdg_geom); clip->x = c->surface.xdg->geometry.x;
clip->x = xdg_geom.x; clip->y = c->surface.xdg->geometry.y;
clip->y = xdg_geom.y;
} }
static inline void static inline void
@ -176,7 +162,7 @@ client_get_geometry(Client *c, struct wlr_box *geom)
return; return;
} }
#endif #endif
wlr_xdg_surface_get_geometry(c->surface.xdg, geom); *geom = c->surface.xdg->geometry;
} }
static inline Client * static inline Client *
@ -212,9 +198,9 @@ client_get_title(Client *c)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c))
return c->surface.xwayland->title; return c->surface.xwayland->title ? c->surface.xwayland->title : "broken";
#endif #endif
return c->surface.xdg->toplevel->title; return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken";
} }
static inline int static inline int
@ -227,16 +213,15 @@ client_is_float_type(Client *c)
if (client_is_x11(c)) { if (client_is_x11(c)) {
struct wlr_xwayland_surface *surface = c->surface.xwayland; struct wlr_xwayland_surface *surface = c->surface.xwayland;
xcb_size_hints_t *size_hints = surface->size_hints; xcb_size_hints_t *size_hints = surface->size_hints;
size_t i;
if (surface->modal) if (surface->modal)
return 1; return 1;
for (i = 0; i < surface->window_type_len; i++) if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG)
if (surface->window_type[i] == netatom[NetWMWindowTypeDialog] || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH)
|| surface->window_type[i] == netatom[NetWMWindowTypeSplash] || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR)
|| surface->window_type[i] == netatom[NetWMWindowTypeToolbar] || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) {
|| surface->window_type[i] == netatom[NetWMWindowTypeUtility])
return 1; return 1;
}
return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0
&& (size_hints->max_width == size_hints->min_width && (size_hints->max_width == size_hints->min_width
@ -313,17 +298,6 @@ client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb)
wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL); wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL);
} }
static inline void
client_restack_surface(Client *c)
{
#ifdef XWAYLAND
if (client_is_x11(c))
wlr_xwayland_surface_restack(c->surface.xwayland, NULL,
XCB_STACK_MODE_ABOVE);
#endif
return;
}
static inline void static inline void
client_send_close(Client *c) client_send_close(Client *c)
{ {
@ -356,6 +330,13 @@ client_set_fullscreen(Client *c, int fullscreen)
wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen); wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen);
} }
static inline void
client_set_scale(struct wlr_surface *s, float scale)
{
wlr_fractional_scale_v1_notify_scale(s, scale);
wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
}
static inline uint32_t static inline uint32_t
client_set_size(Client *c, uint32_t width, uint32_t height) client_set_size(Client *c, uint32_t width, uint32_t height)
{ {
@ -376,8 +357,11 @@ static inline void
client_set_tiled(Client *c, uint32_t edges) client_set_tiled(Client *c, uint32_t edges)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c)) {
wlr_xwayland_surface_set_maximized(c->surface.xwayland,
edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE);
return; return;
}
#endif #endif
if (wl_resource_get_version(c->surface.xdg->toplevel->resource) if (wl_resource_get_version(c->surface.xdg->toplevel->resource)
>= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) {
@ -403,8 +387,8 @@ client_wants_focus(Client *c)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
return client_is_unmanaged(c) return client_is_unmanaged(c)
&& wlr_xwayland_or_surface_wants_focus(c->surface.xwayland) && wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland)
&& wlr_xwayland_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE; && wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE;
#endif #endif
return 0; return 0;
} }

View file

@ -7,54 +7,30 @@
static const int sloppyfocus = 1; /* focus follows mouse */ 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 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 unsigned int borderpx = 1; /* border pixel of windows */
static const int draw_minimal_borders = 1; /* merge adjacent borders */ 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 */ /* 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 const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
static const char *cursor_theme = NULL;
static const char cursor_size[] = "24"; /* Make sure it's a valid integer, otherwise things will break */
static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */
static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */
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 uint32_t colors[][3] = {
/* fg bg border */
[SchemeNorm] = { 0xbbbbbbff, 0x222222ff, 0x444444ff },
[SchemeSel] = { 0xeeeeeeff, 0x005577ff, 0x005577ff },
[SchemeUrg] = { 0, 0, 0x770000ff },
};
enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };
/* tagging - TAGCOUNT must be no greater than 31 */ /* tagging - TAGCOUNT must be no greater than 31 */
static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; #define TAGCOUNT (9)
#define TAGCOUNT (sizeof(tags) / sizeof(tags[0]))
/* logging */ /* logging */
static int log_level = WLR_ERROR; static int log_level = WLR_ERROR;
/* Autostart */
static const char *const autostart[] = {
"wbg", "/path/to/your/image", NULL,
NULL /* terminate */
};
/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */
static const Rule rules[] = { static const Rule rules[] = {
/* app_id title tags mask isfloating monitor */
/* app_id title tags mask isfloating skipfocus isterm noswallow monitor */
/* examples: */ /* examples: */
{ "Gimp_EXAMPLE", NULL, 0, 1, 0, 0, 0, -1 }, /* Start on currently visible tags floating, not tiled */ { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
{ "firefox_EXAMPLE", NULL, 1 << 8, 0, 0, 0, 0, -1 }, /* Start on ONLY tag "9" */ { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
{ "foot", NULL, 0, 0, 0, 1, 1, -1 }, /* make foot swallow clients that are not foot */
}; };
/* layout(s) */ /* layout(s) */
static const Layout layouts[] = { static const Layout layouts[] = {
/* symbol arrange function */ /* symbol arrange function */
{ "|w|", btrtile },
{ "[]=", tile }, { "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */ { "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle }, { "[M]", monocle },
@ -130,16 +106,14 @@ 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; static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
static const int cursor_timeout = 5;
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ /* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT #define MODKEY WLR_MODIFIER_ALT
#define TAGKEYS(KEY,TAG) \ #define TAGKEYS(KEY,SKEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_SHIFT, KEY, tag, {.ui = 1 << TAG} }, \ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,KEY,toggletag, {.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 */ /* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
@ -148,70 +122,55 @@ static const int cursor_timeout = 5;
static const char *termcmd[] = { "foot", NULL }; static const char *termcmd[] = { "foot", NULL };
static const char *menucmd[] = { "wmenu-run", NULL }; static const char *menucmd[] = { "wmenu-run", NULL };
#include "keys.h"
static const Key keys[] = { static const Key keys[] = {
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
/* modifier key function argument */ /* modifier key function argument */
{ MODKEY, Key_p, spawn, {.v = menucmd} }, { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_Return, spawn, {.v = termcmd} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
{ MODKEY, Key_b, togglebar, {0} }, { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, Key_r, regions, SHCMD("grim -g \"$(slurp)\"") }, { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
{ MODKEY, Key_j, focusstack, {.i = +1} }, { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
{ MODKEY, Key_k, focusstack, {.i = -1} }, { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
{ MODKEY, Key_i, incnmaster, {.i = +1} }, { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
{ MODKEY, Key_d, incnmaster, {.i = -1} }, { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
{ MODKEY, Key_h, setmfact, {.f = -0.05f} }, { MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, Key_l, setmfact, {.f = +0.05f} }, { MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY, Key_Return, zoom, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
{ MODKEY, Key_Tab, view, {0} }, { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_c, killclient, {0} }, { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, Key_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, Key_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY, Key_m, setlayout, {.v = &layouts[2]} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, Key_space, setlayout, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_space, togglefloating, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY, Key_e, togglefullscreen, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, Key_0, view, {.ui = ~0} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_0, tag, {.ui = ~0} }, { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY, Key_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, Key_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_comma, tagmon, {.i = WLR_DIRECTION_LEFT} }, TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
{ MODKEY|WLR_MODIFIER_SHIFT, Key_period, tagmon, {.i = WLR_DIRECTION_RIGHT} }, TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
{ MODKEY|WLR_MODIFIER_CTRL, Key_Right, setratio_h, {.f = +0.025f} }, TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
{ MODKEY|WLR_MODIFIER_CTRL, Key_Left, setratio_h, {.f = -0.025f} }, TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
{ MODKEY|WLR_MODIFIER_CTRL, Key_Up, setratio_v, {.f = -0.025f} }, TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
{ MODKEY|WLR_MODIFIER_CTRL, Key_Down, setratio_v, {.f = +0.025f} }, TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
TAGKEYS( Key_1, 0), TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
TAGKEYS( Key_2, 1), TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( Key_3, 2), TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
TAGKEYS( Key_4, 3), { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
TAGKEYS( Key_5, 4),
TAGKEYS( Key_6, 5),
TAGKEYS( Key_7, 6),
TAGKEYS( Key_8, 7),
TAGKEYS( Key_9, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, Key_q, quit, {0} },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,Key_BackSpace, quit, {0} }, { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
#define CHVT(KEY,n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT, KEY, chvt, {.ui = (n)} }
/* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
* do not remove them. * do not remove them.
*/ */
CHVT(Key_F1, 1), CHVT(Key_F2, 2), CHVT(Key_F3, 3), CHVT(Key_F4, 4), #define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
CHVT(Key_F5, 5), CHVT(Key_F6, 6), CHVT(Key_F7, 7), CHVT(Key_F8, 8), CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
CHVT(Key_F9, 9), CHVT(Key_F10, 10), CHVT(Key_F11, 11), CHVT(Key_F12, 12), CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
}; };
static const Button buttons[] = { static const Button buttons[] = {
{ ClkLtSymbol, 0, BTN_LEFT, setlayout, {.v = &layouts[0]} }, { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ ClkLtSymbol, 0, BTN_RIGHT, setlayout, {.v = &layouts[2]} }, { MODKEY, BTN_MIDDLE, togglefloating, {0} },
{ ClkTitle, 0, BTN_MIDDLE, zoom, {0} }, { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
{ 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} },
}; };

234
config.h
View file

@ -1,234 +0,0 @@
/* 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 int draw_minimal_borders = 1; /* merge adjacent borders */
/* 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 const char *cursor_theme = "Adwaita";
static const char cursor_size[] = "24"; /* Make sure it's a valid integer, otherwise things will break */
static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */
static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = {"monospace:size=12", "NotoSansM NFM SemBd"};
static const float rootcolor[] = COLOR(0x6495eded);
/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
static uint32_t colors[][3] = {
/* fg bg border */
[SchemeNorm] = { 0xDA651Dff, 0x000000c0, 0x176815ff },
[SchemeSel] = { 0xB33A25ff, 0x00000050, 0x8FC711ff },
[SchemeUrg] = { 0, 0, 0xffaaaaff },
};
enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };
/* tagging - TAGCOUNT must be no greater than 31 */
static char *tags[] = { "󰍪", "1", "2", "3", "", "", "\ueb11" };
/* logging */
static int log_level = WLR_ERROR;
/* Autostart */
static const char *const autostart[] = {
"wbg", "/home/stachel/.desktop/wallpaper", NULL,
"vesktop", NULL,
"foot", "--term", "xterm-256color", NULL,
"keepassxc", NULL,
NULL /* terminate */
};
/* 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 skipfocus isterm noswallow monitor */
{ "foot", NULL, 0, 0, 0, 1, 1, -1 }, /* make foot swallow clients that are not foot */
{ "KeePassXC", NULL, 1<<6, 0, 1, 0, 0, -1 },
{ "vesktop", NULL, 1, 0, 1, 0, 0, -1 },
{ "wev", NULL, 0, 1, 1, 0, 1, -1 },
};
/* layout(s) */
static const Layout layouts[] = {
/* symbol arrange function */
{ "|w|", btrtile },
{ "[]=", 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
* 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 */
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 */
{ NULL, 0.5f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
};
/* keyboard */
static const struct xkb_rule_names xkb_rules = {
/* can specify fields: rules, model, layout, variant, options */
/* example:
.options = "ctrl:nocaps",
*/
.options = NULL,
.layout = "de",
.variant = "nodeadkeys"
};
static const int repeat_rate = -1;
static const int repeat_delay = 0;
/* 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_FLAT;
static const double accel_speed = -0.75;
/* 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;
static const int cursor_timeout = 1;
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_LOGO
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_SHIFT, KEY, tag, {.ui = 1 << TAG} }, \
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,KEY,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", "--term", "xterm-256color", NULL };
static const char *menucmd[] = { "mew-run", NULL };
static const char *upvol[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+1%", NULL };
static const char *downvol[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-1%", NULL };
//static const char *mutevol[] = { "amixer", "set", "Master", "toggle", NULL };
static const char *playpause[] = { "playerctl", "play-pause", NULL };
static const char *nextTrack[] = { "playerctl", "next", NULL };
static const char *prevTrack[] = { "playerctl", "previous", NULL };
#include "keys.h"
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, Key_d, spawn, {.v = menucmd} },
{ MODKEY, Key_Return, spawn, {.v = termcmd} },
{ 0, Key_XF86AudioRaiseVolume, spawn, {.v = upvol } },
{ 0, Key_XF86AudioLowerVolume, spawn, {.v = downvol } },
//{ 0, Key_XF86AudioMute, spawn, {.v = mutevol } },
{ 0, Key_XF86AudioPlay, spawn, {.v = playpause } },
{ 0, Key_XF86AudioNext, spawn, {.v = nextTrack } },
{ 0, Key_XF86AudioPrev, spawn, {.v = prevTrack } },
{ MODKEY, Key_s, spawn, SHCMD("wl-ss") },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_s, spawn, SHCMD("wl-fullss") },
{ MODKEY, Key_b, togglebar, {0} },
{ MODKEY, Key_r, regions, SHCMD("grim -g \"$(slurp)\"") },
{ MODKEY, Key_j, focusstack, {.i = +1} },
{ MODKEY, Key_k, focusstack, {.i = -1} },
{ MODKEY, Key_o, incnmaster, {.i = +1} },
{ MODKEY, Key_p, incnmaster, {.i = -1} },
{ MODKEY, Key_h, setmfact, {.f = -0.05f} },
{ MODKEY, Key_l, setmfact, {.f = +0.05f} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_Return, swapclients, {0} },
{ MODKEY|WLR_MODIFIER_CTRL, Key_Return, zoom, {0} },
{ MODKEY, Key_Tab, view, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_q, killclient, {0} },
{ MODKEY, Key_w, setlayout, {.v = &layouts[0]} },
{ MODKEY, Key_t, setlayout, {.v = &layouts[1]} },
//{ MODKEY, Key_f, setlayout, {.v = &layouts[2]} },
//{ MODKEY, Key_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_space, togglefloating, {0} },
{ MODKEY, Key_e, togglefullscreen, {0} },
{ MODKEY, Key_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, Key_0, tag, {.ui = ~0} },
//{ MODKEY, Key_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
//{ MODKEY, Key_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
//{ MODKEY|WLR_MODIFIER_SHIFT, Key_comma, tagmon, {.i = WLR_DIRECTION_LEFT} },
//{ MODKEY|WLR_MODIFIER_SHIFT, Key_period, tagmon, {.i = WLR_DIRECTION_RIGHT} },
{ MODKEY|WLR_MODIFIER_CTRL, Key_Right, setratio_h, {.f = +0.025f} },
{ MODKEY|WLR_MODIFIER_CTRL, Key_Left, setratio_h, {.f = -0.025f} },
{ MODKEY|WLR_MODIFIER_CTRL, Key_Up, setratio_v, {.f = -0.025f} },
{ MODKEY|WLR_MODIFIER_CTRL, Key_Down, setratio_v, {.f = +0.025f} },
TAGKEYS( Key_grave, 0),
TAGKEYS( Key_1, 1),
TAGKEYS( Key_2, 2),
TAGKEYS( Key_3, 3),
TAGKEYS( Key_4, 4),
TAGKEYS( Key_5, 5),
TAGKEYS( Key_6, 6),
{ MODKEY|WLR_MODIFIER_SHIFT, Key_e, quit, {0} },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,Key_BackSpace, quit, {0} },
#define CHVT(KEY,n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT, KEY, chvt, {.ui = (n)} }
/* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
* do not remove them.
*/
CHVT(Key_F1, 1), CHVT(Key_F2, 2), CHVT(Key_F3, 3), CHVT(Key_F4, 4),
CHVT(Key_F5, 5), CHVT(Key_F6, 6), CHVT(Key_F7, 7), CHVT(Key_F8, 8),
CHVT(Key_F9, 9), CHVT(Key_F10, 10), CHVT(Key_F11, 11), CHVT(Key_F12, 12),
};
static const Button buttons[] = {
//{ ClkLtSymbol, 0, BTN_LEFT, setlayout, {.v = &layouts[0]} },
//{ ClkLtSymbol, 0, BTN_RIGHT, setlayout, {.v = &layouts[1]} },
//{ 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 _VERSION = 0.8-dev
VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)` VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)`
PKG_CONFIG = pkg-config PKG_CONFIG = pkg-config
@ -8,11 +8,29 @@ PREFIX = /usr/local
MANDIR = $(PREFIX)/share/man MANDIR = $(PREFIX)/share/man
DATADIR = $(PREFIX)/share DATADIR = $(PREFIX)/share
WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.19`
WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19`
# Allow using an alternative wlroots installations
# This has to have all the includes required by wlroots, e.g:
# Assuming wlroots git repo is "${PWD}/wlroots" and you only ran "meson setup build && ninja -C build"
#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
# -I$(PWD)/wlroots/include
# Set -rpath to avoid using the wrong library.
#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19
# Assuming you ran "meson setup --prefix ${PWD}/0.19 build && ninja -C build install"
#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
# -I$(PWD)/wlroots/0.19/include/wlroots-0.19
#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.19/lib64 -L$(PWD)/wlroots/0.19/lib64 -lwlroots-0.19
XWAYLAND = XWAYLAND =
XLIBS = XLIBS =
# Uncomment to build XWayland support # Uncomment to build XWayland support
XWAYLAND = -DXWAYLAND #XWAYLAND = -DXWAYLAND
XLIBS = xcb xcb-icccm #XLIBS = xcb xcb-icccm
CC = gcc
CFLAGS = -O3 -march=native -Wall -Wextra -flto -s # dwl itself only uses C99 features, but wlroots' headers use anonymous unions (C11).
LDFLAGS += -flto # To avoid warnings about them, we do not use -std=c99 and instead of using the
# gmake default 'CC=c99', we use cc.
CC = cc

311
drwl.h
View file

@ -1,311 +0,0 @@
/*
* 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();
}

1564
dwl.c

File diff suppressed because it is too large Load diff

View file

@ -1,95 +0,0 @@
/******************************************************************
* Copyright 2023-2024 Leonardo Hernández Hernández
*
* 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
* OPEN GROUP 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.
*
******************************************************************/
/* cc -lxkbcommon -o generate-keys generate-keys.c */
#define _DEFAULT_SOURCE
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>
#include <xkbcommon/xkbcommon.h>
int
main(void)
{
/* Allow generate keys with a different layout and variant.
* You can also use XKB_DEFAULT_* environmental variables and let this as is */
struct xkb_rule_names rules = {
0
};
struct xkb_context *context = NULL;
struct xkb_keymap *keymap = NULL;
xkb_keycode_t keycode, min_keycode, max_keycode;
xkb_layout_index_t layout, num_layouts;
xkb_level_index_t level, num_levels;
int i, nsyms;
const xkb_keysym_t *syms;
char keyname[64];
bool ok = false;
FILE *file = fopen("keys.h", "w");
if (!file) {
perror("Couldn't open keys.h");
return EXIT_FAILURE;
}
if (!(context = xkb_context_new(XKB_CONTEXT_NO_FLAGS))) {
fputs("Couldn't create xkbcommon context\n", stderr);
goto out;
}
if (!(keymap = xkb_keymap_new_from_names(context, &rules,
XKB_KEYMAP_COMPILE_NO_FLAGS))) {
fputs("Couldn't create xkbcommon keymap\n", stderr);
goto out;
}
min_keycode = xkb_keymap_min_keycode(keymap);
max_keycode = xkb_keymap_max_keycode(keymap);
for (keycode = min_keycode; keycode <= max_keycode; keycode++) {
num_layouts = xkb_keymap_num_layouts_for_key(keymap, keycode);
for (layout = 0; layout < num_layouts; layout++) {
num_levels = xkb_keymap_num_levels_for_key(keymap, keycode, layout);
for (level = 0; level < num_levels; level++) {
nsyms = xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms);
for (i = 0; i < nsyms; i++) {
xkb_keysym_get_name(syms[i], keyname, sizeof(keyname) / sizeof(keyname[0]));
fprintf(file, "#define Key_%-27s %#05"PRIx32"\n", keyname, keycode);
}
}
}
}
ok = true;
sync();
out:
fclose(file);
xkb_keymap_unref(keymap);
xkb_context_unref(context);
return !ok;
}

514
keys.h
View file

@ -1,514 +0,0 @@
/* You can use the macros within this file
* instead of search the keycodes yourself
* with wev or something like that
* You probably are also searching these:
* Key_XF86AudioMute
* Key_XF86AudioLowerVolume
* Key_XF86AudioRaiseVolume
* Key_XF86MonBrightnessDown
* Key_XF86MonBrightnessUp
*/
#define Key_Escape 0x009
#define Key_1 0x00a
#define Key_exclam 0x00a
#define Key_2 0x00b
#define Key_at 0x00b
#define Key_3 0x00c
#define Key_numbersign 0x00c
#define Key_4 0x00d
#define Key_dollar 0x00d
#define Key_5 0x00e
#define Key_percent 0x00e
#define Key_6 0x00f
#define Key_asciicircum 0x00f
#define Key_7 0x010
#define Key_ampersand 0x010
#define Key_8 0x011
#define Key_asterisk 0x011
#define Key_9 0x012
#define Key_parenleft 0x012
#define Key_0 0x013
#define Key_parenright 0x013
#define Key_minus 0x014
#define Key_underscore 0x014
#define Key_equal 0x015
#define Key_plus 0x015
#define Key_BackSpace 0x016
#define Key_Tab 0x017
#define Key_ISO_Left_Tab 0x017
#define Key_q 0x018
#define Key_Q 0x018
#define Key_w 0x019
#define Key_W 0x019
#define Key_e 0x01a
#define Key_E 0x01a
#define Key_r 0x01b
#define Key_R 0x01b
#define Key_t 0x01c
#define Key_T 0x01c
#define Key_y 0x01d
#define Key_Y 0x01d
#define Key_u 0x01e
#define Key_U 0x01e
#define Key_i 0x01f
#define Key_I 0x01f
#define Key_o 0x020
#define Key_O 0x020
#define Key_p 0x021
#define Key_P 0x021
#define Key_bracketleft 0x022
#define Key_braceleft 0x022
#define Key_bracketright 0x023
#define Key_braceright 0x023
#define Key_Return 0x024
#define Key_Control_L 0x025
#define Key_a 0x026
#define Key_A 0x026
#define Key_s 0x027
#define Key_S 0x027
#define Key_d 0x028
#define Key_D 0x028
#define Key_f 0x029
#define Key_F 0x029
#define Key_g 0x02a
#define Key_G 0x02a
#define Key_h 0x02b
#define Key_H 0x02b
#define Key_j 0x02c
#define Key_J 0x02c
#define Key_k 0x02d
#define Key_K 0x02d
#define Key_l 0x02e
#define Key_L 0x02e
#define Key_semicolon 0x02f
#define Key_colon 0x02f
#define Key_apostrophe 0x030
#define Key_quotedbl 0x030
#define Key_grave 0x031
#define Key_asciitilde 0x031
#define Key_Shift_L 0x032
#define Key_backslash 0x033
#define Key_bar 0x033
#define Key_z 0x034
#define Key_Z 0x034
#define Key_x 0x035
#define Key_X 0x035
#define Key_c 0x036
#define Key_C 0x036
#define Key_v 0x037
#define Key_V 0x037
#define Key_b 0x038
#define Key_B 0x038
#define Key_n 0x039
#define Key_N 0x039
#define Key_m 0x03a
#define Key_M 0x03a
#define Key_comma 0x03b
#define Key_less 0x03b
#define Key_period 0x03c
#define Key_greater 0x03c
#define Key_slash 0x03d
#define Key_question 0x03d
#define Key_Shift_R 0x03e
#define Key_KP_Multiply 0x03f
#define Key_XF86ClearGrab 0x03f
#define Key_Alt_L 0x040
#define Key_Meta_L 0x040
#define Key_space 0x041
#define Key_Caps_Lock 0x042
#define Key_F1 0x043
#define Key_XF86Switch_VT_1 0x043
#define Key_F2 0x044
#define Key_XF86Switch_VT_2 0x044
#define Key_F3 0x045
#define Key_XF86Switch_VT_3 0x045
#define Key_F4 0x046
#define Key_XF86Switch_VT_4 0x046
#define Key_F5 0x047
#define Key_XF86Switch_VT_5 0x047
#define Key_F6 0x048
#define Key_XF86Switch_VT_6 0x048
#define Key_F7 0x049
#define Key_XF86Switch_VT_7 0x049
#define Key_F8 0x04a
#define Key_XF86Switch_VT_8 0x04a
#define Key_F9 0x04b
#define Key_XF86Switch_VT_9 0x04b
#define Key_F10 0x04c
#define Key_XF86Switch_VT_10 0x04c
#define Key_Num_Lock 0x04d
#define Key_Scroll_Lock 0x04e
#define Key_KP_Home 0x04f
#define Key_KP_7 0x04f
#define Key_KP_Up 0x050
#define Key_KP_8 0x050
#define Key_KP_Prior 0x051
#define Key_KP_9 0x051
#define Key_KP_Subtract 0x052
#define Key_XF86Prev_VMode 0x052
#define Key_KP_Left 0x053
#define Key_KP_4 0x053
#define Key_KP_Begin 0x054
#define Key_KP_5 0x054
#define Key_KP_Right 0x055
#define Key_KP_6 0x055
#define Key_KP_Add 0x056
#define Key_XF86Next_VMode 0x056
#define Key_KP_End 0x057
#define Key_KP_1 0x057
#define Key_KP_Down 0x058
#define Key_KP_2 0x058
#define Key_KP_Next 0x059
#define Key_KP_3 0x059
#define Key_KP_Insert 0x05a
#define Key_KP_0 0x05a
#define Key_KP_Delete 0x05b
#define Key_KP_Decimal 0x05b
#define Key_ISO_Level3_Shift 0x05c
#define Key_less2 0x05e
#define Key_greater2 0x05e
#define Key_bar2 0x05e
#define Key_brokenbar 0x05e
#define Key_F11 0x05f
#define Key_XF86Switch_VT_11 0x05f
#define Key_F12 0x060
#define Key_XF86Switch_VT_12 0x060
#define Key_Katakana 0x062
#define Key_Hiragana 0x063
#define Key_Henkan_Mode 0x064
#define Key_Hiragana_Katakana 0x065
#define Key_Muhenkan 0x066
#define Key_KP_Enter 0x068
#define Key_Control_R 0x069
#define Key_KP_Divide 0x06a
#define Key_XF86Ungrab 0x06a
#define Key_Print 0x06b
#define Key_Sys_Req 0x06b
#define Key_Alt_R 0x06c
#define Key_Meta_R 0x06c
#define Key_Linefeed 0x06d
#define Key_Home 0x06e
#define Key_Up 0x06f
#define Key_Prior 0x070
#define Key_Left 0x071
#define Key_Right 0x072
#define Key_End 0x073
#define Key_Down 0x074
#define Key_Next 0x075
#define Key_Insert 0x076
#define Key_Delete 0x077
#define Key_XF86AudioMute 0x079
#define Key_XF86AudioLowerVolume 0x07a
#define Key_XF86AudioRaiseVolume 0x07b
#define Key_XF86PowerOff 0x07c
#define Key_KP_Equal 0x07d
#define Key_plusminus 0x07e
#define Key_Pause 0x07f
#define Key_Break 0x07f
#define Key_XF86LaunchA 0x080
#define Key_KP_Decimal2 0x081
#define Key_Hangul 0x082
#define Key_Hangul_Hanja 0x083
#define Key_Super_L 0x085
#define Key_Super_R 0x086
#define Key_Menu 0x087
#define Key_Cancel 0x088
#define Key_Redo 0x089
#define Key_SunProps 0x08a
#define Key_Undo 0x08b
#define Key_SunFront 0x08c
#define Key_XF86Copy 0x08d
#define Key_XF86Open 0x08e
#define Key_XF86Paste 0x08f
#define Key_Find 0x090
#define Key_XF86Cut 0x091
#define Key_Help 0x092
#define Key_XF86MenuKB 0x093
#define Key_XF86Calculator 0x094
#define Key_XF86Sleep 0x096
#define Key_XF86WakeUp 0x097
#define Key_XF86Explorer 0x098
#define Key_XF86Send 0x099
#define Key_XF86Xfer 0x09b
#define Key_XF86Launch1 0x09c
#define Key_XF86Launch2 0x09d
#define Key_XF86WWW 0x09e
#define Key_XF86DOS 0x09f
#define Key_XF86ScreenSaver 0x0a0
#define Key_XF86RotateWindows 0x0a1
#define Key_XF86TaskPane 0x0a2
#define Key_XF86Mail 0x0a3
#define Key_XF86Favorites 0x0a4
#define Key_XF86MyComputer 0x0a5
#define Key_XF86Back 0x0a6
#define Key_XF86Forward 0x0a7
#define Key_XF86Eject 0x0a9
#define Key_XF86Eject2 0x0aa
#define Key_XF86AudioNext 0x0ab
#define Key_XF86AudioPlay 0x0ac
#define Key_XF86AudioPause 0x0ac
#define Key_XF86AudioPrev 0x0ad
#define Key_XF86AudioStop 0x0ae
#define Key_XF86Eject3 0x0ae
#define Key_XF86AudioRecord 0x0af
#define Key_XF86AudioRewind 0x0b0
#define Key_XF86Phone 0x0b1
#define Key_XF86Tools 0x0b3
#define Key_XF86HomePage 0x0b4
#define Key_XF86Reload 0x0b5
#define Key_XF86Close 0x0b6
#define Key_XF86ScrollUp 0x0b9
#define Key_XF86ScrollDown 0x0ba
#define Key_parenleft2 0x0bb
#define Key_parenright2 0x0bc
#define Key_XF86New 0x0bd
#define Key_Redo2 0x0be
#define Key_XF86Tools2 0x0bf
#define Key_XF86Launch5 0x0c0
#define Key_XF86Launch6 0x0c1
#define Key_XF86Launch7 0x0c2
#define Key_XF86Launch8 0x0c3
#define Key_XF86Launch9 0x0c4
#define Key_XF86AudioMicMute 0x0c6
#define Key_XF86TouchpadToggle 0x0c7
#define Key_XF86TouchpadOn 0x0c8
#define Key_XF86TouchpadOff 0x0c9
#define Key_ISO_Level5_Shift 0x0cb
#define Key_Alt_L2 0x0cc
#define Key_Meta_L2 0x0cd
#define Key_Super_L2 0x0ce
#define Key_Hyper_L 0x0cf
#define Key_XF86AudioPlay2 0x0d0
#define Key_XF86AudioPause2 0x0d1
#define Key_XF86Launch3 0x0d2
#define Key_XF86Launch4 0x0d3
#define Key_XF86LaunchB 0x0d4
#define Key_XF86Suspend 0x0d5
#define Key_XF86Close2 0x0d6
#define Key_XF86AudioPlay3 0x0d7
#define Key_XF86AudioForward 0x0d8
#define Key_Print2 0x0da
#define Key_XF86WebCam 0x0dc
#define Key_XF86AudioPreset 0x0dd
#define Key_XF86Mail2 0x0df
#define Key_XF86Messenger 0x0e0
#define Key_XF86Search 0x0e1
#define Key_XF86Go 0x0e2
#define Key_XF86Finance 0x0e3
#define Key_XF86Game 0x0e4
#define Key_XF86Shop 0x0e5
#define Key_Cancel2 0x0e7
#define Key_XF86MonBrightnessDown 0x0e8
#define Key_XF86MonBrightnessUp 0x0e9
#define Key_XF86AudioMedia 0x0ea
#define Key_XF86Display 0x0eb
#define Key_XF86KbdLightOnOff 0x0ec
#define Key_XF86KbdBrightnessDown 0x0ed
#define Key_XF86KbdBrightnessUp 0x0ee
#define Key_XF86Send2 0x0ef
#define Key_XF86Reply 0x0f0
#define Key_XF86MailForward 0x0f1
#define Key_XF86Save 0x0f2
#define Key_XF86Documents 0x0f3
#define Key_XF86Battery 0x0f4
#define Key_XF86Bluetooth 0x0f5
#define Key_XF86WLAN 0x0f6
#define Key_XF86UWB 0x0f7
#define Key_XF86Next_VMode2 0x0f9
#define Key_XF86Prev_VMode2 0x0fa
#define Key_XF86MonBrightnessCycle 0x0fb
#define Key_XF86BrightnessAuto 0x0fc
#define Key_XF86DisplayOff 0x0fd
#define Key_XF86WWAN 0x0fe
#define Key_XF86RFKill 0x0ff
#define Key_XF86AudioMicMute2 0x100
#define Key_XF86Info 0x16e
#define Key_XF86Favorites2 0x174
#define Key_XF86CycleAngle 0x17b
#define Key_XF86FullScreen 0x17c
#define Key_XF86Keyboard 0x17e
#define Key_XF86AspectRatio 0x17f
#define Key_XF86DVD 0x18d
#define Key_XF86Audio 0x190
#define Key_XF86Video 0x191
#define Key_XF86Calendar 0x195
#define Key_XF86ChannelUp 0x19a
#define Key_XF86ChannelDown 0x19b
#define Key_XF86AudioRandomPlay 0x1a2
#define Key_XF86Break 0x1a3
#define Key_XF86VideoPhone 0x1a8
#define Key_XF86Game2 0x1a9
#define Key_XF86ZoomIn 0x1aa
#define Key_XF86ZoomOut 0x1ab
#define Key_XF86ZoomReset 0x1ac
#define Key_XF86Word 0x1ad
#define Key_XF86Editor 0x1ae
#define Key_XF86Excel 0x1af
#define Key_XF86GraphicsEditor 0x1b0
#define Key_XF86Presentation 0x1b1
#define Key_XF86Database 0x1b2
#define Key_XF86News 0x1b3
#define Key_XF86Voicemail 0x1b4
#define Key_XF86Addressbook 0x1b5
#define Key_XF86Messenger2 0x1b6
#define Key_XF86DisplayToggle 0x1b7
#define Key_XF86SpellCheck 0x1b8
#define Key_XF86LogOff 0x1b9
#define Key_dollar2 0x1ba
#define Key_EuroSign 0x1bb
#define Key_XF86FrameBack 0x1bc
#define Key_XF86FrameForward 0x1bd
#define Key_XF86ContextMenu 0x1be
#define Key_XF86MediaRepeat 0x1bf
#define Key_XF8610ChannelsUp 0x1c0
#define Key_XF8610ChannelsDown 0x1c1
#define Key_XF86Images 0x1c2
#define Key_XF86NotificationCenter 0x1c4
#define Key_XF86PickupPhone 0x1c5
#define Key_XF86HangupPhone 0x1c6
#define Key_XF86Fn 0x1d8
#define Key_XF86Fn_Esc 0x1d9
#define Key_XF86FnRightShift 0x1ed
#define Key_braille_dot_1 0x1f9
#define Key_braille_dot_2 0x1fa
#define Key_braille_dot_3 0x1fb
#define Key_braille_dot_4 0x1fc
#define Key_braille_dot_5 0x1fd
#define Key_braille_dot_6 0x1fe
#define Key_braille_dot_7 0x1ff
#define Key_braille_dot_8 0x200
#define Key_braille_dot_9 0x201
#define Key_braille_dot_1_2 0x202
#define Key_XF86Numeric0 0x208
#define Key_XF86Numeric1 0x209
#define Key_XF86Numeric2 0x20a
#define Key_XF86Numeric3 0x20b
#define Key_XF86Numeric4 0x20c
#define Key_XF86Numeric5 0x20d
#define Key_XF86Numeric6 0x20e
#define Key_XF86Numeric7 0x20f
#define Key_XF86Numeric8 0x210
#define Key_XF86Numeric9 0x211
#define Key_XF86NumericStar 0x212
#define Key_XF86NumericPound 0x213
#define Key_XF86NumericA 0x214
#define Key_XF86NumericB 0x215
#define Key_XF86NumericC 0x216
#define Key_XF86NumericD 0x217
#define Key_XF86CameraFocus 0x218
#define Key_XF86WPSButton 0x219
#define Key_XF86TouchpadToggle2 0x21a
#define Key_XF86TouchpadOn2 0x21b
#define Key_XF86TouchpadOff2 0x21c
#define Key_XF86CameraZoomIn 0x21d
#define Key_XF86CameraZoomOut 0x21e
#define Key_XF86CameraUp 0x21f
#define Key_XF86CameraDown 0x220
#define Key_XF86CameraLeft 0x221
#define Key_XF86CameraRight 0x222
#define Key_XF86AttendantOn 0x223
#define Key_XF86AttendantOff 0x224
#define Key_XF86AttendantToggle 0x225
#define Key_XF86LightsToggle 0x226
#define Key_XF86ALSToggle 0x238
#define Key_XF86RotationLockToggle 0x239
#define Key_XF86Buttonconfig 0x248
#define Key_XF86Taskmanager 0x249
#define Key_XF86Journal 0x24a
#define Key_XF86ControlPanel 0x24b
#define Key_XF86AppSelect 0x24c
#define Key_XF86Screensaver 0x24d
#define Key_XF86VoiceCommand 0x24e
#define Key_XF86Assistant 0x24f
#define Key_ISO_Next_Group 0x250
#define Key_XF86EmojiPicker 0x251
#define Key_XF86Dictate 0x252
#define Key_XF86CameraAccessEnable 0x253
#define Key_XF86CameraAccessDisable 0x254
#define Key_XF86CameraAccessToggle 0x255
#define Key_XF86BrightnessMin 0x258
#define Key_XF86BrightnessMax 0x259
#define Key_XF86KbdInputAssistPrev 0x268
#define Key_XF86KbdInputAssistNext 0x269
#define Key_XF86KbdInputAssistPrevgroup 0x26a
#define Key_XF86KbdInputAssistNextgroup 0x26b
#define Key_XF86KbdInputAssistAccept 0x26c
#define Key_XF86KbdInputAssistCancel 0x26d
#define Key_XF86RightUp 0x26e
#define Key_XF86RightDown 0x26f
#define Key_XF86LeftUp 0x270
#define Key_XF86LeftDown 0x271
#define Key_XF86RootMenu 0x272
#define Key_XF86MediaTopMenu 0x273
#define Key_XF86Numeric11 0x274
#define Key_XF86Numeric12 0x275
#define Key_XF86AudioDesc 0x276
#define Key_XF863DMode 0x277
#define Key_XF86NextFavorite 0x278
#define Key_XF86StopRecord 0x279
#define Key_XF86PauseRecord 0x27a
#define Key_XF86VOD 0x27b
#define Key_XF86Unmute 0x27c
#define Key_XF86FastReverse 0x27d
#define Key_XF86SlowReverse 0x27e
#define Key_XF86Data 0x27f
#define Key_XF86OnScreenKeyboard 0x280
#define Key_XF86PrivacyScreenToggle 0x281
#define Key_XF86SelectiveScreenshot 0x282
#define Key_XF86NextElement 0x283
#define Key_XF86PreviousElement 0x284
#define Key_XF86AutopilotEngageToggle 0x285
#define Key_XF86MarkWaypoint 0x286
#define Key_XF86Sos 0x287
#define Key_XF86NavChart 0x288
#define Key_XF86FishingChart 0x289
#define Key_XF86SingleRangeRadar 0x28a
#define Key_XF86DualRangeRadar 0x28b
#define Key_XF86RadarOverlay 0x28c
#define Key_XF86TraditionalSonar 0x28d
#define Key_XF86ClearvuSonar 0x28e
#define Key_XF86SidevuSonar 0x28f
#define Key_XF86NavInfo 0x290
#define Key_XF86BrightnessAdjust 0x291
#define Key_XF86Macro1 0x298
#define Key_XF86Macro2 0x299
#define Key_XF86Macro3 0x29a
#define Key_XF86Macro4 0x29b
#define Key_XF86Macro5 0x29c
#define Key_XF86Macro6 0x29d
#define Key_XF86Macro7 0x29e
#define Key_XF86Macro8 0x29f
#define Key_XF86Macro9 0x2a0
#define Key_XF86Macro10 0x2a1
#define Key_XF86Macro11 0x2a2
#define Key_XF86Macro12 0x2a3
#define Key_XF86Macro13 0x2a4
#define Key_XF86Macro14 0x2a5
#define Key_XF86Macro15 0x2a6
#define Key_XF86Macro16 0x2a7
#define Key_XF86Macro17 0x2a8
#define Key_XF86Macro18 0x2a9
#define Key_XF86Macro19 0x2aa
#define Key_XF86Macro20 0x2ab
#define Key_XF86Macro21 0x2ac
#define Key_XF86Macro22 0x2ad
#define Key_XF86Macro23 0x2ae
#define Key_XF86Macro24 0x2af
#define Key_XF86Macro25 0x2b0
#define Key_XF86Macro26 0x2b1
#define Key_XF86Macro27 0x2b2
#define Key_XF86Macro28 0x2b3
#define Key_XF86Macro29 0x2b4
#define Key_XF86Macro30 0x2b5
#define Key_XF86MacroRecordStart 0x2b8
#define Key_XF86MacroRecordStop 0x2b9
#define Key_XF86MacroPresetCycle 0x2ba
#define Key_XF86MacroPreset1 0x2bb
#define Key_XF86MacroPreset2 0x2bc
#define Key_XF86MacroPreset3 0x2bd
#define Key_XF86KbdLcdMenu1 0x2c0
#define Key_XF86KbdLcdMenu2 0x2c1
#define Key_XF86KbdLcdMenu3 0x2c2
#define Key_XF86KbdLcdMenu4 0x2c3
#define Key_XF86KbdLcdMenu5 0x2c4

View file

@ -1,903 +0,0 @@
From b9789420f166c20579f29ecd171a8956c681848d Mon Sep 17 00:00:00 2001
From: julmajustus <julmajustus@tutanota.com>
Date: Thu, 13 Feb 2025 23:23:40 +0200
Subject: [PATCH] btrtile with multi-tag support
---
btrtile.c | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++
config.def.h | 12 ++
dwl.c | 152 +++++++++++---
3 files changed, 698 insertions(+), 29 deletions(-)
create mode 100644 btrtile.c
diff --git a/btrtile.c b/btrtile.c
new file mode 100644
index 0000000..03f4680
--- /dev/null
+++ b/btrtile.c
@@ -0,0 +1,563 @@
+/* ************************************************************************** */
+/* */
+/* ::: :::::::: */
+/* btrtile.c :+: :+: :+: */
+/* +:+ +:+ +:+ */
+/* By: jmakkone <jmakkone@student.hive.fi> +#+ +:+ +#+ */
+/* +#+#+#+#+#+ +#+ */
+/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */
+/* Updated: 2025/02/13 23:22:33 by jmakkone ### ########.fr */
+/* */
+/* ************************************************************************** */
+
+typedef struct LayoutNode {
+ unsigned int is_client_node;
+ unsigned int is_split_vertically;
+ float split_ratio;
+ struct LayoutNode *left;
+ struct LayoutNode *right;
+ struct LayoutNode *split_node;
+ Client *client;
+} LayoutNode;
+
+static void apply_layout(Monitor *m, LayoutNode *node,
+ struct wlr_box area, unsigned int is_root);
+static void btrtile(Monitor *m);
+static LayoutNode *create_client_node(Client *c);
+static LayoutNode *create_split_node(unsigned int is_split_vertically,
+ LayoutNode *left, LayoutNode *right);
+static void destroy_node(LayoutNode *node);
+static void destroy_tree(Monitor *m);
+static LayoutNode *find_client_node(LayoutNode *node, Client *c);
+static LayoutNode *find_suitable_split(LayoutNode *start, unsigned int need_vert);
+static void init_tree(Monitor *m);
+static void insert_client(Monitor *m, Client *focused_client, Client *new_client);
+static LayoutNode *remove_client_node(LayoutNode *node, Client *c);
+static void remove_client(Monitor *m, Client *c);
+static void setratio_h(const Arg *arg);
+static void setratio_v(const Arg *arg);
+static void swapclients(const Arg *arg);
+static unsigned int visible_count(LayoutNode *node, Monitor *m);
+static Client *xytoclient(double x, double y);
+
+static int resizing_from_mouse = 0;
+static double resize_last_update_x, resize_last_update_y;
+static uint32_t last_resize_time = 0;
+
+void
+apply_layout(Monitor *m, LayoutNode *node,
+ struct wlr_box area, unsigned int is_root)
+{
+ Client *c;
+ float ratio;
+ unsigned int left_count, right_count, mid;
+ struct wlr_box left_area, right_area;
+
+ if (!node)
+ return;
+
+ /* If this node is a client node, check if it is visible. */
+ if (node->is_client_node) {
+ c = node->client;
+ if (!c || !VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ return;
+ resize(c, area, 0);
+ c->old_geom = area;
+ return;
+ }
+
+ /* For a split node, we see how many visible children are on each side: */
+ left_count = visible_count(node->left, m);
+ right_count = visible_count(node->right, m);
+
+ if (left_count == 0 && right_count == 0) {
+ return;
+ } else if (left_count > 0 && right_count == 0) {
+ apply_layout(m, node->left, area, 0);
+ return;
+ } else if (left_count == 0 && right_count > 0) {
+ apply_layout(m, node->right, area, 0);
+ return;
+ }
+
+ /* If were here, we have visible clients in both subtrees. */
+ ratio = node->split_ratio;
+ if (ratio < 0.05f)
+ ratio = 0.05f;
+ if (ratio > 0.95f)
+ ratio = 0.95f;
+
+ memset(&left_area, 0, sizeof(left_area));
+ memset(&right_area, 0, sizeof(right_area));
+
+ if (node->is_split_vertically) {
+ mid = (unsigned int)(area.width * ratio);
+ left_area.x = area.x;
+ left_area.y = area.y;
+ left_area.width = mid;
+ left_area.height = area.height;
+
+ right_area.x = area.x + mid;
+ right_area.y = area.y;
+ right_area.width = area.width - mid;
+ right_area.height = area.height;
+ } else {
+ /* horizontal split */
+ mid = (unsigned int)(area.height * ratio);
+ left_area.x = area.x;
+ left_area.y = area.y;
+ left_area.width = area.width;
+ left_area.height = mid;
+
+ right_area.x = area.x;
+ right_area.y = area.y + mid;
+ right_area.width = area.width;
+ right_area.height= area.height - mid;
+ }
+
+ apply_layout(m, node->left, left_area, 0);
+ apply_layout(m, node->right, right_area, 0);
+}
+
+void
+btrtile(Monitor *m)
+{
+ Client *c, *focused = NULL;
+ int n = 0;
+ LayoutNode *found;
+ struct wlr_box full_area;
+
+ if (!m || !m->root)
+ return;
+
+ /* Remove non tiled clients from tree. */
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon == m && !c->isfloating && !c->isfullscreen) {
+ } else {
+ remove_client(m, c);
+ }
+ }
+
+ /* If no client is found under cursor, fallback to focustop(m) */
+ if (!(focused = xytoclient(cursor->x, cursor->y)))
+ focused = focustop(m);
+
+ /* Insert visible clients that are not part of the tree. */
+ wl_list_for_each(c, &clients, link) {
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && c->mon == m) {
+ found = find_client_node(m->root, c);
+ if (!found) {
+ insert_client(m, focused, c);
+ }
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return;
+
+ full_area = m->w;
+ apply_layout(m, m->root, full_area, 1);
+}
+
+LayoutNode *
+create_client_node(Client *c)
+{
+ LayoutNode *node = calloc(1, sizeof(LayoutNode));
+
+ if (!node)
+ return NULL;
+ node->is_client_node = 1;
+ node->split_ratio = 0.5f;
+ node->client = c;
+ return node;
+}
+
+LayoutNode *
+create_split_node(unsigned int is_split_vertically,
+ LayoutNode *left, LayoutNode *right)
+{
+ LayoutNode *node = calloc(1, sizeof(LayoutNode));
+
+ if (!node)
+ return NULL;
+ node->is_client_node = 0;
+ node->split_ratio = 0.5f;
+ node->is_split_vertically = is_split_vertically;
+ node->left = left;
+ node->right = right;
+ if (left)
+ left->split_node = node;
+ if (right)
+ right->split_node = node;
+ return node;
+}
+
+void
+destroy_node(LayoutNode *node)
+{
+ if (!node)
+ return;
+ if (!node->is_client_node) {
+ destroy_node(node->left);
+ destroy_node(node->right);
+ }
+ free(node);
+}
+
+void
+destroy_tree(Monitor *m)
+{
+ if (!m || !m->root)
+ return;
+ destroy_node(m->root);
+ m->root = NULL;
+}
+
+LayoutNode *
+find_client_node(LayoutNode *node, Client *c)
+{
+ LayoutNode *res;
+
+ if (!node || !c)
+ return NULL;
+ if (node->is_client_node) {
+ return (node->client == c) ? node : NULL;
+ }
+ res = find_client_node(node->left, c);
+ return res ? res : find_client_node(node->right, c);
+}
+
+LayoutNode *
+find_suitable_split(LayoutNode *start_node, unsigned int need_vertical)
+{
+ LayoutNode *n = start_node;
+ /* if we started from a client node, jump to its parent: */
+ if (n && n->is_client_node)
+ n = n->split_node;
+
+ while (n) {
+ if (!n->is_client_node && n->is_split_vertically == need_vertical &&
+ visible_count(n->left, selmon) > 0 && visible_count(n->right, selmon) > 0)
+ return n;
+ n = n->split_node;
+ }
+ return NULL;
+}
+
+void
+init_tree(Monitor *m)
+{
+ if (!m)
+ return;
+ m->root = calloc(1, sizeof(LayoutNode));
+ if (!m->root)
+ m->root = NULL;
+}
+
+void
+insert_client(Monitor *m, Client *focused_client, Client *new_client)
+{
+ Client *old_client;
+ LayoutNode **root = &m->root, *old_root,
+ *focused_node, *new_client_node, *old_client_node;
+ unsigned int wider, mid_x, mid_y;
+
+ /* If no root , new client becomes the root. */
+ if (!*root) {
+ *root = create_client_node(new_client);
+ return;
+ }
+
+ /* Find the focused_client node,
+ * if not found split the root. */
+ focused_node = focused_client ?
+ find_client_node(*root, focused_client) : NULL;
+ if (!focused_node) {
+ old_root = *root;
+ new_client_node = create_client_node(new_client);
+ *root = create_split_node(1, old_root, new_client_node);
+ return;
+ }
+
+ /* Turn focused node from a client node into a split node,
+ * and attach old_client + new_client. */
+ old_client = focused_node->client;
+ old_client_node = create_client_node(old_client);
+ new_client_node = create_client_node(new_client);
+
+ /* Decide split direction. */
+ wider = (focused_client->geom.width >= focused_client->geom.height);
+ focused_node->is_client_node = 0;
+ focused_node->client = NULL;
+ focused_node->is_split_vertically = (wider ? 1 : 0);
+
+ /* Pick new_client side depending on the cursor position. */
+ mid_x = focused_client->geom.x + focused_client->geom.width / 2;
+ mid_y = focused_client->geom.y + focused_client->geom.height / 2;
+
+ if (wider) {
+ /* vertical split => left vs right */
+ if (cursor->x <= mid_x) {
+ focused_node->left = new_client_node;
+ focused_node->right = old_client_node;
+ } else {
+ focused_node->left = old_client_node;
+ focused_node->right = new_client_node;
+ }
+ } else {
+ /* horizontal split => top vs bottom */
+ if (cursor->y <= mid_y) {
+ focused_node->left = new_client_node;
+ focused_node->right = old_client_node;
+ } else {
+ focused_node->left = old_client_node;
+ focused_node->right = new_client_node;
+ }
+ }
+ old_client_node->split_node = focused_node;
+ new_client_node->split_node = focused_node;
+ focused_node->split_ratio = 0.5f;
+}
+
+LayoutNode *
+remove_client_node(LayoutNode *node, Client *c)
+{
+ LayoutNode *tmp;
+ if (!node)
+ return NULL;
+ if (node->is_client_node) {
+ /* If this client_node is the client we're removing,
+ * return NULL to remove it */
+ if (node->client == c) {
+ free(node);
+ return NULL;
+ }
+ return node;
+ }
+
+ node->left = remove_client_node(node->left, c);
+ node->right = remove_client_node(node->right, c);
+
+ /* If one of the client node is NULL after removal and the other is not,
+ * we "lift" the other client node up to replace this split node. */
+ if (!node->left && node->right) {
+ tmp = node->right;
+
+ /* Save pointer to split node */
+ if (tmp)
+ tmp->split_node = node->split_node;
+
+ free(node);
+ return tmp;
+ }
+
+ if (!node->right && node->left) {
+ tmp = node->left;
+
+ /* Save pointer to split node */
+ if (tmp)
+ tmp->split_node = node->split_node;
+
+ free(node);
+ return tmp;
+ }
+
+ /* If both children exist or both are NULL (empty tree),
+ * return node as is. */
+ return node;
+}
+
+void
+remove_client(Monitor *m, Client *c)
+{
+ if (!m->root || !c)
+ return;
+ m->root = remove_client_node(m->root, c);
+}
+
+void
+setratio_h(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ LayoutNode *client_node, *split_node;
+ float new_ratio;
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ client_node = find_client_node(selmon->root, sel);
+ if (!client_node)
+ return;
+
+ split_node = find_suitable_split(client_node, 1);
+ if (!split_node)
+ return;
+
+ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
+ if (new_ratio < 0.05f)
+ new_ratio = 0.05f;
+ if (new_ratio > 0.95f)
+ new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio;
+
+ /* Skip the arrange if done resizing by mouse,
+ * we call arrange from motionotify */
+ if (!resizing_from_mouse) {
+ arrange(selmon);
+ }
+}
+
+void
+setratio_v(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ LayoutNode *client_node, *split_node;
+ float new_ratio;
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+ client_node = find_client_node(selmon->root, sel);
+ if (!client_node)
+ return;
+
+ split_node = find_suitable_split(client_node, 0);
+ if (!split_node)
+ return;
+
+ new_ratio = (arg->f != 0.0f) ? (split_node->split_ratio + arg->f) : 0.5f;
+ if (new_ratio < 0.05f)
+ new_ratio = 0.05f;
+ if (new_ratio > 0.95f)
+ new_ratio = 0.95f;
+ split_node->split_ratio = new_ratio;
+
+ /* Skip the arrange if done resizing by mouse,
+ * we call arrange from motionotify */
+ if (!resizing_from_mouse) {
+ arrange(selmon);
+ }
+}
+
+void swapclients(const Arg *arg) {
+ Client *c, *tmp, *target = NULL, *sel = focustop(selmon);
+ LayoutNode *sel_node, *target_node;
+ int closest_dist = INT_MAX, dist, sel_center_x, sel_center_y,
+ cand_center_x, cand_center_y;
+
+ if (!sel || sel->isfullscreen ||
+ !selmon->root || !selmon->lt[selmon->sellt]->arrange)
+ return;
+
+
+ /* Get the center coordinates of the selected client */
+ sel_center_x = sel->geom.x + sel->geom.width / 2;
+ sel_center_y = sel->geom.y + sel->geom.height / 2;
+
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen || c == sel)
+ continue;
+
+ /* Get the center of candidate client */
+ cand_center_x = c->geom.x + c->geom.width / 2;
+ cand_center_y = c->geom.y + c->geom.height / 2;
+
+ /* Check that the candidate lies in the requested direction. */
+ switch (arg->ui) {
+ case 0:
+ if (cand_center_x >= sel_center_x)
+ continue;
+ break;
+ case 1:
+ if (cand_center_x <= sel_center_x)
+ continue;
+ break;
+ case 2:
+ if (cand_center_y >= sel_center_y)
+ continue;
+ break;
+ case 3:
+ if (cand_center_y <= sel_center_y)
+ continue;
+ break;
+ default:
+ continue;
+ }
+
+ /* Get distance between the centers */
+ dist = abs(sel_center_x - cand_center_x) + abs(sel_center_y - cand_center_y);
+ if (dist < closest_dist) {
+ closest_dist = dist;
+ target = c;
+ }
+ }
+
+ /* If target is found, swap the two clients positions in the layout tree */
+ if (target) {
+ sel_node = find_client_node(selmon->root, sel);
+ target_node = find_client_node(selmon->root, target);
+ if (sel_node && target_node) {
+ tmp = sel_node->client;
+ sel_node->client = target_node->client;
+ target_node->client = tmp;
+ arrange(selmon);
+ }
+ }
+}
+
+unsigned int
+visible_count(LayoutNode *node, Monitor *m)
+{
+ Client *c;
+
+ if (!node)
+ return 0;
+ /* Check if this client is visible. */
+ if (node->is_client_node) {
+ c = node->client;
+ if (c && VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ return 1;
+ return 0;
+ }
+ /* Else its a split node. */
+ return visible_count(node->left, m) + visible_count(node->right, m);
+}
+
+Client *
+xytoclient(double x, double y) {
+ Client *c, *closest = NULL;
+ double dist, mindist = INT_MAX, dx, dy;
+
+ wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen &&
+ x >= c->geom.x && x <= (c->geom.x + c->geom.width) &&
+ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){
+ return c;
+ }
+ }
+
+ /* If no client was found at cursor position fallback to closest. */
+ wl_list_for_each_reverse(c, &clients, link) {
+ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) {
+ dx = 0, dy = 0;
+
+ if (x < c->geom.x)
+ dx = c->geom.x - x;
+ else if (x > (c->geom.x + c->geom.width))
+ dx = x - (c->geom.x + c->geom.width);
+
+ if (y < c->geom.y)
+ dy = c->geom.y - y;
+ else if (y > (c->geom.y + c->geom.height))
+ dy = y - (c->geom.y + c->geom.height);
+
+ dist = sqrt(dx * dx + dy * dy);
+ if (dist < mindist) {
+ mindist = dist;
+ closest = c;
+ }
+ }
+ }
+ return closest;
+}
diff --git a/config.def.h b/config.def.h
index 22d2171..92f3ad6 100644
--- a/config.def.h
+++ b/config.def.h
@@ -13,7 +13,10 @@ 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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
+static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */
+static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */
+enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
@@ -31,6 +34,7 @@ static const Rule rules[] = {
/* layout(s) */
static const Layout layouts[] = {
/* symbol arrange function */
+ { "|w|", btrtile },
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
@@ -148,6 +152,14 @@ static const Key keys[] = {
{ 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} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} },
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
diff --git a/dwl.c b/dwl.c
index a2711f6..e49a061 100644
--- a/dwl.c
+++ b/dwl.c
@@ -1,6 +1,7 @@
/*
* See LICENSE file for copyright and license details.
*/
+#include <limits.h>
#include <getopt.h>
#include <libinput.h>
#include <linux/input-event-codes.h>
@@ -103,6 +104,7 @@ typedef struct {
const Arg arg;
} Button;
+typedef struct LayoutNode LayoutNode;
typedef struct Monitor Monitor;
typedef struct {
/* Must keep these three elements in this order */
@@ -139,8 +141,9 @@ typedef struct {
#endif
unsigned int bw;
uint32_t tags;
- int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, was_tiled;
uint32_t resize; /* configure serial of a pending resize */
+ struct wlr_box old_geom;
} Client;
typedef struct {
@@ -208,6 +211,7 @@ struct Monitor {
int nmaster;
char ltsymbol[16];
int asleep;
+ LayoutNode *root;
};
typedef struct {
@@ -250,6 +254,7 @@ 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 void btrtile(Monitor *m);
static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude);
@@ -333,6 +338,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags);
static void setpsel(struct wl_listener *listener, void *data);
static void setsel(struct wl_listener *listener, void *data);
static void setup(void);
+static void setratio_h(const Arg *arg);
+static void setratio_v(const Arg *arg);
+static void swapclients(const Arg *arg);
static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg);
@@ -431,6 +439,7 @@ static xcb_atom_t netatom[NetLast];
/* attempt to encapsulate suck into one file */
#include "client.h"
+#include "btrtile.c"
/* function implementations */
void
@@ -601,7 +610,7 @@ buttonpress(struct wl_listener *listener, void *data)
struct wlr_pointer_button_event *event = data;
struct wlr_keyboard *keyboard;
uint32_t mods;
- Client *c;
+ Client *c, *target = NULL;
const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -622,7 +631,7 @@ buttonpress(struct wl_listener *listener, void *data)
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) {
+ event->button == b->button && b->func) {
b->func(&b->arg);
return;
}
@@ -632,15 +641,36 @@ buttonpress(struct wl_listener *listener, void *data)
/* If you released any buttons, we exit interactive move/resize mode. */
/* TODO should reset to the pointer focus's current setcursor */
if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
+ c = grabc;
+ if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) {
+ if (cursor_mode == CurMove && c->isfloating) {
+ target = xytoclient(cursor->x, cursor->y);
+
+ if (target && !target->isfloating && !target->isfullscreen)
+ insert_client(selmon, target, c);
+ else
+ selmon->root = create_client_node(c);
+
+ setfloating(c, 0);
+ arrange(selmon);
+
+ } else if (cursor_mode == CurResize && !c->isfloating) {
+ resizing_from_mouse = 0;
+ }
+ } else {
+ if (cursor_mode == CurResize && resizing_from_mouse)
+ resizing_from_mouse = 0;
+ }
+ /* Default behaviour */
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal;
/* Drop the window off on its new monitor */
selmon = xytomon(cursor->x, cursor->y);
setmon(grabc, selmon, 0);
+ grabc = NULL;
return;
- } else {
- cursor_mode = CurNormal;
}
+ cursor_mode = CurNormal;
break;
}
/* If the event wasn't handled by the compositor, notify the client with
@@ -720,6 +750,7 @@ cleanupmon(struct wl_listener *listener, void *data)
wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output);
+ destroy_tree(m);
closemon(m);
wlr_scene_node_destroy(&m->fullscreen_bg->node);
free(m);
@@ -1024,6 +1055,7 @@ createmon(struct wl_listener *listener, void *data)
wl_list_insert(&mons, &m->link);
printstatus();
+ init_tree(m);
/* The xdg-protocol specifies:
*
@@ -1263,6 +1295,10 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->destroy.link);
wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link);
+ /* We check if the destroyed client was part of any tiled_list, to catch
+ * client removals even if they would not be currently managed by btrtile */
+ if (selmon && selmon->root)
+ remove_client(selmon, c);
#ifdef XWAYLAND
if (c->type != XDGShell) {
wl_list_remove(&c->activate.link);
@@ -1809,7 +1845,8 @@ void
motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
double dx_unaccel, double dy_unaccel)
{
- double sx = 0, sy = 0, sx_confined, sy_confined;
+ int tiled = 0;
+ double sx = 0, sy = 0, sx_confined, sy_confined, dx_total, dy_total;
Client *c = NULL, *w = NULL;
LayerSurface *l = NULL;
struct wlr_surface *surface = NULL;
@@ -1863,18 +1900,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
- /* If we are currently grabbing the mouse, handle and return */
+ /* Skip if internal call or already resizing */
+ if (time == 0 && resizing_from_mouse)
+ goto focus;
+
+ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen;
if (cursor_mode == CurMove) {
/* Move the grabbed client to the new position. */
- resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy,
- .width = grabc->geom.width, .height = grabc->geom.height}, 1);
- return;
+ if (grabc && grabc->isfloating) {
+ resize(grabc, (struct wlr_box){
+ .x = (int)round(cursor->x) - grabcx,
+ .y = (int)round(cursor->y) - grabcy,
+ .width = grabc->geom.width,
+ .height = grabc->geom.height
+ }, 1);
+ return;
+ }
} else if (cursor_mode == CurResize) {
- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
- return;
+ if (tiled && resizing_from_mouse) {
+ dx_total = cursor->x - resize_last_update_x;
+ dy_total = cursor->y - resize_last_update_y;
+
+ if (time - last_resize_time >= resize_interval_ms) {
+ Arg a = {0};
+ if (fabs(dx_total) > fabs(dy_total)) {
+ a.f = (float)(dx_total * resize_factor);
+ setratio_h(&a);
+ } else {
+ a.f = (float)(dy_total * resize_factor);
+ setratio_v(&a);
+ }
+ arrange(selmon);
+
+ last_resize_time = time;
+ resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y;
+ }
+
+ } else if (grabc && grabc->isfloating) {
+ /* Floating resize as original */
+ resize(grabc, (struct wlr_box){
+ .x = grabc->geom.x,
+ .y = grabc->geom.y,
+ .width = (int)round(cursor->x) - grabc->geom.x,
+ .height = (int)round(cursor->y) - grabc->geom.y
+ }, 1);
+ return;
+ }
}
+focus:
/* If there's no client surface under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */
@@ -1908,22 +1983,41 @@ moveresize(const Arg *arg)
if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
return;
- /* Float the window and tell motionnotify to grab it */
- setfloating(grabc, 1);
- switch (cursor_mode = arg->ui) {
- case CurMove:
- grabcx = (int)round(cursor->x) - grabc->geom.x;
- grabcy = (int)round(cursor->y) - grabc->geom.y;
- wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
- break;
- case CurResize:
- /* Doesn't work for X11 output - the next absolute motion event
- * returns the cursor to where it started */
- wlr_cursor_warp_closest(cursor, NULL,
- grabc->geom.x + grabc->geom.width,
- grabc->geom.y + grabc->geom.height);
- wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
- break;
+ cursor_mode = arg->ui;
+ grabc->was_tiled = (!grabc->isfloating && !grabc->isfullscreen);
+
+ if (grabc->was_tiled) {
+ switch (cursor_mode) {
+ case CurMove:
+ setfloating(grabc, 1);
+ grabcx = (int)round(cursor->x) - grabc->geom.x;
+ grabcy = (int)round(cursor->y) - grabc->geom.y;
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
+ break;
+ case CurResize:
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ resize_last_update_x = cursor->x;
+ resize_last_update_y = cursor->y;
+ resizing_from_mouse = 1;
+ break;
+ }
+ } else {
+ /* Default floating logic */
+ /* Float the window and tell motionnotify to grab it */
+ setfloating(grabc, 1);
+ switch (cursor_mode) {
+ case CurMove:
+ grabcx = (int)round(cursor->x) - grabc->geom.x;
+ grabcy = (int)round(cursor->y) - grabc->geom.y;
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
+ break;
+ case CurResize:
+ wlr_cursor_warp_closest(cursor, NULL,
+ grabc->geom.x + grabc->geom.width,
+ grabc->geom.y + grabc->geom.height);
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ break;
+ }
}
}
--
2.45.3

@ -1 +0,0 @@
Subproject commit 28e8282191cb41dd9c3f2f21fd2db6eb64b0fdab

View file

@ -1,181 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This is largely ripped from somebar's ipc patchset; just with some personal modifications.
I would probably just submit raphi's patchset but I don't think that would be polite.
-->
<protocol name="dwl_ipc_unstable_v2">
<description summary="inter-proccess-communication about dwl's state">
This protocol allows clients to update and get updates from dwl.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible
changes may be added together with the corresponding interface
version bump.
Backward incompatible changes are done by bumping the version
number in the protocol and interface names and resetting the
interface version. Once the protocol is to be declared stable,
the 'z' prefix and the version number in the protocol and
interface names are removed and the interface version number is
reset.
</description>
<interface name="zdwl_ipc_manager_v2" version="2">
<description summary="manage dwl state">
This interface is exposed as a global in wl_registry.
Clients can use this interface to get a dwl_ipc_output.
After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events.
The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client.
</description>
<request name="release" type="destructor">
<description summary="release dwl_ipc_manager">
Indicates that the client will not the dwl_ipc_manager object anymore.
Objects created through this instance are not affected.
</description>
</request>
<request name="get_output">
<description summary="get a dwl_ipc_outout for a wl_output">
Get a dwl_ipc_outout for the specified wl_output.
</description>
<arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<event name="tags">
<description summary="Announces tag amount">
This event is sent after binding.
A roundtrip after binding guarantees the client recieved all tags.
</description>
<arg name="amount" type="uint"/>
</event>
<event name="layout">
<description summary="Announces a layout">
This event is sent after binding.
A roundtrip after binding guarantees the client recieved all layouts.
</description>
<arg name="name" type="string"/>
</event>
</interface>
<interface name="zdwl_ipc_output_v2" version="2">
<description summary="control dwl output">
Observe and control a dwl output.
Events are double-buffered:
Clients should cache events and redraw when a dwl_ipc_output.frame event is sent.
Request are not double-buffered:
The compositor will update immediately upon request.
</description>
<enum name="tag_state">
<entry name="none" value="0" summary="no state"/>
<entry name="active" value="1" summary="tag is active"/>
<entry name="urgent" value="2" summary="tag has at least one urgent client"/>
</enum>
<request name="release" type="destructor">
<description summary="release dwl_ipc_outout">
Indicates to that the client no longer needs this dwl_ipc_output.
</description>
</request>
<event name="toggle_visibility">
<description summary="Toggle client visibilty">
Indicates the client should hide or show themselves.
If the client is visible then hide, if hidden then show.
</description>
</event>
<event name="active">
<description summary="Update the selected output.">
Indicates if the output is active. Zero is invalid, nonzero is valid.
</description>
<arg name="active" type="uint"/>
</event>
<event name="tag">
<description summary="Update the state of a tag.">
Indicates that a tag has been updated.
</description>
<arg name="tag" type="uint" summary="Index of the tag"/>
<arg name="state" type="uint" enum="tag_state" summary="The state of the tag."/>
<arg name="clients" type="uint" summary="The number of clients in the tag."/>
<arg name="focused" type="uint" summary="If there is a focused client. Nonzero being valid, zero being invalid."/>
</event>
<event name="layout">
<description summary="Update the layout.">
Indicates a new layout is selected.
</description>
<arg name="layout" type="uint" summary="Index of the layout."/>
</event>
<event name="title">
<description summary="Update the title.">
Indicates the title has changed.
</description>
<arg name="title" type="string" summary="The new title name."/>
</event>
<event name="appid" since="1">
<description summary="Update the appid.">
Indicates the appid has changed.
</description>
<arg name="appid" type="string" summary="The new appid."/>
</event>
<event name="layout_symbol" since="1">
<description summary="Update the current layout symbol">
Indicates the layout has changed. Since layout symbols are dynamic.
As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying.
You can ignore the zdwl_ipc_output.layout event.
</description>
<arg name="layout" type="string" summary="The new layout"/>
</event>
<event name="frame">
<description summary="The update sequence is done.">
Indicates that a sequence of status updates have finished and the client should redraw.
</description>
</event>
<request name="set_tags">
<description summary="Set the active tags of this output"/>
<arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
<arg name="toggle_tagset" type="uint" summary="toggle the selected tagset, zero for invalid, nonzero for valid."/>
</request>
<request name="set_client_tags">
<description summary="Set the tags of the focused client.">
The tags are updated as follows:
new_tags = (current_tags AND and_tags) XOR xor_tags
</description>
<arg name="and_tags" type="uint"/>
<arg name="xor_tags" type="uint"/>
</request>
<request name="set_layout">
<description summary="Set the layout of this output"/>
<arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/>
</request>
<!-- Version 2 -->
<event name="fullscreen" since="2">
<description summary="Update fullscreen status">
Indicates if the selected client on this output is fullscreen.
</description>
<arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/>
</event>
<event name="floating" since="2">
<description summary="Update the floating status">
Indicates if the selected client on this output is floating.
</description>
<arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/>
</event>
</interface>
</protocol>