Merge remote-tracking branch 'djpohly/main' into wlroots-next

This commit is contained in:
Leonardo Hernández Hernández 2022-08-26 18:26:36 -05:00
commit e0cc5b046c
No known key found for this signature in database
GPG key ID: E538897EE11B9624
7 changed files with 436 additions and 272 deletions

View file

@ -1,73 +1,68 @@
.POSIX:
.SUFFIXES:
include config.mk include config.mk
CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -pedantic -DVERSION=\"$(VERSION)\" # flags for compiling
DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -DVERSION=\"$(VERSION)\"
WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) # Wayland utils
WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) WAYLAND_PROTOCOLS = `pkg-config --variable=pkgdatadir wayland-protocols`
WAYLAND_SCANNER = `pkg-config --variable=wayland_scanner wayland-scanner`
PKGS = wlroots wayland-server xcb xcb-icccm xkbcommon libinput # CFLAGS / LDFLAGS
CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p))) PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS)
LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p))) DWLCFLAGS = `pkg-config --cflags $(PKGS)` $(DWLCPPFLAGS) $(CFLAGS) $(XWAYLAND)
LDLIBS = `pkg-config --libs $(PKGS)` $(LIBS)
all: dwl # build rules
clean:
rm -f dwl *.o *-protocol.h *-protocol.c
dist: clean
mkdir -p dwl-$(VERSION)
cp -R LICENSE* Makefile README.md generate-version.sh client.h\
config.def.h config.mk protocols dwl.1 dwl.c util.c util.h\
dwl-$(VERSION)
echo "echo $(VERSION)" > dwl-$(VERSION)/generate-version.sh
tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
rm -rf dwl-$(VERSION)
install: dwl
install -Dm755 dwl $(DESTDIR)$(PREFIX)/bin/dwl
install -Dm644 dwl.1 $(DESTDIR)$(MANDIR)/man1/dwl.1
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1
.PHONY: all clean dist install uninstall
# 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
# to your build system yourself and provide them in the include path. # to your build system yourself and provide them in the include path.
all: dwl
dwl: dwl.o util.o
$(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h
util.o: util.c util.h
# wayland scanner rules to generate .h / .c files
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 $@
xdg-shell-protocol.c:
$(WAYLAND_SCANNER) private-code \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
xdg-shell-protocol.o: xdg-shell-protocol.h
wlr-layer-shell-unstable-v1-protocol.h: wlr-layer-shell-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \ $(WAYLAND_SCANNER) server-header \
protocols/wlr-layer-shell-unstable-v1.xml $@ protocols/wlr-layer-shell-unstable-v1.xml $@
wlr-layer-shell-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/wlr-layer-shell-unstable-v1.xml $@
wlr-layer-shell-unstable-v1-protocol.o: wlr-layer-shell-unstable-v1-protocol.h
idle-protocol.h: idle-protocol.h:
$(WAYLAND_SCANNER) server-header \ $(WAYLAND_SCANNER) server-header \
protocols/idle.xml $@ protocols/idle.xml $@
idle-protocol.c: config.h:
$(WAYLAND_SCANNER) private-code \
protocols/idle.xml $@
idle-protocol.o: idle-protocol.h
config.h: | config.def.h
cp config.def.h $@ cp config.def.h $@
clean:
rm -f dwl *.o *-protocol.h
dwl.o: config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h util.h # distribution archive
dist: clean
mkdir -p dwl-$(VERSION)
cp -R LICENSE* Makefile README.md client.h config.def.h\
config.mk protocols dwl.1 dwl.c util.c util.h\
dwl-$(VERSION)
tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
rm -rf dwl-$(VERSION)
dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o util.o # install rules
install: dwl
mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f dwl $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl
mkdir -p $(DESTDIR)$(MANDIR)/man1
cp -f dwl.1 $(DESTDIR)$(MANDIR)/man1
chmod 644 $(DESTDIR)$(MANDIR)/man1/dwl.1
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1
.SUFFIXES: .c .o
.c.o:
$(CC) $(CPPFLAGS) $(DWLCFLAGS) -c $<

View file

@ -6,7 +6,7 @@ dwl is a compact, hackable compositor for Wayland based on [wlroots](https://git
- Easy to understand, hack on, and extend with patches - Easy to understand, hack on, and extend with patches
- One C source file (or a very small number) configurable via `config.h` - One C source file (or a very small number) configurable via `config.h`
- Limited to 2000 SLOC to promote hackability - Limited to 2200 SLOC to promote hackability
- Tied to as few external dependencies as possible - Tied to as few external dependencies as possible
dwl is not meant to provide every feature under the sun. Instead, like dwm, it sticks to features which are necessary, simple, and straightforward to implement given the base on which it is built. Implemented default features are: dwl is not meant to provide every feature under the sun. Instead, like dwm, it sticks to features which are necessary, simple, and straightforward to implement given the base on which it is built. Implemented default features are:
@ -37,7 +37,9 @@ Feature *non-goals* for the main codebase include:
## Building dwl ## Building dwl
dwl has only two dependencies: wlroots and wayland-protocols. Simply install these (and their `-devel` versions if your distro has separate development packages) and run `make`. If you wish to build against a Git version of wlroots, check out the [wlroots-next branch](https://github.com/djpohly/dwl/tree/wlroots-next). dwl has only two dependencies: `wlroots` and `wayland-protocols`.
Simply install these (and their `-devel` versions if your distro has separate development packages) and run `make`. If you wish to build against a Git version of wlroots, check out the [wlroots-next branch](https://github.com/djpohly/dwl/tree/wlroots-next).
To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`. To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`.
@ -71,6 +73,14 @@ If your startup command is a shell script, you can achieve the same inside the s
exec <&- exec <&-
Existing dwl-specific status bars and dwl-specific scripts for other status bars include:
- [somebar](https://sr.ht/~raphi/somebar/) status bar designed for dwl
- [dtaobarv2.sh](https://cdn.discordapp.com/attachments/792078050024095745/862428883423723560/dtaobarv2.sh) for use with [dtao](https://github.com/djpohly/dtao) (See "Pinned Messages" on the "customizations" channel of the [dwl Discord server](https://discord.gg/jJxZnrGPWN) for details.)
- [dwlbar.sh](https://cdn.discordapp.com/attachments/792078050024095745/810926218529472592/dwlbar.sh) for use with [waybar](https://github.com/Alexays/Waybar) (See "Pinned Messages" on the "customizations" channel of the [dwl Discord server](https://discord.gg/jJxZnrGPWN) for details.)
- [waybar-dwl](https://codeberg.org/fauxmight/waybar-dwl.git) for use with [waybar](https://github.com/Alexays/Waybar)
- [dwl-tags.sh](https://codeberg.org/novakane/yambar/src/branch/master/examples/scripts/dwl-tags.sh) for use with [yambar](https://codeberg.org/dnkl/yambar)
- [waybar-dwl.sh](https://gitee.com/guyuming76/personal/tree/dwl/gentoo/waybar-dwl) for use with [waybar](https://github.com/Alexays/Waybar) (ACCESS TO THIS SCRIPT REQUIRES gitee.com LOGIN!)
## Replacements for X applications ## Replacements for X applications
You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide). You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide).

134
client.h
View file

@ -26,6 +26,28 @@ client_surface(Client *c)
return c->surface.xdg->surface; return c->surface.xdg->surface;
} }
static inline Client *
client_from_wlr_surface(struct wlr_surface *s)
{
struct wlr_xdg_surface *surface;
struct wlr_surface *parent;
#ifdef XWAYLAND
struct wlr_xwayland_surface *xsurface;
if (s && wlr_surface_is_xwayland_surface(s)
&& (xsurface = wlr_xwayland_surface_from_wlr_surface(s)))
return xsurface->data;
#endif
if (s && wlr_surface_is_xdg_surface(s)
&& (surface = wlr_xdg_surface_from_wlr_surface(s))
&& surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL)
return surface->data;
if (s && wlr_surface_is_subsurface(s))
return client_from_wlr_surface(wlr_surface_get_root_surface(s));
return NULL;
}
/* The others */ /* The others */
static inline void static inline void
client_activate_surface(struct wlr_surface *s, int activated) client_activate_surface(struct wlr_surface *s, int activated)
@ -94,6 +116,31 @@ client_get_geometry(Client *c, struct wlr_box *geom)
wlr_xdg_surface_get_geometry(c->surface.xdg, geom); wlr_xdg_surface_get_geometry(c->surface.xdg, geom);
} }
static inline void
client_get_size_hints(Client *c, struct wlr_box *max, struct wlr_box *min)
{
struct wlr_xdg_toplevel *toplevel;
struct wlr_xdg_toplevel_state *state;
#ifdef XWAYLAND
if (client_is_x11(c)) {
xcb_size_hints_t *size_hints = c->surface.xwayland->size_hints;
if (size_hints) {
max->width = size_hints->max_width;
max->height = size_hints->max_height;
min->width = size_hints->min_width;
min->height = size_hints->min_height;
}
return;
}
#endif
toplevel = c->surface.xdg->toplevel;
state = &toplevel->current;
max->width = state->max_width;
max->height = state->max_height;
min->width = state->min_width;
min->height = state->min_height;
}
static inline const char * static inline const char *
client_get_title(Client *c) client_get_title(Client *c)
{ {
@ -104,42 +151,58 @@ client_get_title(Client *c)
return c->surface.xdg->toplevel->title; return c->surface.xdg->toplevel->title;
} }
static inline Client *
client_get_parent(Client *c)
{
Client *p;
#ifdef XWAYLAND
if (client_is_x11(c) && c->surface.xwayland->parent)
return client_from_wlr_surface(c->surface.xwayland->parent->surface);
#endif
if (c->surface.xdg->toplevel->parent)
return client_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface);
return NULL;
}
static inline int static inline int
client_is_float_type(Client *c) client_is_float_type(Client *c)
{ {
struct wlr_xdg_toplevel *toplevel; struct wlr_box min = {0}, max = {0};
struct wlr_xdg_toplevel_state state; client_get_size_hints(c, &max, &min);
#ifdef XWAYLAND #ifdef XWAYLAND
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;
if (surface->modal) if (surface->modal)
return 1; return 1;
for (size_t i = 0; i < surface->window_type_len; i++) for (size_t i = 0; i < surface->window_type_len; i++)
if (surface->window_type[i] == netatom[NetWMWindowTypeDialog] || if (surface->window_type[i] == netatom[NetWMWindowTypeDialog]
surface->window_type[i] == netatom[NetWMWindowTypeSplash] || || surface->window_type[i] == netatom[NetWMWindowTypeSplash]
surface->window_type[i] == netatom[NetWMWindowTypeToolbar] || || surface->window_type[i] == netatom[NetWMWindowTypeToolbar]
surface->window_type[i] == netatom[NetWMWindowTypeUtility]) || surface->window_type[i] == netatom[NetWMWindowTypeUtility])
return 1; return 1;
size_hints = surface->size_hints; return ((min.width > 0 || min.height > 0 || max.width > 0 || max.height > 0)
if (size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 && (min.width == max.width || min.height == max.height))
&& (size_hints->max_width == size_hints->min_width || || c->surface.xwayland->parent;
size_hints->max_height == size_hints->min_height))
return 1;
return 0;
} }
#endif #endif
toplevel = c->surface.xdg->toplevel; return ((min.width > 0 || min.height > 0 || max.width > 0 || max.height > 0)
state = toplevel->current; && (min.width == max.width || min.height == max.height))
return (state.min_width != 0 && state.min_height != 0 || c->surface.xdg->toplevel->parent;
&& (state.min_width == state.max_width }
|| state.min_height == state.max_height))
|| toplevel->parent; static inline int
client_is_mapped(Client *c)
{
#ifdef XWAYLAND
if (client_is_x11(c))
return c->surface.xwayland->mapped;
#endif
return c->surface.xdg->mapped;
} }
static inline int static inline int
@ -220,38 +283,27 @@ client_surface_at(Client *c, double cx, double cy, double *sx, double *sy)
} }
static inline void static inline void
client_min_size(Client *c, int *width, int *height) client_restack_surface(Client *c)
{ {
struct wlr_xdg_toplevel *toplevel;
struct wlr_xdg_toplevel_state *state;
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c))
xcb_size_hints_t *size_hints = c->surface.xwayland->size_hints; wlr_xwayland_surface_restack(c->surface.xwayland, NULL,
if (size_hints) { XCB_STACK_MODE_ABOVE);
*width = size_hints->min_width; #endif
*height = size_hints->min_height;
} else {
*width = 0;
*height = 0;
}
return; return;
} }
#endif
toplevel = c->surface.xdg->toplevel;
state = &toplevel->current;
*width = state->min_width;
*height = state->min_height;
}
static inline Client * static inline void *
client_from_popup(struct wlr_xdg_popup *popup) toplevel_from_popup(struct wlr_xdg_popup *popup)
{ {
struct wlr_xdg_surface *surface = popup->base; struct wlr_xdg_surface *surface = popup->base;
while (1) { while (1) {
switch (surface->role) { switch (surface->role) {
case WLR_XDG_SURFACE_ROLE_POPUP: case WLR_XDG_SURFACE_ROLE_POPUP:
if (!wlr_surface_is_xdg_surface(surface->popup->parent)) if (wlr_surface_is_layer_surface(surface->popup->parent))
return wlr_layer_surface_v1_from_wlr_surface(surface->popup->parent)->data;
else if (!wlr_surface_is_xdg_surface(surface->popup->parent))
return NULL; return NULL;
surface = wlr_xdg_surface_from_wlr_surface(surface->popup->parent); surface = wlr_xdg_surface_from_wlr_surface(surface->popup->parent);

View file

@ -5,6 +5,8 @@ static const int lockfullscreen = 1; /* 1 will force focus on the fullscree
static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0}; static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0};
/* tagging */ /* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@ -41,7 +43,7 @@ static const struct xkb_rule_names xkb_rules = {
/* example: /* example:
.options = "ctrl:nocaps", .options = "ctrl:nocaps",
*/ */
.options = "", .options = NULL,
}; };
static const int repeat_rate = 25; static const int repeat_rate = 25;
@ -49,7 +51,40 @@ static const int repeat_delay = 600;
/* Trackpad */ /* Trackpad */
static const int tap_to_click = 1; 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 natural_scrolling = 0;
static const int disable_while_typing = 1;
static const int left_handed = 0;
static const int middle_button_emulation = 0;
/* You can choose between:
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
LIBINPUT_CONFIG_SCROLL_2FG
LIBINPUT_CONFIG_SCROLL_EDGE
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
*/
static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
/* You can choose between:
LIBINPUT_CONFIG_CLICK_METHOD_NONE
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
*/
static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
/* You can choose between:
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
*/
static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
/* You can choose between:
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
*/
static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
static const double accel_speed = 0.0;
/* If you want to use the windows key change this to WLR_MODIFIER_LOGO */ /* If you want to use the windows key change this to WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT #define MODKEY WLR_MODIFIER_ALT

View file

@ -1,12 +1,15 @@
_VERSION = 0.3.1 _VERSION = 0.3.1
VERSION = $(shell ./generate-version.sh $(_VERSION)) VERSION = `git describe --long --tags --dirty 2>/dev/null || echo $(_VERSION)`
# paths # paths
PREFIX = /usr/local PREFIX = /usr/local
MANDIR = $(PREFIX)/share/man MANDIR = $(PREFIX)/share/man
# Default compile flags (overridable by environment) # Compile flags that can be used
CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement #CFLAGS = -pedantic -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement
XWAYLAND =
XLIBS =
# Uncomment to build XWayland support # Uncomment to build XWayland support
#CFLAGS += -DXWAYLAND #XWAYLAND = -DXWAYLAND
#XLIBS = xcb

378
dwl.c
View file

@ -4,6 +4,7 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <getopt.h> #include <getopt.h>
#include <libinput.h> #include <libinput.h>
#include <limits.h>
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@ -92,11 +93,14 @@ typedef struct {
typedef struct Monitor Monitor; typedef struct Monitor Monitor;
typedef struct { typedef struct {
/* Must be first */ /* Must keep these three elements in this order */
unsigned int type; /* XDGShell or X11* */ unsigned int type; /* XDGShell or X11* */
struct wlr_box geom; /* layout-relative, includes border */
Monitor *mon;
struct wlr_scene_tree *scene; struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
struct wlr_scene_tree *scene_surface; struct wlr_scene_tree *scene_surface;
struct wlr_scene_rect *fullscreen_bg; /* See setfullscreen() for info */
struct wl_list link; struct wl_list link;
struct wl_list flink; struct wl_list flink;
union { union {
@ -110,18 +114,16 @@ typedef struct {
struct wl_listener destroy; struct wl_listener destroy;
struct wl_listener set_title; struct wl_listener set_title;
struct wl_listener fullscreen; struct wl_listener fullscreen;
struct wlr_box geom, prev; /* layout-relative, includes border */ struct wlr_box prev; /* layout-relative, includes border */
Monitor *mon;
#ifdef XWAYLAND #ifdef XWAYLAND
struct wl_listener activate; struct wl_listener activate;
struct wl_listener configure; struct wl_listener configure;
struct wl_listener set_hints; struct wl_listener set_hints;
#endif #endif
int bw; unsigned int bw;
unsigned int tags; unsigned int tags;
int isfloating, isurgent; int isfloating, isurgent, isfullscreen;
uint32_t resize; /* configure serial of a pending resize */ uint32_t resize; /* configure serial of a pending resize */
int isfullscreen;
} Client; } Client;
typedef struct { typedef struct {
@ -141,12 +143,14 @@ typedef struct {
} Keyboard; } Keyboard;
typedef struct { typedef struct {
/* Must be first */ /* Must keep these three elements in this order */
unsigned int type; /* LayerShell */ unsigned int type; /* LayerShell */
int mapped; struct wlr_box geom;
Monitor *mon;
struct wlr_scene_tree *scene; struct wlr_scene_tree *scene;
struct wlr_scene_layer_surface_v1 *scene_layer; struct wlr_scene_layer_surface_v1 *scene_layer;
struct wl_list link; struct wl_list link;
int mapped;
struct wlr_layer_surface_v1 *layer_surface; struct wlr_layer_surface_v1 *layer_surface;
struct wl_listener destroy; struct wl_listener destroy;
@ -252,7 +256,7 @@ static void quit(const Arg *arg);
static void quitsignal(int signo); static void quitsignal(int signo);
static void rendermon(struct wl_listener *listener, void *data); static void rendermon(struct wl_listener *listener, void *data);
static void requeststartdrag(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data);
static void resize(Client *c, int x, int y, int w, int h, int interact); static void resize(Client *c, struct wlr_box geo, int interact);
static void run(char *startup_cmd); static void run(char *startup_cmd);
static Client *selclient(void); static Client *selclient(void);
static void setcursor(struct wl_listener *listener, void *data); static void setcursor(struct wl_listener *listener, void *data);
@ -273,7 +277,6 @@ static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg); static void togglefullscreen(const Arg *arg);
static void toggletag(const Arg *arg); static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg); static void toggleview(const Arg *arg);
static void unmaplayersurface(LayerSurface *layersurface);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
static void unmapnotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data);
static void updatemons(struct wl_listener *listener, void *data); static void updatemons(struct wl_listener *listener, void *data);
@ -288,6 +291,8 @@ static void zoom(const Arg *arg);
/* variables */ /* variables */
static const char broken[] = "broken"; static const char broken[] = "broken";
static pid_t child_pid = -1;
static struct wlr_surface *exclusive_focus;
static struct wl_display *dpy; static struct wl_display *dpy;
static struct wlr_backend *backend; static struct wlr_backend *backend;
static struct wlr_scene *scene; static struct wlr_scene *scene;
@ -372,9 +377,17 @@ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
void void
applybounds(Client *c, struct wlr_box *bbox) applybounds(Client *c, struct wlr_box *bbox)
{ {
/* set minimum possible */ if (!c->isfullscreen) {
c->geom.width = MAX(1, c->geom.width); struct wlr_box min = {0}, max = {0};
c->geom.height = MAX(1, c->geom.height); client_get_size_hints(c, &max, &min);
/* try to set size hints */
c->geom.width = MAX(min.width + (2 * c->bw), c->geom.width);
c->geom.height = MAX(min.height + (2 * c->bw), c->geom.height);
if (max.width > 0 && !(2 * c->bw > INT_MAX - max.width)) // Checks for overflow
c->geom.width = MIN(max.width + (2 * c->bw), c->geom.width);
if (max.height > 0 && !(2 * c->bw > INT_MAX - max.height)) // Checks for overflow
c->geom.height = MIN(max.height + (2 * c->bw), c->geom.height);
}
if (c->geom.x >= bbox->x + bbox->width) if (c->geom.x >= bbox->x + bbox->width)
c->geom.x = bbox->x + bbox->width - c->geom.width; c->geom.x = bbox->x + bbox->width - c->geom.width;
@ -425,7 +438,7 @@ arrange(Monitor *m)
if (m->lt[m->sellt]->arrange) if (m->lt[m->sellt]->arrange)
m->lt[m->sellt]->arrange(m); m->lt[m->sellt]->arrange(m);
/* TODO recheck pointer focus here... or in resize()? */ motionnotify(0);
} }
void void
@ -478,11 +491,12 @@ arrangelayers(Monitor *m)
layersurface->layer_surface->mapped) { layersurface->layer_surface->mapped) {
/* Deactivate the focused client. */ /* Deactivate the focused client. */
focusclient(NULL, 0); focusclient(NULL, 0);
exclusive_focus = layersurface->layer_surface->surface;
if (kb) if (kb)
wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, wlr_seat_keyboard_notify_enter(seat, exclusive_focus,
kb->keycodes, kb->num_keycodes, &kb->modifiers); kb->keycodes, kb->num_keycodes, &kb->modifiers);
else else
wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, NULL, 0, NULL); wlr_seat_keyboard_notify_enter(seat, exclusive_focus, NULL, 0, NULL);
return; return;
} }
} }
@ -563,7 +577,10 @@ cleanup(void)
wlr_xwayland_destroy(xwayland); wlr_xwayland_destroy(xwayland);
#endif #endif
wl_display_destroy_clients(dpy); wl_display_destroy_clients(dpy);
if (child_pid > 0) {
kill(child_pid, SIGTERM);
waitpid(child_pid, NULL, 0);
}
wlr_backend_destroy(backend); wlr_backend_destroy(backend);
wlr_xcursor_manager_destroy(cursor_mgr); wlr_xcursor_manager_destroy(cursor_mgr);
wlr_cursor_destroy(cursor); wlr_cursor_destroy(cursor);
@ -615,8 +632,8 @@ closemon(Monitor *m)
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (c->isfloating && c->geom.x > m->m.width) if (c->isfloating && c->geom.x > m->m.width)
resize(c, c->geom.x - m->w.width, c->geom.y, resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
c->geom.width, c->geom.height, 0); .width = c->geom.width, .height = c->geom.height}, 0);
if (c->mon == m) if (c->mon == m)
setmon(c, selmon, c->tags); setmon(c, selmon, c->tags);
} }
@ -629,14 +646,13 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit); LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit);
struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
struct wlr_output *wlr_output = wlr_layer_surface->output; struct wlr_output *wlr_output = wlr_layer_surface->output;
Monitor *m;
if (!wlr_output || !(layersurface->mon = wlr_output->data))
return;
wlr_scene_node_reparent(&layersurface->scene->node, wlr_scene_node_reparent(&layersurface->scene->node,
layers[wlr_layer_surface->current.layer]); layers[wlr_layer_surface->current.layer]);
if (!wlr_output || !(m = wlr_output->data))
return;
if (wlr_layer_surface->current.committed == 0 if (wlr_layer_surface->current.committed == 0
&& layersurface->mapped == wlr_layer_surface->mapped) && layersurface->mapped == wlr_layer_surface->mapped)
return; return;
@ -645,19 +661,27 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
if (layers[wlr_layer_surface->current.layer] != layersurface->scene) { if (layers[wlr_layer_surface->current.layer] != layersurface->scene) {
wl_list_remove(&layersurface->link); wl_list_remove(&layersurface->link);
wl_list_insert(&m->layers[wlr_layer_surface->current.layer], wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->current.layer],
&layersurface->link); &layersurface->link);
} }
arrangelayers(m); arrangelayers(layersurface->mon);
} }
void void
commitnotify(struct wl_listener *listener, void *data) commitnotify(struct wl_listener *listener, void *data)
{ {
Client *c = wl_container_of(listener, c, commit); Client *c = wl_container_of(listener, c, commit);
struct wlr_box box = {0};
client_get_geometry(c, &box);
if (c->mon && !wlr_box_empty(&box) && (box.width != c->geom.width - 2 * c->bw
|| box.height != c->geom.height - 2 * c->bw))
arrange(c->mon);
/* mark a pending resize as completed */ /* mark a pending resize as completed */
if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) if (c->resize && (c->resize <= c->surface.xdg->current.configure_serial
|| (c->surface.xdg->current.geometry.width == c->surface.xdg->pending.geometry.width
&& c->surface.xdg->current.geometry.height == c->surface.xdg->pending.geometry.height)))
c->resize = 0; c->resize = 0;
} }
@ -704,7 +728,6 @@ createlayersurface(struct wl_listener *listener, void *data)
{ {
struct wlr_layer_surface_v1 *wlr_layer_surface = data; struct wlr_layer_surface_v1 *wlr_layer_surface = data;
LayerSurface *layersurface; LayerSurface *layersurface;
Monitor *m;
struct wlr_layer_surface_v1_state old_state; struct wlr_layer_surface_v1_state old_state;
if (!wlr_layer_surface->output) { if (!wlr_layer_surface->output) {
@ -724,7 +747,7 @@ createlayersurface(struct wl_listener *listener, void *data)
layersurface->layer_surface = wlr_layer_surface; layersurface->layer_surface = wlr_layer_surface;
wlr_layer_surface->data = layersurface; wlr_layer_surface->data = layersurface;
m = wlr_layer_surface->output->data; layersurface->mon = wlr_layer_surface->output->data;
layersurface->scene_layer = wlr_scene_layer_surface_v1_create( layersurface->scene_layer = wlr_scene_layer_surface_v1_create(
layers[wlr_layer_surface->pending.layer], wlr_layer_surface); layers[wlr_layer_surface->pending.layer], wlr_layer_surface);
@ -733,7 +756,7 @@ createlayersurface(struct wl_listener *listener, void *data)
layersurface->scene->node.data = layersurface; layersurface->scene->node.data = layersurface;
wl_list_insert(&m->layers[wlr_layer_surface->pending.layer], wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->pending.layer],
&layersurface->link); &layersurface->link);
/* Temporarily set the layer's current state to pending /* Temporarily set the layer's current state to pending
@ -741,7 +764,7 @@ createlayersurface(struct wl_listener *listener, void *data)
*/ */
old_state = wlr_layer_surface->current; old_state = wlr_layer_surface->current;
wlr_layer_surface->current = wlr_layer_surface->pending; wlr_layer_surface->current = wlr_layer_surface->pending;
arrangelayers(m); arrangelayers(layersurface->mon);
wlr_layer_surface->current = old_state; wlr_layer_surface->current = old_state;
} }
@ -752,6 +775,7 @@ createmon(struct wl_listener *listener, void *data)
* monitor) becomes available. */ * monitor) becomes available. */
struct wlr_output *wlr_output = data; struct wlr_output *wlr_output = data;
const MonitorRule *r; const MonitorRule *r;
Client *c;
Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m)); Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m));
m->wlr_output = wlr_output; m->wlr_output = wlr_output;
@ -799,16 +823,6 @@ createmon(struct wl_listener *listener, void *data)
*/ */
m->scene_output = wlr_scene_output_create(scene, wlr_output); m->scene_output = wlr_scene_output_create(scene, wlr_output);
wlr_output_layout_add_auto(output_layout, wlr_output); wlr_output_layout_add_auto(output_layout, wlr_output);
/* If length == 1 we need update selmon.
* Maybe it will change in run(). */
if (wl_list_length(&mons) == 1) {
Client *c;
selmon = m;
/* If there is any client, set c->mon to this monitor */
wl_list_for_each(c, &clients, link)
setmon(c, m, c->tags);
}
} }
void void
@ -824,13 +838,17 @@ createnotify(struct wl_listener *listener, void *data)
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
struct wlr_box box; struct wlr_box box;
LayerSurface *l = toplevel_from_popup(xdg_surface->popup);
xdg_surface->surface->data = wlr_scene_xdg_surface_create( xdg_surface->surface->data = wlr_scene_xdg_surface_create(
xdg_surface->popup->parent->data, xdg_surface); xdg_surface->popup->parent->data, xdg_surface);
if (!(c = client_from_popup(xdg_surface->popup)) || !c->mon) if (wlr_surface_is_layer_surface(xdg_surface->popup->parent) && l
&& l->layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
wlr_scene_node_reparent(xdg_surface->surface->data, layers[LyrTop]);
if (!l || !l->mon)
return; return;
box = c->mon->m; box = l->type == LayerShell ? l->mon->m : l->mon->w;
box.x -= c->geom.x; box.x -= l->geom.x;
box.y -= c->geom.y; box.y -= l->geom.y;
wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box);
return; return;
} else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE)
@ -841,7 +859,6 @@ createnotify(struct wl_listener *listener, void *data)
c->surface.xdg = xdg_surface; c->surface.xdg = xdg_surface;
c->bw = borderpx; c->bw = borderpx;
LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify);
LISTEN(&xdg_surface->events.map, &c->map, mapnotify); LISTEN(&xdg_surface->events.map, &c->map, mapnotify);
LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify); LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify);
LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify);
@ -850,7 +867,6 @@ createnotify(struct wl_listener *listener, void *data)
fullscreennotify); fullscreennotify);
LISTEN(&xdg_surface->toplevel->events.request_maximize, &c->maximize, LISTEN(&xdg_surface->toplevel->events.request_maximize, &c->maximize,
maximizenotify); maximizenotify);
c->isfullscreen = 0;
} }
void void
@ -860,17 +876,39 @@ createpointer(struct wlr_pointer *pointer)
struct libinput_device *libinput_device = (struct libinput_device*) struct libinput_device *libinput_device = (struct libinput_device*)
wlr_libinput_get_device_handle(&pointer->base); wlr_libinput_get_device_handle(&pointer->base);
if (tap_to_click && libinput_device_config_tap_get_finger_count(libinput_device)) if (libinput_device_config_tap_get_finger_count(libinput_device)) {
libinput_device_config_tap_set_enabled(libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); libinput_device_config_tap_set_enabled(libinput_device, tap_to_click);
libinput_device_config_tap_set_drag_enabled(libinput_device, tap_and_drag);
libinput_device_config_tap_set_drag_lock_enabled(libinput_device, drag_lock);
}
if (libinput_device_config_scroll_has_natural_scroll(libinput_device)) if (libinput_device_config_scroll_has_natural_scroll(libinput_device))
libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling); libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling);
if (libinput_device_config_dwt_is_available(libinput_device))
libinput_device_config_dwt_set_enabled(libinput_device, disable_while_typing);
if (libinput_device_config_left_handed_is_available(libinput_device))
libinput_device_config_left_handed_set(libinput_device, left_handed);
if (libinput_device_config_middle_emulation_is_available(libinput_device))
libinput_device_config_middle_emulation_set_enabled(libinput_device, middle_button_emulation);
if (libinput_device_config_scroll_get_methods(libinput_device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
libinput_device_config_scroll_set_method (libinput_device, scroll_method);
if (libinput_device_config_click_get_methods(libinput_device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
libinput_device_config_click_set_method (libinput_device, click_method);
if (libinput_device_config_send_events_get_modes(libinput_device))
libinput_device_config_send_events_set_mode(libinput_device, send_events_mode);
if (libinput_device_config_accel_is_available(libinput_device)) {
libinput_device_config_accel_set_profile(libinput_device, accel_profile);
libinput_device_config_accel_set_speed(libinput_device, accel_speed);
}
} }
/* We don't do anything special with pointers. All of our pointer handling
* is proxied through wlr_cursor. On another compositor, you might take this
* opportunity to do libinput configuration on the device to set
* acceleration, etc. */
wlr_cursor_attach_input_device(cursor, &pointer->base); wlr_cursor_attach_input_device(cursor, &pointer->base);
} }
@ -898,17 +936,15 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data)
{ {
LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy);
if (layersurface->layer_surface->mapped)
unmaplayersurface(layersurface);
wl_list_remove(&layersurface->link); wl_list_remove(&layersurface->link);
wl_list_remove(&layersurface->destroy.link); wl_list_remove(&layersurface->destroy.link);
wl_list_remove(&layersurface->map.link); wl_list_remove(&layersurface->map.link);
wl_list_remove(&layersurface->unmap.link); wl_list_remove(&layersurface->unmap.link);
wl_list_remove(&layersurface->surface_commit.link); wl_list_remove(&layersurface->surface_commit.link);
wlr_scene_node_destroy(&layersurface->scene->node);
if (layersurface->layer_surface->output) { if (layersurface->layer_surface->output) {
Monitor *m = layersurface->layer_surface->output->data; if ((layersurface->mon = layersurface->layer_surface->output->data))
if (m) arrangelayers(layersurface->mon);
arrangelayers(m);
layersurface->layer_surface->output = NULL; layersurface->layer_surface->output = NULL;
} }
free(layersurface); free(layersurface);
@ -929,9 +965,8 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->configure.link); wl_list_remove(&c->configure.link);
wl_list_remove(&c->set_hints.link); wl_list_remove(&c->set_hints.link);
wl_list_remove(&c->activate.link); wl_list_remove(&c->activate.link);
} else }
#endif #endif
wl_list_remove(&c->commit.link);
free(c); free(c);
} }
@ -965,6 +1000,9 @@ focusclient(Client *c, int lift)
struct wlr_surface *old = seat->keyboard_state.focused_surface; struct wlr_surface *old = seat->keyboard_state.focused_surface;
struct wlr_keyboard *kb; struct wlr_keyboard *kb;
int i; int i;
/* Do not focus clients if a layer surface is focused */
if (exclusive_focus)
return;
/* Raise client in stacking order if requested */ /* Raise client in stacking order if requested */
if (c && lift) if (c && lift)
@ -979,6 +1017,7 @@ focusclient(Client *c, int lift)
wl_list_insert(&fstack, &c->flink); wl_list_insert(&fstack, &c->flink);
selmon = c->mon; selmon = c->mon;
c->isurgent = 0; c->isurgent = 0;
client_restack_surface(c);
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
wlr_scene_rect_set_color(c->border[i], focuscolor); wlr_scene_rect_set_color(c->border[i], focuscolor);
@ -1002,8 +1041,7 @@ focusclient(Client *c, int lift)
return; return;
} else { } else {
Client *w; Client *w;
struct wlr_scene_node *node = old->data; if ((w = client_from_wlr_surface(old)))
if (old->role_data && (w = node->data))
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
wlr_scene_rect_set_color(w->border[i], bordercolor); wlr_scene_rect_set_color(w->border[i], bordercolor);
@ -1020,15 +1058,6 @@ focusclient(Client *c, int lift)
return; return;
} }
#ifdef XWAYLAND
/* This resolves an issue where the last spawned xwayland client
* receives all pointer activity.
*/
if (c->type == X11Managed)
wlr_xwayland_surface_restack(c->surface.xwayland, NULL,
XCB_STACK_MODE_ABOVE);
#endif
/* Have a client, so focus its top-level wlr_surface */ /* Have a client, so focus its top-level wlr_surface */
kb = wlr_seat_get_keyboard(seat); kb = wlr_seat_get_keyboard(seat);
if (kb) if (kb)
@ -1044,9 +1073,11 @@ focusclient(Client *c, int lift)
void void
focusmon(const Arg *arg) focusmon(const Arg *arg)
{ {
do int i = 0, nmons = wl_list_length(&mons);
if (nmons)
do /* don't switch to disabled mons */
selmon = dirtomon(arg->i); selmon = dirtomon(arg->i);
while (!selmon->wlr_output->enabled); while (!selmon->wlr_output->enabled && i++ < nmons);
focusclient(focustop(selmon), 1); focusclient(focustop(selmon), 1);
} }
@ -1222,8 +1253,9 @@ void
maplayersurfacenotify(struct wl_listener *listener, void *data) maplayersurfacenotify(struct wl_listener *listener, void *data)
{ {
LayerSurface *layersurface = wl_container_of(listener, layersurface, map); LayerSurface *layersurface = wl_container_of(listener, layersurface, map);
layersurface->mon = layersurface->layer_surface->output->data;
wlr_surface_send_enter(layersurface->layer_surface->surface, wlr_surface_send_enter(layersurface->layer_surface->surface,
layersurface->layer_surface->output); layersurface->mon->wlr_output);
motionnotify(0); motionnotify(0);
} }
@ -1231,15 +1263,21 @@ void
mapnotify(struct wl_listener *listener, void *data) mapnotify(struct wl_listener *listener, void *data)
{ {
/* Called when the surface is mapped, or ready to display on-screen. */ /* Called when the surface is mapped, or ready to display on-screen. */
Client *c = wl_container_of(listener, c, map); Client *p, *c = wl_container_of(listener, c, map);
int i; int i;
/* Create scene tree for this client and its border */ /* Create scene tree for this client and its border */
c->scene = wlr_scene_tree_create(layers[LyrTile]); c->scene = wlr_scene_tree_create(layers[LyrTile]);
c->scene_surface = client_surface(c)->data = c->type == XDGShell c->scene_surface = c->type == XDGShell
? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
: wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
c->scene_surface->node.data = c; if (client_surface(c)) {
client_surface(c)->data = c->scene;
/* Ideally we should do this in createnotify{,x11} but at that moment
* wlr_xwayland_surface doesn't have wlr_surface yet. */
LISTEN(&client_surface(c)->events.commit, &c->commit, commitnotify);
}
c->scene->node.data = c->scene_surface->node.data = c;
if (client_is_unmanaged(c)) { if (client_is_unmanaged(c)) {
client_get_geometry(c, &c->geom); client_get_geometry(c, &c->geom);
@ -1254,7 +1292,6 @@ mapnotify(struct wl_listener *listener, void *data)
c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor);
c->border[i]->node.data = c; c->border[i]->node.data = c;
wlr_scene_rect_set_color(c->border[i], bordercolor); wlr_scene_rect_set_color(c->border[i], bordercolor);
wlr_scene_node_lower_to_bottom(&c->border[i]->node);
} }
/* Initialize client geometry with room for border */ /* Initialize client geometry with room for border */
@ -1268,7 +1305,15 @@ mapnotify(struct wl_listener *listener, void *data)
wl_list_insert(&fstack, &c->flink); wl_list_insert(&fstack, &c->flink);
/* Set initial monitor, tags, floating status, and focus */ /* Set initial monitor, tags, floating status, and focus */
if ((p = client_get_parent(c))) {
/* Set the same monitor and tags than its parent */
c->isfloating = 1;
wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
/* TODO recheck if !p->mon is possible with wlroots 0.16.0 */
setmon(c, p->mon ? p->mon : selmon, p->tags);
} else {
applyrules(c); applyrules(c);
}
printstatus(); printstatus();
if (c->isfullscreen) if (c->isfullscreen)
@ -1297,7 +1342,7 @@ monocle(Monitor *m)
wl_list_for_each(c, &clients, link) { wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
continue; continue;
resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0); resize(c, m->w, 0);
} }
focusclient(focustop(m), 1); focusclient(focustop(m), 1);
} }
@ -1339,13 +1384,12 @@ motionnotify(uint32_t time)
/* If we are currently grabbing the mouse, handle and return */ /* If we are currently grabbing the mouse, handle and return */
if (cursor_mode == CurMove) { if (cursor_mode == CurMove) {
/* Move the grabbed client to the new position. */ /* Move the grabbed client to the new position. */
resize(grabc, cursor->x - grabcx, cursor->y - grabcy, resize(grabc, (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy,
grabc->geom.width, grabc->geom.height, 1); .width = grabc->geom.width, .height = grabc->geom.height}, 1);
return; return;
} else if (cursor_mode == CurResize) { } else if (cursor_mode == CurResize) {
resize(grabc, grabc->geom.x, grabc->geom.y, resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
cursor->x - grabc->geom.x, .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1);
cursor->y - grabc->geom.y, 1);
return; return;
} }
@ -1424,11 +1468,27 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
struct wlr_output_configuration_head_v1 *config_head; struct wlr_output_configuration_head_v1 *config_head;
int ok = 1; int ok = 1;
/* First disable outputs we need to disable */
wl_list_for_each(config_head, &config->heads, link) { wl_list_for_each(config_head, &config->heads, link) {
struct wlr_output *wlr_output = config_head->state.output; struct wlr_output *wlr_output = config_head->state.output;
if (!wlr_output->enabled || config_head->state.enabled)
continue;
wlr_output_enable(wlr_output, 0);
if (test) {
ok &= wlr_output_test(wlr_output);
wlr_output_rollback(wlr_output);
} else {
ok &= wlr_output_commit(wlr_output);
}
}
wlr_output_enable(wlr_output, config_head->state.enabled); /* Then enable outputs that need to */
if (config_head->state.enabled) { wl_list_for_each(config_head, &config->heads, link) {
struct wlr_output *wlr_output = config_head->state.output;
if (!config_head->state.enabled)
continue;
wlr_output_enable(wlr_output, 1);
if (config_head->state.mode) if (config_head->state.mode)
wlr_output_set_mode(wlr_output, config_head->state.mode); wlr_output_set_mode(wlr_output, config_head->state.mode);
else else
@ -1441,17 +1501,31 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
config_head->state.x, config_head->state.y); config_head->state.x, config_head->state.y);
wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_transform(wlr_output, config_head->state.transform);
wlr_output_set_scale(wlr_output, config_head->state.scale); wlr_output_set_scale(wlr_output, config_head->state.scale);
if (test) {
ok &= wlr_output_test(wlr_output);
wlr_output_rollback(wlr_output);
} else {
int output_ok = 1;
/* If it's a custom mode to avoid an assertion failed in wlr_output_commit()
* we test if that mode does not fail rather than just call wlr_output_commit().
* We do not test normal modes because (at least in my hardware (@sevz17))
* wlr_output_test() fails even if that mode can actually be set */
if (!config_head->state.mode)
ok &= (output_ok = wlr_output_test(wlr_output)
&& wlr_output_commit(wlr_output));
else
ok &= wlr_output_commit(wlr_output);
/* In custom modes we call wlr_output_test(), it it fails
* we need to rollback, and normal modes seems to does not cause
* assertions failed in wlr_output_commit() which rollback
* the output on failure */
if (!output_ok)
wlr_output_rollback(wlr_output);
}
} }
if (!(ok = wlr_output_test(wlr_output)))
break;
}
wl_list_for_each(config_head, &config->heads, link) {
if (ok && !test)
wlr_output_commit(config_head->state.output);
else
wlr_output_rollback(config_head->state.output);
}
if (ok) if (ok)
wlr_output_configuration_v1_send_succeeded(config); wlr_output_configuration_v1_send_succeeded(config);
else else
@ -1528,7 +1602,6 @@ printstatus(void)
sel, urg); sel, urg);
printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol); printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol);
} }
fflush(stdout);
} }
void void
@ -1577,16 +1650,23 @@ rendermon(struct wl_listener *listener, void *data)
} }
void void
resize(Client *c, int x, int y, int w, int h, int interact) requeststartdrag(struct wl_listener *listener, void *data)
{
struct wlr_seat_request_start_drag_event *event = data;
if (wlr_seat_validate_pointer_grab_serial(seat, event->origin,
event->serial))
wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
else
wlr_data_source_destroy(event->drag->source);
}
void
resize(Client *c, struct wlr_box geo, int interact)
{ {
int min_width = 0, min_height = 0;
struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; struct wlr_box *bbox = interact ? &sgeom : &c->mon->w;
client_set_bounds(c, w, h); client_set_bounds(c, geo.width, geo.height);
client_min_size(c, &min_width, &min_height); c->geom = geo;
c->geom.x = x;
c->geom.y = y;
c->geom.width = MAX(min_width + 2 * c->bw, w);
c->geom.height = MAX(min_height + 2 * c->bw, h);
applybounds(c, bbox); applybounds(c, bbox);
/* Update scene-graph, including borders */ /* Update scene-graph, including borders */
@ -1605,23 +1685,9 @@ resize(Client *c, int x, int y, int w, int h, int interact)
c->geom.height - 2 * c->bw); c->geom.height - 2 * c->bw);
} }
void
requeststartdrag(struct wl_listener *listener, void *data)
{
struct wlr_seat_request_start_drag_event *event = data;
if (wlr_seat_validate_pointer_grab_serial(seat, event->origin,
event->serial))
wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
else
wlr_data_source_destroy(event->drag->source);
}
void void
run(char *startup_cmd) run(char *startup_cmd)
{ {
pid_t startup_pid = -1;
/* Add a Unix socket to the Wayland display. */ /* Add a Unix socket to the Wayland display. */
const char *socket = wl_display_add_socket_auto(dpy); const char *socket = wl_display_add_socket_auto(dpy);
if (!socket) if (!socket)
@ -1633,9 +1699,9 @@ run(char *startup_cmd)
int piperw[2]; int piperw[2];
if (pipe(piperw) < 0) if (pipe(piperw) < 0)
die("startup: pipe:"); die("startup: pipe:");
if ((startup_pid = fork()) < 0) if ((child_pid = fork()) < 0)
die("startup: fork:"); die("startup: fork:");
if (startup_pid == 0) { if (child_pid == 0) {
dup2(piperw[0], STDIN_FILENO); dup2(piperw[0], STDIN_FILENO);
close(piperw[0]); close(piperw[0]);
close(piperw[1]); close(piperw[1]);
@ -1671,11 +1737,6 @@ run(char *startup_cmd)
* loop configuration to listen to libinput events, DRM events, generate * loop configuration to listen to libinput events, DRM events, generate
* frame events at the refresh rate, and so on. */ * frame events at the refresh rate, and so on. */
wl_display_run(dpy); wl_display_run(dpy);
if (startup_cmd) {
kill(startup_pid, SIGTERM);
waitpid(startup_pid, NULL, 0);
}
} }
Client * Client *
@ -1724,11 +1785,29 @@ setfullscreen(Client *c, int fullscreen)
if (fullscreen) { if (fullscreen) {
c->prev = c->geom; c->prev = c->geom;
resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); resize(c, c->mon->m, 0);
/* The xdg-protocol specifies:
*
* If the fullscreened surface is not opaque, the compositor must make
* sure that other screen content not part of the same surface tree (made
* up of subsurfaces, popups or similarly coupled surfaces) are not
* visible below the fullscreened surface.
*
* For brevity we set a black background for all clients
*/
if (!c->fullscreen_bg) {
c->fullscreen_bg = wlr_scene_rect_create(c->scene,
c->geom.width, c->geom.height, fullscreen_bg);
wlr_scene_node_lower_to_bottom(&c->fullscreen_bg->node);
}
} else { } else {
/* restore previous size instead of arrange for floating windows since /* restore previous size instead of arrange for floating windows since
* client positions are set by the user and cannot be recalculated */ * client positions are set by the user and cannot be recalculated */
resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0); resize(c, c->prev, 0);
if (c->fullscreen_bg) {
wlr_scene_node_destroy(&c->fullscreen_bg->node);
c->fullscreen_bg = NULL;
}
} }
arrange(c->mon); arrange(c->mon);
printstatus(); printstatus();
@ -1777,7 +1856,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
} }
if (m) { if (m) {
/* Make sure window actually overlaps with the monitor */ /* Make sure window actually overlaps with the monitor */
resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0); resize(c, c->geom, 0);
wlr_surface_send_enter(client_surface(c), m->wlr_output); wlr_surface_send_enter(client_surface(c), m->wlr_output);
c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
arrange(m); arrange(m);
@ -1810,6 +1889,9 @@ setsel(struct wl_listener *listener, void *data)
void void
setup(void) setup(void)
{ {
/* Force line-buffered stdout */
setvbuf(stdout, NULL, _IOLBF, 0);
/* The Wayland display is managed by libwayland. It handles accepting /* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */ * clients from the Unix socket, manging Wayland globals, and so on. */
dpy = wl_display_create(); dpy = wl_display_create();
@ -2032,7 +2114,7 @@ tagmon(const Arg *arg)
void void
tile(Monitor *m) tile(Monitor *m)
{ {
unsigned int i, n = 0, h, mw, my, ty; unsigned int i, n = 0, mw, my, ty;
Client *c; Client *c;
wl_list_for_each(c, &clients, link) wl_list_for_each(c, &clients, link)
@ -2050,12 +2132,12 @@ tile(Monitor *m)
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
continue; continue;
if (i < m->nmaster) { if (i < m->nmaster) {
h = (m->w.height - my) / (MIN(n, m->nmaster) - i); resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
resize(c, m->w.x, m->w.y + my, mw, h, 0); .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
my += c->geom.height; my += c->geom.height;
} else { } else {
h = (m->w.height - ty) / (n - i); resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
resize(c, m->w.x + mw, m->w.y + ty, m->w.width - mw, h, 0); .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
ty += c->geom.height; ty += c->geom.height;
} }
i++; i++;
@ -2109,22 +2191,20 @@ toggleview(const Arg *arg)
} }
void void
unmaplayersurface(LayerSurface *layersurface) unmaplayersurfacenotify(struct wl_listener *listener, void *data)
{ {
layersurface->layer_surface->mapped = 0; LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap);
layersurface->layer_surface->mapped = (layersurface->mapped = 0);
wlr_scene_node_set_enabled(&layersurface->scene->node, 0);
if (layersurface->layer_surface->surface == exclusive_focus)
exclusive_focus = NULL;
if (layersurface->layer_surface->surface == if (layersurface->layer_surface->surface ==
seat->keyboard_state.focused_surface) seat->keyboard_state.focused_surface)
focusclient(selclient(), 1); focusclient(selclient(), 1);
motionnotify(0); motionnotify(0);
} }
void
unmaplayersurfacenotify(struct wl_listener *listener, void *data)
{
LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap);
unmaplayersurface(layersurface);
}
void void
unmapnotify(struct wl_listener *listener, void *data) unmapnotify(struct wl_listener *listener, void *data)
{ {
@ -2146,6 +2226,7 @@ unmapnotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->link); wl_list_remove(&c->link);
setmon(c, NULL, 0); setmon(c, NULL, 0);
wl_list_remove(&c->flink); wl_list_remove(&c->flink);
wl_list_remove(&c->commit.link);
wlr_scene_node_destroy(&c->scene->node); wlr_scene_node_destroy(&c->scene->node);
printstatus(); printstatus();
} }
@ -2162,6 +2243,7 @@ updatemons(struct wl_listener *listener, void *data)
*/ */
struct wlr_output_configuration_v1 *config = struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create(); wlr_output_configuration_v1_create();
Client *c;
Monitor *m; Monitor *m;
wlr_output_layout_get_box(output_layout, NULL, &sgeom); wlr_output_layout_get_box(output_layout, NULL, &sgeom);
wl_list_for_each(m, &mons, link) { wl_list_for_each(m, &mons, link) {
@ -2186,6 +2268,11 @@ updatemons(struct wl_listener *listener, void *data)
config_head->state.y = m->m.y; config_head->state.y = m->m.y;
} }
if (selmon && selmon->wlr_output->enabled)
wl_list_for_each(c, &clients, link)
if (!c->mon && client_is_mapped(c))
setmon(c, selmon, c->tags);
wlr_output_manager_v1_set_configuration(output_mgr, config); wlr_output_manager_v1_set_configuration(output_mgr, config);
} }
@ -2201,12 +2288,8 @@ void
urgent(struct wl_listener *listener, void *data) urgent(struct wl_listener *listener, void *data)
{ {
struct wlr_xdg_activation_v1_request_activate_event *event = data; struct wlr_xdg_activation_v1_request_activate_event *event = data;
Client *c; Client *c = client_from_wlr_surface(event->surface);
if (c && c != selclient()) {
if (!wlr_surface_is_xdg_surface(event->surface))
return;
c = wlr_xdg_surface_from_wlr_surface(event->surface)->data;
if (c != selclient()) {
c->isurgent = 1; c->isurgent = 1;
printstatus(); printstatus();
} }
@ -2339,7 +2422,6 @@ createnotifyx11(struct wl_listener *listener, void *data)
c->surface.xwayland = xwayland_surface; c->surface.xwayland = xwayland_surface;
c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed;
c->bw = borderpx; c->bw = borderpx;
c->isfullscreen = 0;
/* Listen to the various events it can emit */ /* Listen to the various events it can emit */
LISTEN(&xwayland_surface->events.map, &c->map, mapnotify); LISTEN(&xwayland_surface->events.map, &c->map, mapnotify);

View file

@ -1,13 +0,0 @@
#!/bin/sh
if git tag --contains HEAD | grep -q $1; then
echo $1
else
branch="$(git rev-parse --abbrev-ref HEAD)"
commit="$(git rev-parse --short HEAD)"
if [ "${branch}" != "main" ]; then
echo $1-$branch-$commit
else
echo $1-$commit
fi
fi