swm

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

config.c (9641B)


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