polytreewm/src/dwm/spaghetti/interaction.c

594 lines
12 KiB
C

void configborder(const Arg *const arg)
{
if (arg == NULL) return;
const int old_border_width = settings_get_border_width();
const int new_border_width = old_border_width + (arg->i >= 0 ? +1 : -1);
settings_set_border_width(new_border_width);
arrange(selmon);
}
void configgap(const Arg *const arg)
{
if (arg == NULL) return;
const int old_gap_size = settings_get_gap_size();
const int new_gap_size = old_gap_size + (arg->i >= 0 ? +2 : -2);
settings_set_gap_size(new_gap_size);
arrange(selmon);
}
void dorestart(__attribute__((unused)) const Arg *const arg)
{
restart();
}
void focusmon(const Arg *arg)
{
if (!mons->next) return;
Monitor *m;
if ((m = dirtomon(arg->i)) == selmon) return;
unfocus(selmon->sel, 0);
selmon = m;
focus(NULL);
}
void focusstack(const Arg *arg)
{
if (!selmon->sel) return;
Client *c = NULL;
if (arg->i > 0) {
for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
if (!c) {
for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
}
} else {
Client *i = selmon->clients;
for (; i != selmon->sel; i = i->next) {
if (ISVISIBLE(i)) {
c = i;
}
}
if (!c) {
for (; i; i = i->next) {
if (ISVISIBLE(i)) {
c = i;
}
}
}
}
if (c) {
focus(c);
restack(selmon);
{
unsigned int n = 0;
for (Client *cc = nexttiled(selmon->clients); cc; cc = nexttiled(cc->next), ++n);
// TODO: Maybe it's an unnecessary optimization
// and we don't need the condition.
if (n > 1) {
// We have to rearrange because borders and gaps may have
// changed in monocle layout.
arrange(selmon);
}
}
}
}
void incnmaster(const Arg *arg)
{
const int max_clients_in_master = settings_get_max_clients_in_master();
const int new_clients_in_master = MAX(0, selmon->nmaster + arg->i);
selmon->nmaster =
max_clients_in_master == 0
? new_clients_in_master
: MIN(new_clients_in_master, max_clients_in_master);
arrange(selmon);
}
void killclient(__attribute__((unused)) const Arg *arg)
{
if (!selmon->sel)
return;
if (!sendevent(selmon->sel, xbase->atoms->wmatom[WMDelete])) {
XGrabServer(xbase->x_display);
XSetErrorHandler(xerrordummy);
XSetCloseDownMode(xbase->x_display, DestroyAll);
XKillClient(xbase->x_display, selmon->sel->x_window);
XSync(xbase->x_display, False);
XSetErrorHandler(xerror);
XUngrabServer(xbase->x_display);
}
}
void movemouse(__attribute__((unused)) const Arg *arg)
{
Client *const c = selmon->sel;
if (c == NULL) return;
restack(selmon);
if (
XGrabPointer(
xbase->x_display,
xbase->x_root,
False,
MOUSEMASK,
GrabModeAsync,
GrabModeAsync,
None,
cursors[CURSOR_MOVE],
CurrentTime
) != GrabSuccess
) {
return;
}
int x, y;
if (!getrootptr(&x, &y)) return;
const unsigned int snap_distance = settings_get_snap_distance();
const int ocx = c->state.geom.basic.position.x;
const int ocy = c->state.geom.basic.position.y;
Time lasttime = 0;
XEvent ev;
do {
XMaskEvent(
xbase->x_display,
MOUSEMASK | ExposureMask | SubstructureRedirectMask,
&ev
);
switch (ev.type) {
case ConfigureRequest:
case Expose:
case MapRequest:
handler[ev.type](&ev);
break;
case MotionNotify:
if ((ev.xmotion.time - lasttime) <= (1000 / 60)) continue;
lasttime = ev.xmotion.time;
int nx = ocx + (ev.xmotion.x - x);
int ny = ocy + (ev.xmotion.y - y);
if (
abs(selmon->window_area_geom.position.x - nx)
<
snap_distance
) {
nx = selmon->window_area_geom.position.x;
} else if (
abs(
(
selmon->window_area_geom.position.x
+
selmon->window_area_geom.sizes.w
)
-
(nx + win_geom_total_width(&c->state.geom))
)
<
snap_distance
) {
nx =
selmon->window_area_geom.position.x
+
selmon->window_area_geom.sizes.w
-
win_geom_total_width(&c->state.geom);
}
if (
abs(selmon->window_area_geom.position.y - ny)
<
snap_distance
) {
ny = selmon->window_area_geom.position.y;
} else if (
abs(
(
selmon->window_area_geom.position.y
+
selmon->window_area_geom.sizes.h
) - (ny + win_geom_total_height(&c->state.geom))
)
<
snap_distance
) {
ny =
selmon->window_area_geom.position.y
+
selmon->window_area_geom.sizes.h
-
win_geom_total_height(&c->state.geom);
}
if (
!c->state.is_floating
&&
(
abs(nx - c->state.geom.basic.position.x) > snap_distance
||
abs(ny - c->state.geom.basic.position.y) > snap_distance)
) {
togglefloating(NULL);
}
if (!selmon->lt[selmon->sellt]->arrange || c->state.is_floating) {
struct WinGeom win_geom = c->state.geom;
position_init_from_args(&win_geom.basic.position, nx, ny);
resize(c, win_geom, 1);
}
break;
}
} while (ev.type != ButtonRelease);
XUngrabPointer(xbase->x_display, CurrentTime);
Monitor *const m = recttomon(
c->state.geom.basic.position.x,
c->state.geom.basic.position.y,
c->state.geom.basic.sizes.w,
c->state.geom.basic.sizes.h
);
if (m != selmon) {
sendmon(c, m);
selmon = m;
focus(NULL);
}
}
void movestack(const Arg *arg)
{
Client *c = NULL, *p = NULL, *pc = NULL, *i = NULL;
if (arg->i > 0) {
/* find the client after selmon->sel */
for (
c = selmon->sel->next;
c && (!ISVISIBLE(c) || c->state.is_floating);
c = c->next
);
if(!c) {
for(
c = selmon->clients;
c && (!ISVISIBLE(c) || c->state.is_floating);
c = c->next
);
}
} else {
/* find the client before selmon->sel */
for (i = selmon->clients; i != selmon->sel; i = i->next) {
if (ISVISIBLE(i) && !i->state.is_floating) {
c = i;
}
}
if (!c) {
for (; i; i = i->next) {
if (ISVISIBLE(i) && !i->state.is_floating) {
c = i;
}
}
}
}
/* find the client before selmon->sel and c */
for (i = selmon->clients; i && (!p || !pc); i = i->next) {
if (i->next == selmon->sel) {
p = i;
}
if (i->next == c) {
pc = i;
}
}
/* swap c and selmon->sel selmon->clients in the selmon->clients list */
if (c && c != selmon->sel) {
Client *temp = selmon->sel->next == c ? selmon->sel : selmon->sel->next;
selmon->sel->next = c->next==selmon->sel?c:c->next;
c->next = temp;
if (p && p != c) {
p->next = c;
}
if (pc && pc != selmon->sel) {
pc->next = selmon->sel;
}
if (selmon->sel == selmon->clients) {
selmon->clients = c;
} else if (c == selmon->clients) {
selmon->clients = selmon->sel;
}
arrange(selmon);
}
}
void quit(__attribute__((unused)) const Arg *arg)
{
running = 0;
}
void resetnmaster(const Arg *arg)
{
const int max_clients_in_master = settings_get_max_clients_in_master();
const int new_clients_in_master = arg->i == 0 ? 0 : settings_get_default_clients_in_master();
selmon->nmaster =
max_clients_in_master == 0
? new_clients_in_master
: MIN(new_clients_in_master, max_clients_in_master);
arrange(selmon);
}
void resizemouse(__attribute__((unused)) const Arg *arg)
{
Client *const c = selmon->sel;
if (c == NULL) return;
restack(selmon);
if (
XGrabPointer(
xbase->x_display,
xbase->x_root,
False,
MOUSEMASK,
GrabModeAsync,
GrabModeAsync,
None,
cursors[CURSOR_RESIZE],
CurrentTime
) != GrabSuccess
) {
return;
}
XWarpPointer(
xbase->x_display,
None,
c->x_window,
0,
0,
0,
0,
c->state.geom.basic.sizes.w + c->state.geom.border_width - 1,
c->state.geom.basic.sizes.h + c->state.geom.border_width - 1
);
const unsigned int snap_distance = settings_get_snap_distance();
const int ocx = c->state.geom.basic.position.x;
const int ocy = c->state.geom.basic.position.y;
Time lasttime = 0;
XEvent ev;
do {
XMaskEvent(
xbase->x_display,
MOUSEMASK | ExposureMask | SubstructureRedirectMask,
&ev
);
switch (ev.type) {
case ConfigureRequest:
case Expose:
case MapRequest:
handler[ev.type](&ev);
break;
case MotionNotify:
if ((ev.xmotion.time - lasttime) <= (1000 / 60)) continue;
lasttime = ev.xmotion.time;
const int nw = MAX(
ev.xmotion.x - ocx - 2 * c->state.geom.border_width + 1,
1
);
const int nh = MAX(
ev.xmotion.y - ocy - 2 * c->state.geom.border_width + 1,
1
);
if (
(
c->mon->window_area_geom.position.x + nw
>=
selmon->window_area_geom.position.x
)
&&
(
c->mon->window_area_geom.position.x + nw
<=
(
selmon->window_area_geom.position.x
+
selmon->window_area_geom.sizes.w
)
)
&&
(
c->mon->window_area_geom.position.y + nh
>=
selmon->window_area_geom.position.y
)
&&
(
c->mon->window_area_geom.position.y + nh
<=
(
selmon->window_area_geom.position.y
+
selmon->window_area_geom.sizes.h
)
)
) {
if (
!c->state.is_floating
&&
(
selmon->lt[selmon->sellt]->arrange == NULL
||
abs(nw - c->state.geom.basic.sizes.w) > snap_distance
||
abs(nh - c->state.geom.basic.sizes.h) > snap_distance
)
) {
togglefloating(NULL);
}
}
if (!selmon->lt[selmon->sellt]->arrange || c->state.is_floating) {
struct WinGeom win_geom = c->state.geom;
sizes_init_from_args(&win_geom.basic.sizes, nw, nh);
resize(c, win_geom, 1);
}
break;
}
} while (ev.type != ButtonRelease);
XWarpPointer(
xbase->x_display,
None,
c->x_window,
0,
0,
0,
0,
c->state.geom.basic.sizes.w + c->state.geom.border_width - 1,
c->state.geom.basic.sizes.h + c->state.geom.border_width - 1
);
XUngrabPointer(xbase->x_display, CurrentTime);
while (XCheckMaskEvent(xbase->x_display, EnterWindowMask, &ev));
Monitor *const m = recttomon(
c->state.geom.basic.position.x,
c->state.geom.basic.position.y,
c->state.geom.basic.sizes.w,
c->state.geom.basic.sizes.h
);
if (m != selmon) {
sendmon(c, m);
selmon = m;
focus(NULL);
}
}
void setlayout(const Arg *arg)
{
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
selmon->sellt ^= 1;
}
if (arg && arg->v) {
const Layout *const new_layout = arg->v;
selmon->lt[selmon->sellt] = new_layout;
}
unsigned int visible_clients = 0;
for (const Client *client = selmon->clients; client; client = client->next) {
if (ISVISIBLE(client)) ++visible_clients;
}
if (selmon->sel) {
arrange(selmon);
}
}
void setmfact(const Arg *arg)
{
if (!arg) return;
unit_inc_master_area_factor(selmon->unit, arg->f);
for (Monitor *m = mons; m; m = m->next) {
arrange(m);
}
}
void spawn(const Arg *arg)
{
const char *const command_name = arg->v;
spawn_command(command_name, spawn_callback, selmon->num);
}
void spawn_callback()
{
if (xbase->x_display) {
close(ConnectionNumber(xbase->x_display));
}
}
void tagmon(const Arg *arg)
{
if (!selmon->sel || !mons->next)
return;
sendmon(selmon->sel, dirtomon(arg->i));
}
void togglefloating(__attribute__((unused)) const Arg *arg)
{
if (!selmon->sel) return;
selmon->sel->state.is_floating =
!selmon->sel->state.is_floating || selmon->sel->state.is_fixed;
const int border_width = settings_get_border_width();
if (selmon->sel->state.is_floating) {
struct WinGeom win_geom = selmon->sel->state.geom;
win_geom.basic.sizes = sizes_create_from_args(
selmon->sel->state.geom.basic.sizes.w -
2 * (border_width - selmon->sel->state.geom.border_width),
selmon->sel->state.geom.basic.sizes.h -
2 * (border_width - selmon->sel->state.geom.border_width)
);
win_geom.border_width = border_width;
resize(selmon->sel, win_geom, 0);
}
arrange(selmon);
}
void zoom(__attribute__((unused)) const Arg *arg)
{
Client *c = selmon->sel;
if (!selmon->lt[selmon->sellt]->arrange
|| (selmon->sel && selmon->sel->state.is_floating))
return;
if (c == nexttiled(selmon->clients))
if (!c || !(c = nexttiled(c->next)))
return;
pop(c);
}