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 }