swim.git
act.c
/* swim - window manager
* Copyright (C) 2022-2023 ArcNyxx
* see LICENCE file for licensing information */
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include "bar.h"
#include "conv.h"
#include "func.h"
#include "swim.h"
#include "tile.h"
#include "util.h"
#define MASK ButtonPressMask | ButtonReleaseMask | PointerMotionMask
#define MODKEY Mod4Mask
#define TAGKEY(keysym, shift) \
{ view, 1 << shift, MODKEY, keysym }, \
{ toggleview, 1 << shift, MODKEY | ControlMask, keysym }, \
{ tag, 1 << shift, MODKEY | ShiftMask, keysym }, \
{ toggletag, 1 << shift, MODKEY | ControlMask | ShiftMask, keysym }
static mon_t *dirtomon(int dir);
static void focusmon (long arg);
static void focusstack (long arg);
static void incnmaster (long arg);
static void killclient (long arg);
static void mouseevent (long arg);
static void quit (long arg);
static void setmfact (long arg);
static void startexec (long arg);
static void startnirv (long arg);
static void tag (long arg);
static void tagmon (long arg);
static void togglebar (long arg);
static void togglefloating (long arg);
static void togglegaps (long arg);
static void toggletag (long arg);
static void toggletop (long arg);
static void toggleview (long arg);
static void view (long arg);
static void zoom (long arg);
extern mon_t *sel;
const keyp_t keys[63] = {
{ spawn, (long)(const void *[]){ "amixer", "set", "Master",
"3%+", NULL }, MODKEY, XK_a },
{ spawn, (long)(const void *[]){ "amixer", "set", "Master",
"3%-", NULL }, MODKEY, XK_z },
{ spawn, (long)(const void *[]){ "amixer", "set", "Master",
"toggle", NULL }, MODKEY, XK_m },
{ spawn, (long)(const void *[]){ "firefox", NULL }, MODKEY, XK_w },
{ spawn, (long)(const void *[]){ "st", NULL }, MODKEY, XK_Return },
{ spawn, (long)(const void *[]){ "bscr", NULL }, MODKEY, XK_s },
{ startexec, 0, MODKEY, XK_e },
{ startnirv, 0, MODKEY, XK_n },
{ killclient, 0, MODKEY | ShiftMask, XK_q },
{ zoom, 0, MODKEY, XK_semicolon },
{ focusstack, 1, MODKEY, XK_j },
{ focusstack, -1, MODKEY, XK_k },
{ setmfact, 5, MODKEY, XK_l },
{ setmfact, -5, MODKEY, XK_h },
{ incnmaster, 1, MODKEY, XK_i },
{ incnmaster, -1, MODKEY, XK_o },
{ focusmon, -1, MODKEY, XK_comma },
{ focusmon, 1, MODKEY, XK_period },
{ tagmon, -1, MODKEY | ShiftMask, XK_comma },
{ tagmon, 1, MODKEY | ShiftMask, XK_period },
{ togglebar, 0, MODKEY, XK_b },
{ togglegaps, 0, MODKEY, XK_g },
{ toggletop, 0, MODKEY, XK_t },
{ togglefloating, 0, MODKEY, XK_f },
{ view, ~0, MODKEY, XK_0 },
{ tag, ~0, MODKEY | ShiftMask, XK_0 },
{ quit, 0, MODKEY | ShiftMask, XK_e },
TAGKEY(XK_1, 0),
TAGKEY(XK_2, 1),
TAGKEY(XK_3, 2),
TAGKEY(XK_4, 3),
TAGKEY(XK_5, 4),
TAGKEY(XK_6, 5),
TAGKEY(XK_7, 6),
TAGKEY(XK_8, 7),
TAGKEY(XK_9, 8)
};
const but_t buts[6] = {
{ view, 0, CLK_TAG, 0, Button1 },
{ toggleview, 0, CLK_TAG, 0, Button3 },
{ tag, 0, CLK_TAG, MODKEY, Button1 },
{ toggletag, 0, CLK_TAG, MODKEY, Button3 },
{ mouseevent, true, CLK_CLI, MODKEY, Button1 },
{ mouseevent, false, CLK_CLI, MODKEY, Button3 }
};
const char *tags[9] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static mon_t *
dirtomon(int dir)
{
extern mon_t *mons;
if (mons->next == NULL)
return NULL;
mon_t *mon = NULL;
if (dir > 0) /* just take next monitor or wrap to start */
return sel->next != NULL ? sel->next : mons;
else if (sel != mons) /* take mon before sel */
for (mon = mons; mon->next != sel; mon = mon->next);
else /* no monitor before sel, take last monitor to wrap around */
for (mon = mons; mon->next != NULL; mon = mon->next);
return mon;
}
static void
focusmon(long arg)
{
mon_t *mon; /* more than one mon */
if ((mon = dirtomon(arg)) != NULL)
unfocus(sel->sel, false), sel = mon, focus(NULL);
}
static void
focusstack(long arg)
{
if (sel->sel == NULL || sel->sel->fsc)
return;
cli_t *cli, *cnext;
if (arg > 0) {
for (cli = sel->sel->next; cli != NULL && !VIS(cli);
cli = cli->next);
if (cli == NULL)
for (cli = sel->cli; cli != NULL && !VIS(cli);
cli = cli->next);
} else {
for (cli = NULL, cnext = sel->cli; cnext != sel->sel;
cnext = cnext->next)
if (VIS(cnext))
cli = cnext; /* first before client */
if (cli == NULL)
for (; cnext != NULL; cnext = cnext->next)
if (VIS(cnext))
cli = cnext; /* last client */
}
if (cli != NULL)
focus(cli);
}
static void
incnmaster(long arg)
{
sel->nmaster = MAX(sel->nmaster + arg, 1);
tile(sel);
}
static void
killclient(long arg)
{
if (sel->sel != NULL && !sendevent(sel->sel, atom[CC_DELETE])) {
XGrabServer(dpy);
XKillClient(dpy, sel->sel->win);
XSync(dpy, false);
XUngrabServer(dpy);
}
}
static void
mouseevent(long arg)
{
extern void (*evts[LASTEvent])(XEvent *);
cli_t *cli;
if ((cli = sel->sel) == NULL || !cli->flt || cli->fsc ||
XGrabPointer(dpy, ROOT, false, MASK, GrabModeAsync,
GrabModeAsync, 0, 0, CurrentTime) != GrabSuccess)
return;
RAISE(cli);
int x, y;
XQueryPointer(dpy, ROOT, &(Window){ 0 }, &(Window){ 0 }, &x, &y,
&(int){ 0 }, &(int){ 0 }, &(unsigned int){ 0 });
int oldx = arg ? cli->x : cli->w;
int oldy = arg ? cli->y : cli->h;
XEvent evt;
do {
XMaskEvent(dpy, MASK | ExposureMask |
SubstructureRedirectMask, &evt);
switch (evt.type) {
default: break;
case ConfigureRequest:
case Expose:
case MapRequest:
case PropertyNotify: ; /* ensure bar runs smoothly */
evts[evt.type](&evt);
break;
case MotionNotify:
if (arg) {
cli->x = oldx + evt.xmotion.x - x;
cli->y = oldy + evt.xmotion.y - y;
} else {
cli->w = MAX(oldx + evt.xmotion.x - x, H);
cli->h = MAX(oldy + evt.xmotion.y - y, H);
}
CSIZE(cli);
}
} while (evt.type != ButtonRelease);
XUngrabPointer(dpy, CurrentTime);
mon_t *mon;
if ((mon = rectomon(cli->x, cli->y, cli->w, cli->h)) != sel)
unfocus(cli, true), detach(cli), cli->mon = mon, cli->tags =
mon->tags, attach(cli), focus(NULL);
}
static void
quit(long arg)
{
XCloseDisplay(dpy);
exit(0);
}
static void
setmfact(long arg)
{
sel->mfact = MIN(MAX(arg, 5), 95);
tile(sel);
}
static void
startexec(long arg)
{
extern int exec;
exec = 0;
XGrabKey(dpy, AnyKey, AnyModifier, ROOT, true,
GrabModeAsync, GrabModeAsync);
drawbars();
}
static void
startnirv(long arg)
{
extern int exec;
extern char etext[256];
exec = 6;
memcpy(etext, "nctl -", 6);
XGrabKey(dpy, AnyKey, AnyModifier, ROOT, true,
GrabModeAsync, GrabModeAsync);
drawbars();
}
static void
tag(long arg)
{
if (sel->sel != NULL && arg != 0)
sel->sel->tags = arg, tile(sel), focus(NULL);
}
static void
tagmon(long arg)
{
mon_t *mon, *old; /* has sel window and more than one mon */
if (sel->sel != NULL && !sel->sel->flt && !sel->sel->fsc &&
(mon = dirtomon(arg)) != NULL)
unfocus(sel->sel, true), detach(sel->sel),
old = sel->sel->mon, sel->sel->mon = mon,
sel->sel->tags = mon->tags, attach(sel->sel),
tile(old), tile(mon), focus(NULL), drawbars();
}
static void
togglebar(long arg)
{
sel->show = !sel->show;
MOVE(sel->win, sel->x,
sel->show ? sel->y + !sel->top * (sel->h - H) : -H);
tile(sel);
}
static void
togglefloating(long arg)
{
if (sel->sel != NULL && !sel->sel->fsc)
(sel->sel->flt = !sel->sel->flt) ? RAISE(sel->sel) :
LOWER(sel->sel), tile(sel);
}
static void
togglegaps(long arg)
{
sel->gap = !sel->gap; tile(sel);
}
static void
toggletag(long arg)
{
if (sel->sel != NULL && (sel->sel->tags ^ arg) != 0)
sel->sel->tags ^= arg, tile(sel), focus(NULL);
}
static void
toggletop(long arg)
{
sel->top = !sel->top;
MOVE(sel->win, sel->x,
sel->show ? sel->y + !sel->top * (sel->h - H) : -H);
tile(sel);
}
static void
toggleview(long arg)
{
if ((sel->tags ^ arg) != 0)
sel->tags ^= arg, tile(sel), focus(NULL);
}
static void
view(long arg)
{
if (arg != 0)
sel->tags = arg, tile(sel), focus(NULL);
}
static void
zoom(long arg)
{
cli_t *cli = sel->sel;
if (cli == NULL || cli->flt || cli->fsc)
return;
if (cli != next(sel->cli) || ((cli = next(cli->next)) != NULL))
detach(cli), attach(cli), tile(cli->mon), focus(cli);
}