swm

sigma window manager
git clone git://wolog.xyz/swm
Log | Files | Refs | README | LICENSE

config.c (9520B)


      1 /*
      2  * Copyright (C) Raymond Cole <rc@wolog.xyz>
      3  * License: GPL-3.0-or-later
      4  *
      5  * Portions of the code are derived from suckless dwm.  See file
      6  * LICENSE-dwm for copyright and license details of those portions.
      7  */
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <unistd.h>
     12 #include <X11/keysym.h>
     13 #include <X11/Xlib.h>
     14 #include <X11/Xutil.h>
     15 
     16 #include "swm.h"
     17 
     18 #define ARGV(...) ((char **)(const char *[]){__VA_ARGS__, NULL})
     19 #define SPAWN(...) spawn(ARGV(__VA_ARGS__))
     20 #define CLEANMASK(X) ((X) & ~lockmasks[3] & (ShiftMask|ControlMask|MODMASK))
     21 
     22 static void gapmenu(void);
     23 static void lytmenu(void);
     24 
     25 const unsigned borderw = 3; /* border width of windows in pixels */
     26 const unsigned snap = 32;   /* snap distance in pixels */
     27 
     28 /* i = inner, o = outer, h = horizontal, v = vertical */
     29 const int gapih = 15;
     30 const int gapiv = 10;
     31 const int gapoh = 15;
     32 const int gapov = 20;
     33 
     34 static const char *fonts_[] = {
     35 	"monospace:size=11:antialias=true:autohint=true",
     36 	"Font Awesome 5 Free Solid:size=11:antialias=true:autohint=true",
     37 };
     38 const char **fonts = fonts_;
     39 const size_t nfonts = LENGTH(fonts_);
     40 
     41 /* n = normal, s = selected, f = foreground, b = background */
     42 const char *colnf = "#bbbbbb";
     43 const char *colnb = "#222222";
     44 const char *colsf = "#eeeeee";
     45 const char *colsb = "#113355";
     46 
     47 /* border color for focused, unfocused or urgent windows */
     48 const char *colfocus   = "#770000";
     49 const char *colunfocus = "#444444";
     50 const char *colurgent  = "#ffff00";
     51 
     52 static const char rightstack[] = "[*]:.55 []";
     53 static const char monocle[] = "()";
     54 
     55 const char *defaultlayout = rightstack;
     56 
     57 char **statusprog = ARGV("infobar");
     58 
     59 static char **scratchterm = ARGV("st", "-n", "scratchpad", "-ig", "80x24");
     60 
     61 void
     62 applyrules(Client *c)
     63 {
     64 	/*
     65 	 * In xprop(1)'s output:
     66 	 *    WM_CLASS(STRING) = app name, app class
     67 	 *    WM_NAME(STRING) = title
     68 	 */
     69 	if (!strcmp(c->appname, "scratchpad"))
     70 		setscratch(c, 1);
     71 	if (!strcmp(c->appclass, "Gimp"))
     72 		setfloating(c, 1);
     73 	else if (!strcmp(c->appclass, "Firefox"))
     74 		c->tags = 1 << (LENGTH(tags) - 1);
     75 }
     76 
     77 void
     78 execmenu(void)
     79 {
     80 	execlp("dmenu", "dmenu", "-l", "20",
     81 	       "-m", (char []){'0' + selmon->num, '\0'},
     82 	       "-nf", colnf, "-sf", colsf,
     83 	       "-nb", colnb, "-sb", colsb,
     84 	       "-fn", fonts[0], NULL);
     85 	die("execlp `dmenu':");
     86 }
     87 
     88 void
     89 gapmenu(void)
     90 {
     91 	struct {
     92 		char *s;
     93 		int d;
     94 		int *p;
     95 	} a[] = {
     96 		{ "ih", gapih, &selmon->gapih },
     97 		{ "iv", gapiv, &selmon->gapiv },
     98 		{ "oh", gapoh, &selmon->gapoh },
     99 		{ "ov", gapov, &selmon->gapov },
    100 	};
    101 	char *r, s[3], t[3];
    102 	int i, n;
    103 
    104 	newmenu();
    105 	for (i = 0; i < 4; ++i) {
    106 		menuput("gap%s = %d", a[i].s, a[i].d);
    107 		menuput("gap%s += %d", a[i].s, borderw);
    108 		menuput("gap%s -= %d", a[i].s, borderw);
    109 	}
    110 	while ((r = menuget())) {
    111 		if (sscanf(r, "gap%2s %2s %d", s, t, &n) != 3)
    112 			continue;
    113 		for (i = 0; i < 4 && strcmp(a[i].s, s); ++i)
    114 			;
    115 		if (i == 4)
    116 			continue;
    117 		switch (*t) {
    118 		case '=': *a[i].p =  n; break;
    119 		case '+': *a[i].p += n; break;
    120 		case '-': *a[i].p -= n; break;
    121 		}
    122 	}
    123 	lyttile(selmon);
    124 }
    125 
    126 void
    127 lytmenu(void)
    128 {
    129 	char *s, *t;
    130 
    131 	newmenu();
    132 	menuput("%s # right stack", rightstack);
    133 	menuput("%s # monocle", monocle);
    134 	menuput(" [*]:.55 ()  # right deck");
    135 	menuput("[{*}:.55 {}] # bottom stack");
    136 	menuput(".62 [.62 -{-[() .62] .62}] # fibonacci spiral");
    137 	menuput(".62 [.62  {.62  [.62 ()]}] # fibonacci dwindle");
    138 	menuput("-{[*] [*]:.76} []:.19 # centered master");
    139 	menuput("({*}:.55 {}) # centered pseudo-floating master");
    140 	while ((s = menuget())) {
    141 		if ((t = strchr(s, '#')))
    142 			*t = '\0';
    143 		setlayout(s);
    144 	}
    145 }
    146 
    147 #define TAGKEYS \
    148 	case XK_1: case XK_2: case XK_3: \
    149 	case XK_4: case XK_5: case XK_6: \
    150 	case XK_7: case XK_8: case XK_9
    151 
    152 #define NEXTWS(X) ((X) < LENGTH(tags) - 1 ? (X) + 1 : 0)
    153 #define PREVWS(X) ((X) ? (X) - 1 : LENGTH(tags) - 1)
    154 
    155 void
    156 keypress(XEvent *e)
    157 {
    158 	XKeyEvent *ev = &e->xkey;
    159 	KeySym keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
    160 
    161 	switch (CLEANMASK(ev->state)) {
    162 	case MODMASK:
    163 		switch (keysym) {
    164 		TAGKEYS:
    165 			setworkspace(keysym - XK_1);
    166 			break;
    167 		case XK_comma:
    168 			setworkspace(PREVWS(selmon->selws));
    169 			break;
    170 		case XK_semicolon:
    171 			setworkspace(NEXTWS(selmon->selws));
    172 			break;
    173 		case XK_Tab:
    174 			setworkspace(selmon->altws);
    175 			break;
    176 		case XK_j:
    177 		case XK_k:
    178 			if (!selmon->sel) {
    179 				focus(selmon->stack);
    180 				break;
    181 			}
    182 			if (selmon->sel->isfullscreen)
    183 				break;
    184 			if (keysym == XK_j)
    185 				focus(nextclient(selmon->sel));
    186 			else
    187 				focus(prevclient(selmon->sel));
    188 			break;
    189 		case XK_Return:
    190 			SPAWN("st");
    191 			break;
    192 		case XK_apostrophe:
    193 			togglescratch(scratchterm);
    194 			break;
    195 		case XK_Escape:
    196 			SPAWN("slock");
    197 			sleep(1);
    198 			SPAWN("xset", "dpms", "force", "standby");
    199 			break;
    200 		case XK_p:
    201 			SPAWN("dmenu_run",
    202 			      "-m", (char []){'0' + selmon->num, '\0'},
    203 			      "-nf", colnf, "-nb", colnb,
    204 			      "-sf", colsf, "-sb", colsb,
    205 			      "-fn", fonts[0]);
    206 			break;
    207 		case XK_w:
    208 			SPAWN("firefox");
    209 			break;
    210 		case XK_d:
    211 		case XK_i:
    212 			incnmaster(keysym == XK_i ? 1 : -1);
    213 			break;
    214 		case XK_h:
    215 		case XK_l:
    216 			incmfact(keysym == XK_l ? 0.05 : -0.05);
    217 			break;
    218 		case XK_m:
    219 			setlayout(monocle);
    220 			break;
    221 		case XK_t:
    222 			setlayout(rightstack);
    223 			break;
    224 		case XK_y:
    225 			lytmenu();
    226 			break;
    227 		case XK_b:
    228 			selmon->showbar = !selmon->showbar;
    229 			XMoveWindow(dpy, selmon->barwin, selmon->x, BY(selmon));
    230 			parrange(selmon);
    231 			break;
    232 		case XK_g:
    233 			gapmenu();
    234 			break;
    235 		case XK_bracketleft:
    236 			focusmon(prevmon(selmon));
    237 			break;
    238 		case XK_bracketright:
    239 			focusmon(nextmon(selmon));
    240 			break;
    241 		case XK_period:
    242 			cyclescratch();
    243 			break;
    244 		default:
    245 			if (selmon->sel)
    246 				goto needfocus1;
    247 			break;
    248 		}
    249 		break;
    250 needfocus1:
    251 		switch (keysym) {
    252 		case XK_BackSpace:
    253 			if (!sendevent(selmon->sel, wmatom[WMDeleteWindow]))
    254 				XKillClient(dpy, selmon->sel->win);
    255 			break;
    256 		case XK_f:
    257 			setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
    258 			break;
    259 		case XK_grave:
    260 			setscratch(selmon->sel, !selmon->sel->isscratch);
    261 			break;
    262 		case XK_space:
    263 			detach(selmon->sel);
    264 			attach(selmon->sel);
    265 			parrange(selmon);
    266 			break;
    267 		}
    268 		break;
    269 	case MODMASK|ShiftMask:
    270 		switch (keysym) {
    271 		case XK_f:
    272 			setlayout(NULL);
    273 			break;
    274 		case XK_g:
    275 			selmon->nogaps = !selmon->nogaps;
    276 			lyttile(selmon);
    277 			break;
    278 		case XK_Return:
    279 			spawn(scratchterm);
    280 			break;
    281 		case XK_r:
    282 			restart = 1;
    283 			/* FALLTHROUGH */
    284 		case XK_q:
    285 			running = 0;
    286 			break;
    287 		case XK_s:
    288 			sfdr < 0 ? runsprog() : endsprog();
    289 			updatestatus();
    290 			break;
    291 		default:
    292 			if (selmon->sel)
    293 				goto needfocus2;
    294 			break;
    295 		}
    296 		break;
    297 needfocus2:
    298 		switch (keysym) {
    299 		case XK_0:
    300 			tag(~0);
    301 			break;
    302 		TAGKEYS:
    303 			tag(1 << (keysym - XK_1));
    304 			break;
    305 		case XK_BackSpace:
    306 			XKillClient(dpy, selmon->sel->win);
    307 			break;
    308 		case XK_bracketleft:
    309 			sendtomon(selmon->sel, prevmon(selmon));
    310 			break;
    311 		case XK_bracketright:
    312 			sendtomon(selmon->sel, nextmon(selmon));
    313 			break;
    314 		case XK_comma:
    315 			tag(1 << PREVWS(selmon->selws));
    316 			break;
    317 		case XK_semicolon:
    318 			tag(1 << NEXTWS(selmon->selws));
    319 			break;
    320 		case XK_j:
    321 			swap(selmon->sel, nextclient(selmon->sel));
    322 			break;
    323 		case XK_k:
    324 			swap(selmon->sel, prevclient(selmon->sel));
    325 			break;
    326 		case XK_space:
    327 			setfloating(selmon->sel, !selmon->sel->isfloating);
    328 			break;
    329 		}
    330 		break;
    331 	case MODMASK|ControlMask:
    332 		switch (keysym) {
    333 		case XK_0:
    334 			view(selmon->tagset ^ ~0);
    335 			break;
    336 		TAGKEYS:
    337 			view(selmon->tagset ^ (1 << (keysym - XK_1)));
    338 			break;
    339 		}
    340 		break;
    341 	case MODMASK|ControlMask|ShiftMask:
    342 		if (!selmon->sel)
    343 			break;
    344 		switch (keysym) {
    345 		case XK_0:
    346 			tag(selmon->sel->tags ^ ~0);
    347 			break;
    348 		TAGKEYS:
    349 			tag(selmon->sel->tags ^ (1 << (keysym - XK_1)));
    350 			break;
    351 		}
    352 		break;
    353 	}
    354 }
    355 
    356 void
    357 buttonpress(XEvent *e)
    358 {
    359 	XButtonPressedEvent *ev = &e->xbutton;
    360 	Monitor *m;
    361 	Client *c;
    362 	unsigned mod;
    363 	int n;
    364 
    365 	for (m = mons; m && m->barwin != ev->window; m = m->next)
    366 		;
    367 	if (m) {
    368 		focusmon(m);
    369 		mod = CLEANMASK(ev->state);
    370 		switch (posinbar(m, ev->x, &n)) {
    371 		case BarTags:
    372 			if (!mod) {
    373 				if (ev->button == Button1)
    374 					setworkspace(n);
    375 				else if (ev->button == Button3)
    376 					view(selmon->tagset ^ (1 << n));
    377 			} else if (mod == MODMASK) {
    378 				if (ev->button == Button1)
    379 					tag(1 << n);
    380 				else if (ev->button == Button3 && selmon->sel)
    381 					tag(selmon->sel->tags ^ (1 << n));
    382 			}
    383 			break;
    384 		case BarLytDesc:
    385 			if (!mod) {
    386 				if (ev->button == Button1)
    387 					setlayout(rightstack);
    388 				else if (ev->button == Button3)
    389 					setlayout(monocle);
    390 			}
    391 			break;
    392 		case BarWinTitle:
    393 			if (!mod && ev->button == Button2 && selmon->sel) {
    394 				detach(selmon->sel);
    395 				attach(selmon->sel);
    396 				parrange(selmon);
    397 			}
    398 			break;
    399 		case BarStatus:
    400 			{
    401 				char m[5], *p = m;
    402 
    403 				*p++ = ' ';
    404 				if (mod & ControlMask)
    405 					*p++ = 'C';
    406 				if (mod & ShiftMask)
    407 					*p++ = 'S';
    408 				if (mod & MODMASK)
    409 					*p++ = 'M';
    410 				p[p[-1] == ' ' ? -1 : 0] = '\0';
    411 				dprintf(sfdw, "%d %d%s\n", n, ev->button, m);
    412 			}
    413 			break;
    414 		}
    415 	} else {
    416 		if ((c = wintoclient(ev->window))) {
    417 			if (selmon->sel != c)
    418 				focus(c);
    419 			XAllowEvents(dpy, ReplayPointer, CurrentTime);
    420 		}
    421 		if (CLEANMASK(ev->state) == MODMASK) {
    422 			switch (ev->button) {
    423 			case Button1:
    424 				movebymouse();
    425 				break;
    426 			case Button2:
    427 				if (c)
    428 					setfloating(c, !c->isfloating);
    429 				break;
    430 			case Button3:
    431 				resizebymouse();
    432 				break;
    433 			case Button4:
    434 			case Button5:
    435 				n = ev->button == Button4 ? borderw : -borderw;
    436 				selmon->gapih += n;
    437 				selmon->gapiv += n;
    438 				selmon->gapoh += n;
    439 				selmon->gapov += n;
    440 				lyttile(selmon);
    441 				break;
    442 			}
    443 		}
    444 	}
    445 }