#include "client.h" void applybounds(Client *c, struct wlr_box *bbox) { /* set minimum possible */ c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); if (c->geom.x >= bbox->x + bbox->width) c->geom.x = bbox->x + bbox->width - c->geom.width; if (c->geom.y >= bbox->y + bbox->height) c->geom.y = bbox->y + bbox->height - c->geom.height; if (c->geom.x + c->geom.width <= bbox->x) c->geom.x = bbox->x; if (c->geom.y + c->geom.height <= bbox->y) c->geom.y = bbox->y; } void focusclient(Client *c, int lift) { struct wlr_surface *old = seat->keyboard_state.focused_surface; int unused_lx, unused_ly, old_client_type; Client *old_c = NULL; LayerSurface *old_l = NULL; if (locked) return; /* Raise client in stacking order if requested */ if (c && lift) wlr_scene_node_raise_to_top(&c->scene->node); if (c && client_surface(c) == old) return; if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) { struct wlr_xdg_popup *popup, *tmp; wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link) wlr_xdg_popup_destroy(popup); } /* Put the new client atop the focus stack and select its monitor */ if (c && !client_is_unmanaged(c)) { wl_list_remove(&c->flink); wl_list_insert(&fstack, &c->flink); 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); } /* Deactivate old client if focus is changing */ if (old && (!c || client_surface(c) != old)) { /* If an overlay is focused, don't focus or activate the client, * but only update its position in fstack to render its border with * focuscolor and focus it after the overlay is closed. */ if (old_client_type == LayerShell && wlr_scene_node_coords(&old_l->scene->node, &unused_lx, &unused_ly) && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { return; } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) { return; /* 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_activate_surface(old, 0); } } printstatus(); if (!c) { /* With no client, all we have left is to clear focus */ wlr_seat_keyboard_notify_clear_focus(seat); return; } /* Change cursor surface */ motionnotify(0, NULL, 0, 0, 0, 0); /* Have a client, so focus its top-level wlr_surface */ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); /* Activate the new client */ client_activate_surface(client_surface(c), 1); } void killclient(const Arg *arg) { Client *sel = focustop(selmon); if (sel) client_send_close(sel); } void resize(Client *c, struct wlr_box geo, int interact) { struct wlr_box *bbox; struct wlr_box clip; if (!c->mon || !client_surface(c)->mapped) return; bbox = interact ? &sgeom : &c->mon->w; client_set_bounds(c, geo.width, geo.height); c->geom = geo; applybounds(c, bbox); /* Update scene-graph, including borders */ wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); 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); /* 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); client_get_clip(c, &clip); wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); } void setfloating(Client *c, int floating) { Client *p = client_get_parent(c); c->isfloating = 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; wlr_scene_node_reparent( &c->scene->node, layers[c->isfullscreen || (p && p->isfullscreen) ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); arrange(c->mon); printstatus(); } void setfullscreen(Client *c, int fullscreen) { c->isfullscreen = fullscreen; if (!c->mon || !client_surface(c)->mapped) return; c->bw = fullscreen ? 0 : borderpx; client_set_fullscreen(c, fullscreen); wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); if (fullscreen) { c->prev = c->geom; resize(c, c->mon->m, 0); } else { /* restore previous size instead of arrange for floating windows since * client positions are set by the user and cannot be recalculated */ resize(c, c->prev, 0); } arrange(c->mon); printstatus(); } void setpsel(struct wl_listener *listener, void *data) { /* This event is raised by the seat when a client wants to set the selection, * usually when the user copies something. wlroots allows compositors to * ignore such requests if they so choose, but in dwl we always honor them */ struct wlr_seat_request_set_primary_selection_event *event = data; wlr_seat_set_primary_selection(seat, event->source, event->serial); } void setsel(struct wl_listener *listener, void *data) { /* This event is raised by the seat when a client wants to set the selection, * usually when the user copies something. wlroots allows compositors to * ignore such requests if they so choose, but in dwl we always honor them */ struct wlr_seat_request_set_selection_event *event = data; wlr_seat_set_selection(seat, event->source, event->serial); } void togglefloating(const Arg *arg) { Client *sel = focustop(selmon); /* return if fullscreen */ if (sel && !sel->isfullscreen) setfloating(sel, !sel->isfloating); } void togglefullscreen(const Arg *arg) { Client *sel = focustop(selmon); if (sel) setfullscreen(sel, !sel->isfullscreen); } void toggletag(const Arg *arg) { uint32_t newtags; Client *sel = focustop(selmon); if (!sel || !(newtags = sel->tags ^ (arg->ui & TAGMASK))) return; sel->tags = newtags; focusclient(focustop(selmon), 1); arrange(selmon); printstatus(); } void toggleview(const Arg *arg) { uint32_t newtagset; if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0)) return; selmon->tagset[selmon->seltags] = newtagset; focusclient(focustop(selmon), 1); arrange(selmon); printstatus(); } void updatetitle(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_title); if (c == focustop(c->mon)) printstatus(); } void urgent(struct wl_listener *listener, void *data) { struct wlr_xdg_activation_v1_request_activate_event *event = data; Client *c = NULL; toplevel_from_wlr_surface(event->surface, &c, NULL); if (!c || c == focustop(selmon)) return; c->isurgent = 1; printstatus(); if (client_surface(c)->mapped) client_set_border_color(c, urgentcolor); } void view(const Arg *arg) { if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; focusclient(focustop(selmon), 1); arrange(selmon); printstatus(); } void zoom(const Arg *arg) { Client *c, *sel = focustop(selmon); if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) return; /* Search for the first tiled window that is not sel, marking sel as * NULL if we pass it along the way */ wl_list_for_each(c, &clients, link) { if (VISIBLEON(c, selmon) && !c->isfloating) { if (c != sel) break; sel = NULL; } } /* Return if no other tiled window was found */ if (&c->link == &clients) return; /* If we passed sel, move c to the front; otherwise, move sel to the * front */ if (!sel) sel = c; wl_list_remove(&sel->link); wl_list_insert(&clients, &sel->link); focusclient(sel, 1); arrange(selmon); }