594 lines
12 KiB
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,
|
|
cursor[CurMove]->cursor,
|
|
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,
|
|
cursor[CurResize]->cursor,
|
|
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);
|
|
}
|