summaryrefslogtreecommitdiffstats
path: root/dwl.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dwl.c1044
1 files changed, 951 insertions, 93 deletions
diff --git a/dwl.c b/dwl.c
index 4816159..4358e4b 100644
--- a/dwl.c
+++ b/dwl.c
@@ -11,6 +11,7 @@
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
+#include <regex.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/libinput.h>
@@ -40,6 +41,7 @@
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
+#include <wlr/types/wlr_pointer_gestures_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_primary_selection_v1.h>
@@ -51,6 +53,7 @@
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_tearing_control_v1.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -68,6 +71,7 @@
#include <xcb/xcb_icccm.h>
#endif
+#include "dwl-ipc-unstable-v2-protocol.h"
#include "util.h"
/* macros */
@@ -85,6 +89,13 @@
enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
enum { XDGShell, LayerShell, X11 }; /* client types */
enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
+enum { SWIPE_LEFT, SWIPE_RIGHT, SWIPE_DOWN, SWIPE_UP };
+enum { BrdOriginal, BrdStart, BrdEnd, BrdStartEnd };
+
+typedef struct ForceTearingRule {
+ const char* title;
+ const char* appid;
+} ForceTearingRule;
typedef union {
int i;
@@ -100,14 +111,25 @@ typedef struct {
const Arg arg;
} Button;
-typedef struct Monitor Monitor;
typedef struct {
- /* Must keep this field first */
+ unsigned int mod;
+ unsigned int motion;
+ unsigned int fingers_count;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Gesture;
+
+typedef struct Monitor Monitor;
+typedef struct Client Client;
+struct Client {
+ /* Must keep these three elements in this order */
unsigned int type; /* XDGShell or X11* */
Monitor *mon;
struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
+ struct wlr_scene_rect *borders[4]; /* top, bottom, left, right */
+ struct wlr_scene_rect *bordere[4]; /* top, bottom, left, right */
struct wlr_scene_tree *scene_surface;
struct wl_list link;
struct wl_list flink;
@@ -131,15 +153,27 @@ typedef struct {
#ifdef XWAYLAND
struct wl_listener activate;
struct wl_listener associate;
+ struct wl_listener minimize;
struct wl_listener dissociate;
struct wl_listener configure;
struct wl_listener set_hints;
#endif
unsigned int bw;
+ unsigned int bws;
+ unsigned int bwe;
uint32_t tags;
- int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, isterm, noswallow;
uint32_t resize; /* configure serial of a pending resize */
-} Client;
+ enum wp_tearing_control_v1_presentation_hint tearing_hint;
+ pid_t pid;
+ Client *swallowing, *swallowedby;
+};
+
+typedef struct {
+ struct wl_list link;
+ struct wl_resource *resource;
+ Monitor *mon;
+} DwlIpcOutput;
typedef struct {
uint32_t mod;
@@ -185,6 +219,7 @@ typedef struct {
struct Monitor {
struct wl_list link;
+ struct wl_list dwl_ipc_outputs;
struct wlr_output *wlr_output;
struct wlr_scene_output *scene_output;
struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
@@ -197,6 +232,7 @@ struct Monitor {
struct wlr_box w; /* window area, layout-relative */
struct wl_list layers[4]; /* LayerSurface.link */
const Layout *lt[2];
+ int gappx; /* horizontal outer gaps */
unsigned int seltags;
unsigned int sellt;
uint32_t tagset[2];
@@ -204,6 +240,7 @@ struct Monitor {
int gamma_lut_changed;
int nmaster;
char ltsymbol[16];
+ char *name;
int asleep;
};
@@ -227,6 +264,8 @@ typedef struct {
const char *title;
uint32_t tags;
int isfloating;
+ int isterm;
+ int noswallow;
int monitor;
} Rule;
@@ -239,6 +278,19 @@ typedef struct {
struct wl_listener destroy;
} SessionLock;
+typedef struct TearingController {
+ struct wlr_tearing_control_v1 *tearing_control;
+ struct wl_listener set_hint;
+ struct wl_listener destroy;
+
+ struct wl_list link; /* tearing_controllers */
+} TearingController;
+
+typedef struct SendFrameDoneData {
+ struct timespec when;
+ struct Monitor *mon;
+} SendFrameDoneData;
+
/* function declarations */
static void applybounds(Client *c, struct wlr_box *bbox);
static void applyrules(Client *c);
@@ -248,6 +300,15 @@ static void arrangelayer(Monitor *m, struct wl_list *list,
static void arrangelayers(Monitor *m);
static void axisnotify(struct wl_listener *listener, void *data);
static void buttonpress(struct wl_listener *listener, void *data);
+static int ongesture(struct wlr_pointer_swipe_end_event *event);
+static void swipe_begin(struct wl_listener *listener, void *data);
+static void swipe_update(struct wl_listener *listener, void *data);
+static void swipe_end(struct wl_listener *listener, void *data);
+static void pinch_begin(struct wl_listener *listener, void *data);
+static void pinch_update(struct wl_listener *listener, void *data);
+static void pinch_end(struct wl_listener *listener, void *data);
+static void hold_begin(struct wl_listener *listener, void *data);
+static void hold_end(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
static void checkidleinhibitor(struct wlr_surface *exclude);
static void cleanup(void);
@@ -282,6 +343,17 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data);
static void destroysessionlock(struct wl_listener *listener, void *data);
static void destroykeyboardgroup(struct wl_listener *listener, void *data);
static Monitor *dirtomon(enum wlr_direction dir);
+static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id);
+static void dwl_ipc_manager_destroy(struct wl_resource *resource);
+static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output);
+static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource);
+static void dwl_ipc_output_destroy(struct wl_resource *resource);
+static void dwl_ipc_output_printstatus(Monitor *monitor);
+static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output);
+static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags);
+static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index);
+static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset);
+static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource);
static void focusclient(Client *c, int lift);
static void focusmon(const Arg *arg);
static void focusstack(const Arg *arg);
@@ -289,9 +361,13 @@ static Client *focustop(Monitor *m);
static void fullscreennotify(struct wl_listener *listener, void *data);
static void gpureset(struct wl_listener *listener, void *data);
static void handlesig(int signo);
+static void handletearingcontrollersethint(struct wl_listener *listener, void *data);
+static void handletearingcontrollerdestroy(struct wl_listener *listener, void *data);
+static void handlenewtearinghint(struct wl_listener *listener, void *data);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym);
+static int lockedkeybinding(uint32_t mods, xkb_keysym_t sym);
static void keypress(struct wl_listener *listener, void *data);
static void keypressmod(struct wl_listener *listener, void *data);
static int keyrepeat(void *data);
@@ -305,6 +381,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double
double sy, double sx_unaccel, double sy_unaccel);
static void motionrelative(struct wl_listener *listener, void *data);
static void moveresize(const Arg *arg);
+static int moncantear(Monitor* m);
static void outputmgrapply(struct wl_listener *listener, void *data);
static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
static void outputmgrtest(struct wl_listener *listener, void *data);
@@ -319,6 +396,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data);
static void requestmonstate(struct wl_listener *listener, void *data);
static void resize(Client *c, struct wlr_box geo, int interact);
static void run(char *startup_cmd);
+static void sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data);
static void setcursor(struct wl_listener *listener, void *data);
static void setcursorshape(struct wl_listener *listener, void *data);
static void setfloating(Client *c, int floating);
@@ -334,8 +412,12 @@ static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *m);
+static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
+static void togglegaps(const Arg *arg);
+static void togglepointerconstraints(const Arg *arg);
+static void togglepassthrough(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unlocksession(struct wl_listener *listener, void *data);
@@ -351,6 +433,11 @@ static Monitor *xytomon(double x, double y);
static void xytonode(double x, double y, struct wlr_surface **psurface,
Client **pc, LayerSurface **pl, double *nx, double *ny);
static void zoom(const Arg *arg);
+static int regex_match(const char *pattern, const char *str);
+static pid_t getparentprocess(pid_t p);
+static int isdescprocess(pid_t p, pid_t c);
+static Client *termforwin(Client *w);
+static void swallow(Client *c, Client *w);
/* variables */
static pid_t child_pid = -1;
@@ -382,6 +469,7 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
static struct wlr_output_power_manager_v1 *power_mgr;
+static struct wlr_pointer_gestures_v1 *pointer_gestures;
static struct wlr_pointer_constraints_v1 *pointer_constraints;
static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
@@ -395,17 +483,29 @@ static struct wlr_session_lock_manager_v1 *session_lock_mgr;
static struct wlr_scene_rect *locked_bg;
static struct wlr_session_lock_v1 *cur_lock;
+struct wlr_tearing_control_manager_v1 *tearing_control_v1;
+struct wl_listener tearing_control_new_object;
+struct wl_list tearing_controllers;
+
static struct wlr_seat *seat;
static KeyboardGroup *kb_group;
static unsigned int cursor_mode;
static Client *grabc;
static int grabcx, grabcy; /* client-relative */
+static int enable_constraints = 1;
+
static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static uint32_t swipe_fingers = 0;
+static double swipe_dx = 0;
+static double swipe_dy = 0;
+
+static int enablegaps = 1; /* enables gaps, used by togglegaps */
+
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
static struct wl_listener cursor_button = {.notify = buttonpress};
@@ -435,6 +535,8 @@ static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag};
static struct wl_listener new_session_lock = {.notify = locksession};
+static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output};
+static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags};
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
@@ -442,6 +544,8 @@ static void associatex11(struct wl_listener *listener, void *data);
static void configurex11(struct wl_listener *listener, void *data);
static void createnotifyx11(struct wl_listener *listener, void *data);
static void dissociatex11(struct wl_listener *listener, void *data);
+static xcb_atom_t getatom(xcb_connection_t *xc, const char *name);
+static void minimizenotify(struct wl_listener *listener, void *data);
static void sethints(struct wl_listener *listener, void *data);
static void xwaylandready(struct wl_listener *listener, void *data);
static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11};
@@ -455,6 +559,8 @@ static struct wlr_xwayland *xwayland;
/* attempt to encapsulate suck into one file */
#include "client.h"
+static const unsigned int abzsquare = swipe_min_threshold * swipe_min_threshold;
+
/* function implementations */
void
applybounds(Client *c, struct wlr_box *bbox)
@@ -487,10 +593,14 @@ applyrules(Client *c)
appid = client_get_appid(c);
title = client_get_title(c);
+ c->pid = client_get_pid(c);
+
for (r = rules; r < END(rules); r++) {
- if ((!r->title || strstr(title, r->title))
- && (!r->id || strstr(appid, r->id))) {
+ if ((!r->title || regex_match(r->title, title))
+ && (!r->id || regex_match(r->id, appid))) {
c->isfloating = r->isfloating;
+ c->isterm = r->isterm;
+ c->noswallow = r->noswallow;
newtags |= r->tags;
i = 0;
wl_list_for_each(m, &mons, link) {
@@ -499,6 +609,21 @@ applyrules(Client *c)
}
}
}
+ if (!c->noswallow && !client_is_float_type(c)
+ && !c->surface.xdg->initial_commit) {
+ Client *p = termforwin(c);
+ if (p) {
+ c->swallowedby = p;
+ p->swallowing = c;
+ wl_list_remove(&c->link);
+ wl_list_remove(&c->flink);
+ swallow(c, p);
+ wl_list_remove(&p->link);
+ wl_list_remove(&p->flink);
+ mon = p->mon;
+ newtags = p->tags;
+ }
+ }
setmon(c, mon, newtags);
}
@@ -645,6 +770,11 @@ buttonpress(struct wl_listener *listener, void *data)
for (b = buttons; b < END(buttons); b++) {
if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
event->button == b->button && b->func) {
+ if (passthrough) {
+ if (b->func != togglepassthrough) continue;
+ b->func(&b->arg);
+ break;
+ }
b->func(&b->arg);
return;
}
@@ -672,6 +802,174 @@ buttonpress(struct wl_listener *listener, void *data)
}
void
+swipe_begin(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_swipe_begin_event *event = data;
+
+ swipe_fingers = event->fingers;
+ // Reset swipe distance at the beginning of a swipe
+ swipe_dx = 0;
+ swipe_dy = 0;
+
+ // Forward swipe begin event to client
+ wlr_pointer_gestures_v1_send_swipe_begin(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->fingers
+ );
+}
+
+void
+swipe_update(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_swipe_update_event *event = data;
+
+ swipe_fingers = event->fingers;
+ // Accumulate swipe distance
+ swipe_dx += event->dx;
+ swipe_dy += event->dy;
+
+ // Forward swipe update event to client
+ wlr_pointer_gestures_v1_send_swipe_update(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->dx,
+ event->dy
+ );
+}
+
+int
+ongesture(struct wlr_pointer_swipe_end_event *event)
+{
+ struct wlr_keyboard *keyboard;
+ uint32_t mods;
+ const Gesture *g;
+ unsigned int motion;
+ unsigned int adx = (int)round(fabs(swipe_dx));
+ unsigned int ady = (int)round(fabs(swipe_dy));
+ int handled = 0;
+
+ if (event->cancelled) {
+ return handled;
+ }
+
+ // Require absolute distance movement beyond a small thresh-hold
+ if (adx * adx + ady * ady < abzsquare) {
+ return handled;
+ }
+
+ if (adx > ady) {
+ motion = swipe_dx < 0 ? SWIPE_LEFT : SWIPE_RIGHT;
+ } else {
+ motion = swipe_dy < 0 ? SWIPE_UP : SWIPE_DOWN;
+ }
+
+ keyboard = wlr_seat_get_keyboard(seat);
+ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
+ for (g = gestures; g < END(gestures); g++) {
+ if (CLEANMASK(mods) == CLEANMASK(g->mod) &&
+ swipe_fingers == g->fingers_count &&
+ motion == g->motion && g->func) {
+ g->func(&g->arg);
+ handled = 1;
+ }
+ }
+ return handled;
+}
+
+void
+swipe_end(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_swipe_end_event *event = data;
+
+ // TODO: should we stop here if the event has been handled?
+ ongesture(event);
+
+ // Forward swipe end event to client
+ wlr_pointer_gestures_v1_send_swipe_end(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->cancelled
+ );
+}
+
+void
+pinch_begin(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_pinch_begin_event *event = data;
+
+ // Forward pinch begin event to client
+ wlr_pointer_gestures_v1_send_pinch_begin(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->fingers
+ );
+}
+
+void
+pinch_update(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_pinch_update_event *event = data;
+
+ // Forward pinch update event to client
+ wlr_pointer_gestures_v1_send_pinch_update(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->dx,
+ event->dy,
+ event->scale,
+ event->rotation
+ );
+}
+
+void
+pinch_end(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_pinch_end_event *event = data;
+
+ // Forward pinch end event to client
+ wlr_pointer_gestures_v1_send_pinch_end(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->cancelled
+ );
+}
+
+void
+hold_begin(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_hold_begin_event *event = data;
+
+ // Forward hold begin event to client
+ wlr_pointer_gestures_v1_send_hold_begin(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->fingers
+ );
+}
+
+void
+hold_end(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_hold_end_event *event = data;
+
+ // Forward hold end event to client
+ wlr_pointer_gestures_v1_send_hold_end(
+ pointer_gestures,
+ seat,
+ event->time_msec,
+ event->cancelled
+ );
+}
+
+void
chvt(const Arg *arg)
{
wlr_session_change_vt(session, arg->ui);
@@ -729,6 +1027,10 @@ cleanupmon(struct wl_listener *listener, void *data)
LayerSurface *l, *tmp;
size_t i;
+ DwlIpcOutput *ipc_output, *ipc_output_tmp;
+ wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link)
+ wl_resource_destroy(ipc_output->resource);
+
/* m->layers[i] are intentionally not unlinked */
for (i = 0; i < LENGTH(m->layers); i++) {
wl_list_for_each_safe(l, tmp, &m->layers[i], link)
@@ -1049,13 +1351,16 @@ createmon(struct wl_listener *listener, void *data)
m = wlr_output->data = ecalloc(1, sizeof(*m));
m->wlr_output = wlr_output;
+ wl_list_init(&m->dwl_ipc_outputs);
+
for (i = 0; i < LENGTH(m->layers); i++)
wl_list_init(&m->layers[i]);
-
wlr_output_state_init(&state);
/* Initialize monitor state using configured rules */
+ m->gappx = gappx;
m->tagset[0] = m->tagset[1] = 1;
for (r = monrules; r < END(monrules); r++) {
+ m->name = wlr_output->name;
if (!r->name || strstr(wlr_output->name, r->name)) {
m->m.x = r->x;
m->m.y = r->y;
@@ -1124,6 +1429,8 @@ createnotify(struct wl_listener *listener, void *data)
c = toplevel->base->data = ecalloc(1, sizeof(*c));
c->surface.xdg = toplevel->base;
c->bw = borderpx;
+ c->bws = borders_only_floating ? 0 : borderspx;
+ c->bwe = borders_only_floating ? 0 : borderepx;
LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify);
LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify);
@@ -1334,6 +1641,7 @@ destroynotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->activate.link);
wl_list_remove(&c->associate.link);
wl_list_remove(&c->configure.link);
+ wl_list_remove(&c->minimize.link);
wl_list_remove(&c->dissociate.link);
wl_list_remove(&c->set_hints.link);
} else
@@ -1397,6 +1705,192 @@ dirtomon(enum wlr_direction dir)
}
void
+dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id);
+ if (!manager_resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy);
+
+ zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT);
+
+ for (unsigned int i = 0; i < LENGTH(layouts); i++)
+ zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol);
+}
+
+void
+dwl_ipc_manager_destroy(struct wl_resource *resource)
+{
+ /* No state to destroy */
+}
+
+void
+dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output)
+{
+ DwlIpcOutput *ipc_output;
+ Monitor *monitor = wlr_output_from_resource(output)->data;
+ struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id);
+ if (!output_resource)
+ return;
+
+ ipc_output = ecalloc(1, sizeof(*ipc_output));
+ ipc_output->resource = output_resource;
+ ipc_output->mon = monitor;
+ wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy);
+ wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link);
+ dwl_ipc_output_printstatus_to(ipc_output);
+}
+
+void
+dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+dwl_ipc_output_destroy(struct wl_resource *resource)
+{
+ DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource);
+ wl_list_remove(&ipc_output->link);
+ free(ipc_output);
+}
+
+void
+dwl_ipc_output_printstatus(Monitor *monitor)
+{
+ DwlIpcOutput *ipc_output;
+ wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link)
+ dwl_ipc_output_printstatus_to(ipc_output);
+}
+
+void
+dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output)
+{
+ Monitor *monitor = ipc_output->mon;
+ Client *c, *focused;
+ int tagmask, state, numclients, focused_client, tag;
+ const char *title, *appid;
+ focused = focustop(monitor);
+ zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon);
+
+ for (tag = 0 ; tag < TAGCOUNT; tag++) {
+ numclients = state = focused_client = 0;
+ tagmask = 1 << tag;
+ if ((tagmask & monitor->tagset[monitor->seltags]) != 0)
+ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE;
+
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon != monitor)
+ continue;
+ if (!(c->tags & tagmask))
+ continue;
+ if (c == focused)
+ focused_client = 1;
+ if (c->isurgent)
+ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT;
+
+ numclients++;
+ }
+ zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client);
+ }
+ title = focused ? client_get_title(focused) : "";
+ appid = focused ? client_get_appid(focused) : "";
+
+ zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts);
+ zdwl_ipc_output_v2_send_title(ipc_output->resource, title);
+ zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid);
+ zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol);
+ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) {
+ zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0);
+ }
+ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) {
+ zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0);
+ }
+ zdwl_ipc_output_v2_send_frame(ipc_output->resource);
+}
+
+void
+dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags)
+{
+ DwlIpcOutput *ipc_output;
+ Monitor *monitor;
+ Client *selected_client;
+ unsigned int newtags = 0;
+
+ ipc_output = wl_resource_get_user_data(resource);
+ if (!ipc_output)
+ return;
+
+ monitor = ipc_output->mon;
+ selected_client = focustop(monitor);
+ if (!selected_client)
+ return;
+
+ newtags = (selected_client->tags & and_tags) ^ xor_tags;
+ if (!newtags)
+ return;
+
+ selected_client->tags = newtags;
+ if (selmon == monitor)
+ focusclient(focustop(monitor), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index)
+{
+ DwlIpcOutput *ipc_output;
+ Monitor *monitor;
+
+ ipc_output = wl_resource_get_user_data(resource);
+ if (!ipc_output)
+ return;
+
+ monitor = ipc_output->mon;
+ if (index >= LENGTH(layouts))
+ return;
+ if (index != monitor->lt[monitor->sellt] - layouts)
+ monitor->sellt ^= 1;
+
+ monitor->lt[monitor->sellt] = &layouts[index];
+ arrange(monitor);
+ printstatus();
+}
+
+void
+dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset)
+{
+ DwlIpcOutput *ipc_output;
+ Monitor *monitor;
+ unsigned int newtags = tagmask & TAGMASK;
+
+ ipc_output = wl_resource_get_user_data(resource);
+ if (!ipc_output)
+ return;
+ monitor = ipc_output->mon;
+
+ if (!newtags || newtags == monitor->tagset[monitor->seltags])
+ return;
+ if (toggle_tagset)
+ monitor->seltags ^= 1;
+
+ monitor->tagset[monitor->seltags] = newtags;
+ if (selmon == monitor)
+ focusclient(focustop(monitor), 1);
+ arrange(monitor);
+ printstatus();
+}
+
+void
+dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+void
focusclient(Client *c, int lift)
{
struct wlr_surface *old = seat->keyboard_state.focused_surface;
@@ -1427,10 +1921,7 @@ focusclient(Client *c, int lift)
selmon = c->mon;
c->isurgent = 0;
- /* Don't change border color if there is an exclusive focus or we are
- * handling a drag operation */
- if (!exclusive_focus && !seat->drag)
- client_set_border_color(c, focuscolor);
+ client_set_border_color(c, focuscolor);
}
/* Deactivate old client if focus is changing */
@@ -1447,7 +1938,7 @@ focusclient(Client *c, int lift)
/* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
* and probably other clients */
} else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
- client_set_border_color(old_c, bordercolor);
+ client_set_border_color(old_c, bordercolor);
client_activate_surface(old, 0);
}
@@ -1473,6 +1964,8 @@ focusclient(Client *c, int lift)
void
focusmon(const Arg *arg)
{
+ Client *c;
+
int i = 0, nmons = wl_list_length(&mons);
if (nmons) {
do /* don't switch to disabled mons */
@@ -1480,6 +1973,17 @@ focusmon(const Arg *arg)
while (!selmon->wlr_output->enabled && i++ < nmons);
}
focusclient(focustop(selmon), 1);
+
+ if (warpcursor) {
+ wlr_cursor_warp_closest(cursor, NULL, selmon->w.x + selmon->w.width / 2.0,
+ selmon->w.y + selmon->w.height / 2.0);
+ c = focustop(selmon);
+ if (c) {
+ wlr_cursor_warp_closest(cursor, NULL, c->geom.x + c->geom.width / 2.0,
+ c->geom.y + c->geom.height / 2.0);
+ }
+ motionnotify(0, NULL, 0, 0, 0, 0);
+ }
}
void
@@ -1506,6 +2010,12 @@ focusstack(const Arg *arg)
}
/* If only one client is visible on selmon, then c == sel */
focusclient(c, 1);
+
+ if (warpcursor) {
+ wlr_cursor_warp_closest( cursor, NULL, c->geom.x + c->geom.width / 2.0,
+ c->geom.y + c->geom.height / 2.0);
+ motionnotify(0, NULL, 0, 0, 0, 0);
+ }
}
/* We probably should change the name of this, it sounds like
@@ -1564,12 +2074,150 @@ handlesig(int signo)
}
void
+handletearingcontrollersethint(struct wl_listener *listener, void *data)
+{
+ Client *c = NULL, *i = NULL;
+ struct TearingController *controller = wl_container_of(listener, controller, set_hint);
+
+ struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(controller->tearing_control->surface);
+#ifdef XWAYLAND
+ struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(controller->tearing_control->surface);
+#endif
+
+ wl_list_for_each(i, &fstack, flink) {
+ if (i->surface.xdg == surface
+#ifdef XWAYLAND
+ || i->surface.xwayland == xsurface
+#endif
+ ) {
+ c = i;
+ break;
+ }
+ }
+
+ if (c) {
+ enum wp_tearing_control_v1_presentation_hint hint = controller->tearing_control->current;
+ fprintf(
+ stderr, "TEARING: found surface: %p(appid: '%s', title: '%s'), hint: %d(%s)\n",
+ (void*)c, client_get_appid(c), client_get_title(c), hint, hint ? "ASYNC" : "VSYNC"
+ );
+ c->tearing_hint = controller->tearing_control->current;
+ }
+}
+
+void
+handletearingcontrollerdestroy(struct wl_listener *listener, void *data)
+{
+ struct TearingController *controller = wl_container_of(listener, controller, destroy);
+
+ wl_list_remove(&controller->set_hint.link);
+ wl_list_remove(&controller->destroy.link);
+ wl_list_remove(&controller->link);
+ free(controller);
+}
+
+void
+handlenewtearinghint(struct wl_listener *listener, void *data)
+{
+ struct wlr_tearing_control_v1 *tearing_control = data;
+ struct TearingController *controller = calloc(1, sizeof(struct TearingController));
+
+ if (!controller)
+ return;
+
+ controller->tearing_control = tearing_control;
+ controller->set_hint.notify = handletearingcontrollersethint;
+ wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
+
+ controller->destroy.notify = handletearingcontrollerdestroy;
+ wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
+
+ wl_list_init(&controller->link);
+ wl_list_insert(&tearing_controllers, &controller->link);
+}
+
+pid_t
+getparentprocess(pid_t p)
+{
+ unsigned int v = 0;
+
+ FILE *f;
+ char buf[256];
+ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
+
+ if (!(f = fopen(buf, "r")))
+ return 0;
+
+ fscanf(f, "%*u %*s %*c %u", &v);
+ fclose(f);
+
+ return (pid_t)v;
+}
+
+int
+isdescprocess(pid_t p, pid_t c)
+{
+ while (p != c && c != 0)
+ c = getparentprocess(c);
+
+ return (int)c;
+}
+
+Client *
+termforwin(Client *w)
+{
+ Client *c;
+
+ if (!w->pid || w->isterm || w->noswallow)
+ return NULL;
+
+ wl_list_for_each(c, &fstack, flink)
+ if (c->isterm && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
+ return c;
+
+ return NULL;
+}
+
+void
+swallow(Client *c, Client *w)
+{
+ c->bw = w->bw;
+ c->isfloating = w->isfloating;
+ c->isurgent = w->isurgent;
+ c->isfullscreen = w->isfullscreen;
+ c->tags = w->tags;
+ c->geom = w->geom;
+ wl_list_insert(&w->link, &c->link);
+ wl_list_insert(&w->flink, &c->flink);
+ wlr_scene_node_set_enabled(&w->scene->node, 0);
+ wlr_scene_node_set_enabled(&c->scene->node, 1);
+}
+
+void
incnmaster(const Arg *arg)
{
+ Client *c;
+ const MonitorRule *r;
+
if (!arg || !selmon)
return;
+ if (arg->i == 0) {
+ for (r = monrules; r < END(monrules); r++)
+ if (!r->name || strstr(selmon->name, r->name))
+ selmon->nmaster = r->nmaster;
+ }
selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
arrange(selmon);
+
+
+ if (warpcursor) {
+ c = focustop(selmon);
+ if (c) {
+ wlr_cursor_warp_closest(cursor, NULL, c->geom.x + c->geom.width / 2.0,
+ c->geom.y + c->geom.height / 2.0);
+ motionnotify(0, NULL, 0, 0, 0, 0);
+ }
+ }
}
void
@@ -1614,6 +2262,8 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
for (k = keys; k < END(keys); k++) {
if (CLEANMASK(mods) == CLEANMASK(k->mod)
&& sym == k->keysym && k->func) {
+ if (passthrough && k->func != togglepassthrough)
+ continue;
k->func(&k->arg);
return 1;
}
@@ -1621,6 +2271,21 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
return 0;
}
+int
+lockedkeybinding(uint32_t mods, xkb_keysym_t sym)
+{
+ int handled = 0;
+ const Key *k;
+ for (k = lockedkeys; k < END(lockedkeys); k++) {
+ if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
+ sym == k->keysym && k->func) {
+ k->func(&k->arg);
+ handled = 1;
+ }
+ }
+ return handled;
+}
+
void
keypress(struct wl_listener *listener, void *data)
{
@@ -1648,6 +2313,10 @@ keypress(struct wl_listener *listener, void *data)
handled = keybinding(mods, syms[i]) || handled;
}
+ if (locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ for (i = 0; i < nsyms; i++)
+ handled = lockedkeybinding(mods, syms[i]) || handled;
+
if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) {
group->mods = mods;
group->keysyms = syms;
@@ -1730,6 +2399,33 @@ locksession(struct wl_listener *listener, void *data)
wlr_session_lock_v1_send_locked(session_lock);
}
+static inline void
+forcetearingrule(Client *c)
+{
+ int success = 0;
+ const char* appid = client_get_appid(c);
+ const char* title = client_get_title(c);
+
+ for (unsigned i = 0; i < LENGTH(force_tearing); i++) {
+ if (appid)
+ if (strcmp(force_tearing[i].appid, appid) == 0) {
+ success = 1;
+ break;
+ }
+
+ if (title)
+ if (strcmp(force_tearing[i].title, title) == 0) {
+ success = 1;
+ break;
+ }
+ }
+
+ if (success) {
+ c->tearing_hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
+ fprintf(stderr, "tearing forced for: appid: '%s', title: '%s'\n", appid, title);
+ }
+}
+
void
mapnotify(struct wl_listener *listener, void *data)
{
@@ -1739,6 +2435,8 @@ mapnotify(struct wl_listener *listener, void *data)
Monitor *m;
int i;
+ forcetearingrule(c);
+
/* Create scene tree for this client and its border */
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
/* Enabled later by a call to arrange() */
@@ -1767,6 +2465,12 @@ mapnotify(struct wl_listener *listener, void *data)
c->border[i] = wlr_scene_rect_create(c->scene, 0, 0,
c->isurgent ? urgentcolor : bordercolor);
c->border[i]->node.data = c;
+
+ c->borders[i] = wlr_scene_rect_create(c->scene, 0, 0, borderscolor);
+ c->borders[i]->node.data = c;
+
+ c->bordere[i] = wlr_scene_rect_create(c->scene, 0, 0, borderecolor);
+ c->bordere[i]->node.data = c;
}
/* Initialize client geometry with room for border */
@@ -1788,6 +2492,12 @@ mapnotify(struct wl_listener *listener, void *data)
} else {
applyrules(c);
}
+
+ if (borders_only_floating) {
+ c->bws = c->isfloating ? borderspx : 0;
+ c->bwe = c->isfloating ? borderepx : 0;
+ }
+
printstatus();
unset_fullscreen:
@@ -1820,12 +2530,16 @@ void
monocle(Monitor *m)
{
Client *c;
- int n = 0;
+ int n = 0, e = enablegaps;
+
+ if (smartgaps == 1) {
+ e = 0; // outer gaps disabled
+ }
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
continue;
- resize(c, m->w, 0);
+ resize(c, (struct wlr_box){ .x = m->w.x + m->gappx*e, .y = m->w.y + m->gappx*e, .width = m->w.width - 2*m->gappx*e, .height = m->w.height - 2*m->gappx*e }, 0);
n++;
}
if (n)
@@ -1883,22 +2597,24 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
relative_pointer_mgr, seat, (uint64_t)time * 1000,
dx, dy, dx_unaccel, dy_unaccel);
- wl_list_for_each(constraint, &pointer_constraints->constraints, link)
- cursorconstrain(constraint);
-
- if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) {
- toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
- if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
- sx = cursor->x - c->geom.x - c->bw;
- sy = cursor->y - c->geom.y - c->bw;
- if (wlr_region_confine(&active_constraint->region, sx, sy,
- sx + dx, sy + dy, &sx_confined, &sy_confined)) {
- dx = sx_confined - sx;
- dy = sy_confined - sy;
+ if (enable_constraints){
+ wl_list_for_each(constraint, &pointer_constraints->constraints, link)
+ cursorconstrain(constraint);
+
+ if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) {
+ toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
+ if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
+ sx = cursor->x - c->geom.x - c->bw;
+ sy = cursor->y - c->geom.y - c->bw;
+ if (wlr_region_confine(&active_constraint->region, sx, sy,
+ sx + dx, sy + dy, &sx_confined, &sy_confined)) {
+ dx = sx_confined - sx;
+ dy = sy_confined - sy;
+ }
+
+ if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
+ return;
}
-
- if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
- return;
}
}
@@ -1909,6 +2625,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
if (sloppyfocus)
selmon = xytomon(cursor->x, cursor->y);
}
+ client_set_border_color(focustop(selmon), focuscolor);
/* Update drag icon's position */
wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
@@ -1977,6 +2694,13 @@ moveresize(const Arg *arg)
}
}
+int
+moncantear(Monitor* m)
+{
+ Client *c = focustop(m);
+ return (c && c->isfullscreen && c->tearing_hint); /* 1 == ASYNC */
+}
+
void
outputmgrapply(struct wl_listener *listener, void *data)
{
@@ -2060,7 +2784,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
{
struct timespec now;
- if (surface != seat->pointer_state.focused_surface &&
+ if ((!active_constraint || active_constraint->surface != surface) &&
sloppyfocus && time && c && !client_is_unmanaged(c))
focusclient(c, 0);
@@ -2086,38 +2810,9 @@ void
printstatus(void)
{
Monitor *m = NULL;
- Client *c;
- uint32_t occ, urg, sel;
- wl_list_for_each(m, &mons, link) {
- occ = urg = 0;
- wl_list_for_each(c, &clients, link) {
- if (c->mon != m)
- continue;
- occ |= c->tags;
- if (c->isurgent)
- urg |= c->tags;
- }
- if ((c = focustop(m))) {
- printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
- printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));
- printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen);
- printf("%s floating %d\n", m->wlr_output->name, c->isfloating);
- sel = c->tags;
- } else {
- printf("%s title \n", m->wlr_output->name);
- printf("%s appid \n", m->wlr_output->name);
- printf("%s fullscreen \n", m->wlr_output->name);
- printf("%s floating \n", m->wlr_output->name);
- sel = 0;
- }
-
- printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
- printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n",
- m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
- printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
- }
- fflush(stdout);
+ wl_list_for_each(m, &mons, link)
+ dwl_ipc_output_printstatus(m);
}
void
@@ -2147,27 +2842,40 @@ quit(const Arg *arg)
void
rendermon(struct wl_listener *listener, void *data)
{
- /* This function is called every time an output is ready to display a frame,
- * generally at the output's refresh rate (e.g. 60Hz). */
Monitor *m = wl_container_of(listener, m, frame);
- Client *c;
+ struct wlr_scene_output *scene_output = m->scene_output;
struct wlr_output_state pending = {0};
- struct timespec now;
+ SendFrameDoneData frame_done_data = {0};
- /* Render if no XDG clients have an outstanding resize and are visible on
- * this monitor. */
- wl_list_for_each(c, &clients, link) {
- if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
- goto skip;
+ m->wlr_output->frame_pending = false;
+
+ if (!wlr_scene_output_needs_frame(scene_output)) {
+ goto skip;
}
- wlr_scene_output_commit(m->scene_output, NULL);
+ wlr_output_state_init(&pending);
+ if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL)) {
+ goto skip;
+ }
+
+ if (tearing_allowed && moncantear(m)) {
+ pending.tearing_page_flip = true;
+
+ if (!wlr_output_test_state(m->wlr_output, &pending)) {
+ fprintf(stderr, "Output test failed on '%s', retrying without tearing page-flip\n", m->wlr_output->name);
+ pending.tearing_page_flip = false;
+ }
+ }
+
+ if (!wlr_output_commit_state(m->wlr_output, &pending))
+ fprintf(stderr, "Page-flip failed on output %s", m->wlr_output->name);
-skip:
- /* Let clients know a frame has been rendered */
- clock_gettime(CLOCK_MONOTONIC, &now);
- wlr_scene_output_send_frame_done(m->scene_output, &now);
wlr_output_state_finish(&pending);
+
+skip:
+ clock_gettime(CLOCK_MONOTONIC, &frame_done_data.when);
+ frame_done_data.mon = m;
+ wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator, &frame_done_data);
}
void
@@ -2225,6 +2933,24 @@ resize(Client *c, struct wlr_box geo, int interact)
wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
+ wlr_scene_rect_set_size(c->borders[0], c->geom.width - 2 * borderspx_offset, c->bws);
+ wlr_scene_rect_set_size(c->borders[1], c->geom.width - 2 * borderspx_offset, c->bws);
+ wlr_scene_rect_set_size(c->borders[2], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset);
+ wlr_scene_rect_set_size(c->borders[3], c->bws, c->geom.height - 2 * c->bws - 2 * borderspx_offset);
+ wlr_scene_node_set_position(&c->borders[0]->node, borderspx_offset, borderspx_offset);
+ wlr_scene_node_set_position(&c->borders[1]->node, borderspx_offset, c->geom.height - c->bws - borderspx_offset);
+ wlr_scene_node_set_position(&c->borders[2]->node, borderspx_offset, c->bws + borderspx_offset);
+ wlr_scene_node_set_position(&c->borders[3]->node, c->geom.width - c->bws - borderspx_offset, c->bws + borderspx_offset);
+
+ wlr_scene_rect_set_size(c->bordere[0], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe);
+ wlr_scene_rect_set_size(c->bordere[1], c->geom.width - (c->bw - c->bwe) * 2 + borderepx_negative_offset * 2, c->bwe);
+ wlr_scene_rect_set_size(c->bordere[2], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset);
+ wlr_scene_rect_set_size(c->bordere[3], c->bwe, c->geom.height - 2 * c->bw + 2 * borderepx_negative_offset);
+ wlr_scene_node_set_position(&c->bordere[0]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - c->bwe - borderepx_negative_offset);
+ wlr_scene_node_set_position(&c->bordere[1]->node, c->bw - c->bwe - borderepx_negative_offset, c->geom.height - c->bw + borderepx_negative_offset);
+ wlr_scene_node_set_position(&c->bordere[2]->node, c->bw - c->bwe - borderepx_negative_offset, c->bw - borderepx_negative_offset);
+ wlr_scene_node_set_position(&c->bordere[3]->node, c->geom.width - c->bw + borderepx_negative_offset, c->bw - borderepx_negative_offset);
+
/* this is a no-op if size hasn't changed */
c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 2 * c->bw);
@@ -2292,6 +3018,16 @@ run(char *startup_cmd)
}
void
+sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data)
+{
+ SendFrameDoneData *data = user_data;
+ if (buffer->primary_output != data->mon->scene_output)
+ return;
+
+ wlr_scene_buffer_send_frame_done(buffer, &data->when);
+}
+
+void
setcursor(struct wl_listener *listener, void *data)
{
/* This event is raised by the seat when a client provides a cursor image */
@@ -2333,6 +3069,12 @@ setfloating(Client *c, int floating)
/* If in floating layout do not change the client's layer */
if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
return;
+
+ if (borders_only_floating) {
+ c->bws = c->isfloating ? borderspx : 0;
+ c->bwe = c->isfloating ? borderepx : 0;
+ }
+
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
(p && p->isfullscreen) ? LyrFS
: c->isfloating ? LyrFloat : LyrTile]);
@@ -2347,6 +3089,8 @@ setfullscreen(Client *c, int fullscreen)
if (!c->mon || !client_surface(c)->mapped)
return;
c->bw = fullscreen ? 0 : borderpx;
+ c->bws = fullscreen ? 0 : borderspx;
+ c->bwe = fullscreen ? 0 : borderepx;
client_set_fullscreen(c, fullscreen);
wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
@@ -2382,14 +3126,30 @@ void
setmfact(const Arg *arg)
{
float f;
+ const MonitorRule *r;
+ Client *c;
if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange)
return;
f = arg->f < 1.0f ? arg->f + selmon->mfact : arg->f - 1.0f;
- if (f < 0.1 || f > 0.9)
- return;
- selmon->mfact = f;
+ if (arg->f == 0) {
+ for (r = monrules; r < END(monrules); r++)
+ if (!r->name || strstr(selmon->name, r->name))
+ selmon->mfact = r->mfact;
+ } else if (f > 0.1 || f < 0.9)
+ selmon->mfact = f;
+ else
+ return;
arrange(selmon);
+
+ if (warpcursor) {
+ c = focustop(selmon);
+ if (c) {
+ wlr_cursor_warp_closest(cursor, NULL, c->geom.x + c->geom.width / 2.0,
+ c->geom.y + c->geom.height / 2.0);
+ motionnotify(0, NULL, 0, 0, 0, 0);
+ }
+ }
}
void
@@ -2627,6 +3387,16 @@ setup(void)
wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer,
&new_virtual_pointer);
+ pointer_gestures = wlr_pointer_gestures_v1_create(dpy);
+ LISTEN_STATIC(&cursor->events.swipe_begin, swipe_begin);
+ LISTEN_STATIC(&cursor->events.swipe_update, swipe_update);
+ LISTEN_STATIC(&cursor->events.swipe_end, swipe_end);
+ LISTEN_STATIC(&cursor->events.pinch_begin, pinch_begin);
+ LISTEN_STATIC(&cursor->events.pinch_update, pinch_update);
+ LISTEN_STATIC(&cursor->events.pinch_end, pinch_end);
+ LISTEN_STATIC(&cursor->events.hold_begin, hold_begin);
+ LISTEN_STATIC(&cursor->events.hold_end, hold_end);
+
seat = wlr_seat_create(dpy, "seat0");
wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
wl_signal_add(&seat->events.request_set_selection, &request_set_sel);
@@ -2641,6 +3411,13 @@ setup(void)
wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
wl_signal_add(&output_mgr->events.test, &output_mgr_test);
+ wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind);
+
+ tearing_control_v1 = wlr_tearing_control_manager_v1_create(dpy, 1);
+ tearing_control_new_object.notify = handlenewtearinghint;
+ wl_signal_add(&tearing_control_v1->events.new_object, &tearing_control_new_object);
+ wl_list_init(&tearing_controllers);
+
/* Make sure XWayland clients don't connect to the parent X server,
* e.g when running in the x11 backend or the wayland backend and the
* compositor has Xwayland support */
@@ -2707,8 +3484,8 @@ tagmon(const Arg *arg)
void
tile(Monitor *m)
{
- unsigned int mw, my, ty;
- int i, n = 0;
+ unsigned int h, r, e = enablegaps, mw, my, ty;
+ int i, n = 0;
Client *c;
wl_list_for_each(c, &clients, link)
@@ -2717,28 +3494,44 @@ tile(Monitor *m)
if (n == 0)
return;
+ if (smartgaps == n) {
+ e = 0; // outer gaps disabled
+ }
+
if (n > m->nmaster)
- mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;
+ mw = m->nmaster ? (int)roundf((m->w.width + m->gappx*e) * m->mfact) : 0;
else
- mw = m->w.width;
- i = my = ty = 0;
+ mw = m->w.width - 2*m->gappx*e + m->gappx*e;
+ i = 0;
+ my = ty = m->gappx*e;
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
continue;
if (i < m->nmaster) {
- resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
- .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
- my += c->geom.height;
+ r = MIN(n, m->nmaster) - i;
+ h = (m->w.height - my - m->gappx*e - m->gappx*e * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + m->gappx*e, .y = m->w.y + my,
+ .width = mw - m->gappx*e, .height = h}, 0);
+ my += c->geom.height + m->gappx*e;
} else {
- resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
- .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
- ty += c->geom.height;
+ r = n - i;
+ h = (m->w.height - ty - m->gappx*e - m->gappx*e * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappx*e, .y = m->w.y + ty,
+ .width = m->w.width - mw - 2*m->gappx*e, .height = h}, 0);
+ ty += c->geom.height + m->gappx*e;
}
i++;
}
}
void
+togglebar(const Arg *arg) {
+ DwlIpcOutput *ipc_output;
+ wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link)
+ zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource);
+}
+
+void
togglefloating(const Arg *arg)
{
Client *sel = focustop(selmon);
@@ -2756,6 +3549,25 @@ togglefullscreen(const Arg *arg)
}
void
+togglegaps(const Arg *arg)
+{
+ enablegaps = !enablegaps;
+ arrange(selmon);
+}
+
+void
+togglepointerconstraints(const Arg *arg)
+{
+ enable_constraints = !enable_constraints;
+}
+
+void
+togglepassthrough(const Arg *arg)
+{
+ passthrough = !passthrough;
+}
+
+void
toggletag(const Arg *arg)
{
uint32_t newtags;
@@ -2815,15 +3627,32 @@ unmapnotify(struct wl_listener *listener, void *data)
grabc = NULL;
}
+ if (c->swallowedby)
+ swallow(c->swallowedby, c);
+
if (client_is_unmanaged(c)) {
if (c == exclusive_focus) {
exclusive_focus = NULL;
focusclient(focustop(selmon), 1);
}
} else {
- wl_list_remove(&c->link);
+ if (!c->swallowing)
+ wl_list_remove(&c->link);
setmon(c, NULL, 0);
- wl_list_remove(&c->flink);
+ if (!c->swallowing)
+ wl_list_remove(&c->flink);
+ }
+
+ if (c->swallowedby) {
+ c->swallowedby->prev = c->geom;
+ setfullscreen(c->swallowedby, c->isfullscreen);
+ c->swallowedby->swallowing = NULL;
+ c->swallowedby = NULL;
+ }
+
+ if (c->swallowing) {
+ c->swallowing->swallowedby = NULL;
+ c->swallowing = NULL;
}
wlr_scene_node_destroy(&c->scene->node);
@@ -2958,7 +3787,7 @@ urgent(struct wl_listener *listener, void *data)
printstatus();
if (client_surface(c)->mapped)
- client_set_border_color(c, urgentcolor);
+ client_set_border_color(c, urgentcolor);
}
void
@@ -3070,6 +3899,19 @@ zoom(const Arg *arg)
arrange(selmon);
}
+int
+regex_match(const char *pattern, const char *str) {
+ regex_t regex;
+ int reti;
+ if (regcomp(&regex, pattern, REG_EXTENDED) != 0)
+ return 0;
+ reti = regexec(&regex, str, (size_t)0, NULL, 0);
+ regfree(&regex);
+ if (reti == 0)
+ return 1;
+ return 0;
+}
+
#ifdef XWAYLAND
void
activatex11(struct wl_listener *listener, void *data)
@@ -3132,6 +3974,7 @@ createnotifyx11(struct wl_listener *listener, void *data)
LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify);
LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11);
LISTEN(&xsurface->events.request_activate, &c->activate, activatex11);
+ LISTEN(&xsurface->events.request_minimize, &c->minimize, minimizenotify);
LISTEN(&xsurface->events.request_configure, &c->configure, configurex11);
LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify);
LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
@@ -3147,6 +3990,21 @@ dissociatex11(struct wl_listener *listener, void *data)
}
void
+minimizenotify(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, minimize);
+ struct wlr_xwayland_surface *xsurface = c->surface.xwayland;
+ struct wlr_xwayland_minimize_event *e = data;
+ int focused;
+
+ if (xsurface->surface == NULL || !xsurface->surface->mapped)
+ return;
+
+ focused = seat->keyboard_state.focused_surface == xsurface->surface;
+ wlr_xwayland_surface_set_minimized(xsurface, !focused && e->minimize);
+}
+
+void
sethints(struct wl_listener *listener, void *data)
{
Client *c = wl_container_of(listener, c, set_hints);
@@ -3158,7 +4016,7 @@ sethints(struct wl_listener *listener, void *data)
printstatus();
if (c->isurgent && surface && surface->mapped)
- client_set_border_color(c, urgentcolor);
+ client_set_border_color(c, urgentcolor);
}
void