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 }