Files
peachwm/src/scratchpad.c
T
huntedbytheirs 4ac03d4059 feat(effects): implement window rounded corners via scenefx
- Parse effects.windows.rounded/rounding from Lua config
- Add CfgEffectsWindows and CfgEffects structs to Config
- Switch to fx_renderer_create for scenefx support
- Replace 5 wlr/types/wlr_scene.h includes with scenefx variant
- Add config_get_corner_radius() helper (light=6px, heavy=14px)
- Apply corner_radii_top/bottom/left/right to 4 border rects
- Apply wlr_scene_buffer_set_corner_radius to surface buffers
- Disable on fullscreen, restore on exit
- Re-apply on resize and config hot-reload
- Add -Wno-extra-semi for scenefx header compatibility
- All targets compile cleanly (make test passes)
2026-07-02 05:00:26 -04:00

226 lines
6.4 KiB
C

/*
* See LICENSE file for copyright and license details.
* PeachWM is a fork of swindle, which is also GPLv3 licensed.
*
* Scratchpad workspace — a hidden layer of clients that can be toggled
* as a group, with monocle-style cycling within the scratchpad.
*/
#include <wlr/types/wlr_output_layout.h>
#include <scenefx/types/wlr_scene.h>
#include <wlr/util/log.h>
#include "client.h"
#include "layout.h"
#include "monitor.h"
#include "scratchpad.h"
/* ------------------------------------------------------------------ */
/* Extern functions from peachwm.c / layout.c / ipc client */
/* ------------------------------------------------------------------ */
void arrange(Monitor *m);
void focusclient(Client *c, int lift);
Client *focustop(Monitor *m);
void printstatus(void);
void resize(Client *c, struct wlr_box geo, int interact);
void setfloating(Client *c, int floating);
void client_set_suspended(Client *c, int suspended);
void dwindle_remove_client(Client *c);
void master_remove_client(Client *c);
/* ------------------------------------------------------------------ */
/* scratchpad_arrange — centre & resize all scratchpad clients */
/* ------------------------------------------------------------------ */
void scratchpad_arrange(Monitor *m) {
Client *c;
int sw = m->w.width * 0.8;
int sh = m->w.height * 0.8;
int sx = m->w.x + (m->w.width - sw) / 2;
int sy = m->w.y + (m->w.height - sh) / 2;
wl_list_for_each(c, &clients, link) {
if (c->mon != m || !c->isscratchpad)
continue;
wlr_scene_node_reparent(&c->scene->node, layers[LyrScratch]);
resize(c, (struct wlr_box){sx, sy, sw, sh}, 0);
}
}
/* ------------------------------------------------------------------ */
/* togglescratchpad — show / hide the scratchpad workspace */
/* ------------------------------------------------------------------ */
void togglescratchpad(const Arg *arg) {
Monitor *m = selmon;
Client *c, *focus = nullptr;
if (!m)
return;
if (!m->scratchpad_visible) {
/* Save current focus before showing scratchpad */
m->scratchpad_prev_focus = focustop(m);
}
m->scratchpad_visible = !m->scratchpad_visible;
if (m->scratchpad_visible) {
/* Find the most recently focused scratchpad client */
scratchpad_arrange(m);
wl_list_for_each(c, &fstack, flink) {
if (c->mon == m && c->isscratchpad) {
focus = c;
break;
}
}
/* Show only that one (monocle-like) */
wl_list_for_each(c, &clients, link) {
if (c->mon == m && c->isscratchpad) {
wlr_scene_node_set_enabled(&c->scene->node, c == focus);
client_set_suspended(c, c != focus);
}
}
m->scratchpad_current = focus;
if (focus)
focusclient(focus, 1);
} else {
/* Hide all scratchpad clients */
wl_list_for_each(c, &clients, link) {
if (c->mon == m && c->isscratchpad) {
wlr_scene_node_set_enabled(&c->scene->node, 0);
client_set_suspended(c, 1);
}
}
m->scratchpad_current = nullptr;
/* Restore focus to previous client */
if (m->scratchpad_prev_focus && VISIBLEON(m->scratchpad_prev_focus, m))
focusclient(m->scratchpad_prev_focus, 1);
else
focusclient(focustop(m), 1);
}
printstatus();
}
/* ------------------------------------------------------------------ */
/* swapdirscratchpad — move client into / out of scratchpad */
/* ------------------------------------------------------------------ */
void swapdirscratchpad(const Arg *arg) {
Client *sel = focustop(selmon);
if (!sel || sel->isfullscreen || !selmon)
return;
if (sel->isscratchpad) {
/* Move FROM scratchpad to current workspace */
Client *next = nullptr;
sel->isscratchpad = 0;
sel->isfloating = 0;
sel->tags = selmon->tagset[selmon->seltags];
if (selmon->scratchpad_visible) {
/* Find another scratchpad client to show */
wl_list_for_each(next, &fstack, flink) {
if (next != sel && next->isscratchpad && next->mon == selmon) {
scratchpad_arrange(selmon);
break;
}
}
if (!next)
selmon->scratchpad_current = nullptr;
else
selmon->scratchpad_current = next;
} else {
selmon->scratchpad_current = nullptr;
}
/* setfloating reparents + arranges; let it manage visibility */
setfloating(sel, 0);
if (next)
focusclient(next, 1);
else
focusclient(focustop(selmon), 1);
} else {
/* Move TO scratchpad */
Client *old = selmon->scratchpad_current;
dwindle_remove_client(sel);
master_remove_client(sel);
sel->isscratchpad = 1;
sel->isfloating = 1;
setfloating(sel, 1);
if (selmon->scratchpad_visible) {
/* Hide previous scratchpad client if different */
if (old && old != sel) {
wlr_scene_node_set_enabled(&old->scene->node, 0);
client_set_suspended(old, 1);
}
scratchpad_arrange(selmon);
wlr_scene_node_set_enabled(&sel->scene->node, 1);
client_set_suspended(sel, 0);
selmon->scratchpad_current = sel;
focusclient(sel, 1);
} else {
wlr_scene_node_set_enabled(&sel->scene->node, 0);
client_set_suspended(sel, 1);
arrange(selmon);
focusclient(focustop(selmon), 1);
}
}
printstatus();
}
/* ------------------------------------------------------------------ */
/* scratchpad_focusdir_cycle — cycle focus among scratchpad clients */
/* Called from focusdir() when the focused client is a scratchpad and */
/* the scratchpad workspace is visible. */
/* ------------------------------------------------------------------ */
void scratchpad_focusdir_cycle(const Arg *arg) {
Client *sel = focustop(selmon);
Client *c, *first = nullptr, *next = nullptr, *prev = nullptr;
int found = 0;
wl_list_for_each(c, &clients, link) {
if (!c->isscratchpad || c->mon != selmon)
continue;
if (!first)
first = c;
if (found) {
next = c;
break;
}
if (c == sel) {
found = 1;
continue;
}
prev = c;
}
Client *target = nullptr;
if (arg->i == WLR_DIRECTION_DOWN || arg->i == WLR_DIRECTION_RIGHT) {
if (next)
target = next;
else if (first)
target = first;
} else {
if (prev)
target = prev;
else {
/* wrap to last */
wl_list_for_each(c, &clients, link)
if (c->isscratchpad && c->mon == selmon)
target = c;
}
}
if (target && target != sel) {
if (selmon->scratchpad_current)
wlr_scene_node_set_enabled(
&selmon->scratchpad_current->scene->node, 0);
wlr_scene_node_set_enabled(&target->scene->node, 1);
client_set_suspended(target, 0);
selmon->scratchpad_current = target;
focusclient(target, 1);
}
}