diff options
Diffstat (limited to 'dwl.c')
-rw-r--r-- | dwl.c | 1044 |
1 files changed, 951 insertions, 93 deletions
@@ -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(®ex, pattern, REG_EXTENDED) != 0) + return 0; + reti = regexec(®ex, str, (size_t)0, NULL, 0); + regfree(®ex); + 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 |