misc.c (11047B)
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 <stdarg.h> 9 #include <signal.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <unistd.h> 13 #include <X11/Xlib.h> 14 15 #include "swm.h" 16 17 #define MOUSEMASK (ButtonPressMask|ButtonReleaseMask|PointerMotionMask) 18 19 static LytNode *master(LytNode *lyt); 20 21 static const unsigned ctrlshift[] = { 22 0, ControlMask, ShiftMask, ControlMask|ShiftMask 23 }; 24 static FILE *menufpr, *menufpw; 25 26 int 27 depart(int *fds) 28 { 29 int fds1[2], fds2[2]; 30 struct sigaction sa; 31 32 if (fds) { 33 if (pipe(fds1) < 0 || pipe(fds2) < 0) { 34 warn("pipe:"); 35 return -1; 36 } 37 } 38 switch (fork()) { 39 case -1: 40 if (fds) { 41 close(fds1[0]); 42 close(fds1[1]); 43 close(fds2[0]); 44 close(fds2[1]); 45 } 46 return -1; 47 case 0: 48 if (dpy) 49 close(ConnectionNumber(dpy)); 50 if (fds) { 51 close(fds1[1]); 52 close(fds2[0]); 53 if (fds1[0] != 0) { 54 if (dup2(fds1[0], 0) < 0) 55 warn("dup2:"); 56 close(fds1[0]); 57 } 58 if (fds2[1] != 1) { 59 if (dup2(fds2[1], 1) < 0) 60 warn("dup2:"); 61 close(fds2[1]); 62 } 63 } 64 sigemptyset(&sa.sa_mask); 65 sa.sa_flags = 0; 66 sa.sa_handler = SIG_DFL; 67 if (sigaction(SIGCHLD, &sa, NULL) < 0) 68 warn("sigaction:"); 69 if (setsid() < 0) 70 warn("setsid:"); 71 return 0; 72 default: 73 if (fds) { 74 close(fds1[0]); 75 close(fds2[1]); 76 fds[0] = fds2[0]; 77 fds[1] = fds1[1]; 78 } 79 return 1; 80 } 81 } 82 83 void 84 spawn(char **cmd) 85 { 86 if (depart(NULL) == 0) { 87 execvp(cmd[0], cmd); 88 die("execvp '%s':", cmd[0]); 89 } 90 } 91 92 int 93 getrootptr(int *x, int *y) 94 { 95 int di; 96 unsigned dui; 97 Window dw; 98 99 return XQueryPointer(dpy, root, &dw, &dw, x, y, &di, &di, &dui); 100 } 101 102 long 103 getstate(Window w) 104 { 105 int format; 106 long result = -1; 107 unsigned char *p = NULL; 108 unsigned long n, extra; 109 Atom real; 110 111 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, 112 wmatom[WMState], &real, &format, &n, 113 &extra, (unsigned char **)&p) != Success) 114 return -1; 115 if (n != 0) 116 result = *p; 117 XFree(p); 118 return result; 119 } 120 121 void 122 grabbuttons(Client *c, int focused) 123 { 124 int i, j; 125 126 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 127 if (!focused) 128 XGrabButton(dpy, AnyButton, AnyModifier, c->win, 129 False, ButtonPressMask|ButtonReleaseMask, 130 GrabModeSync, GrabModeSync, None, None); 131 for (i = 0; i < 4; ++i) 132 for (j = 0; j < 4; ++j) 133 XGrabButton(dpy, AnyButton, 134 MODMASK|lockmasks[i]|ctrlshift[j], 135 c->win, False, 136 ButtonPressMask|ButtonReleaseMask, 137 GrabModeAsync, GrabModeSync, None, None); 138 } 139 140 void 141 grabkeys(void) 142 { 143 int i, j; 144 145 XUngrabKey(dpy, AnyKey, AnyModifier, root); 146 for (i = 0; i < 4; ++i) 147 for (j = 0; j < 4; ++j) 148 XGrabKey(dpy, AnyKey, 149 MODMASK|lockmasks[i]|ctrlshift[j], 150 root, True, GrabModeAsync, GrabModeAsync); 151 } 152 153 void 154 swap(Client *c, Client *d) 155 { 156 Client **tc, **td; 157 Monitor *m; 158 Client *t; 159 160 if (!c || !d || c == d) 161 return; 162 tc = &c->mon->clients; 163 while (*tc && *tc != c && *tc != d) 164 tc = &(*tc)->next; 165 if (!*tc) 166 return; 167 if (*tc == d) { 168 t = c; 169 c = d; 170 d = t; 171 } 172 if (c->next == d) { 173 c->next = d->next; 174 d->next = c; 175 *tc = d; 176 } else { 177 td = c->mon == d->mon ? &c->next : &d->mon->clients; 178 while (*td && *td != d) 179 td = &(*td)->next; 180 if (!*td) 181 return; 182 *tc = d; 183 *td = c; 184 t = c->next; 185 c->next = d->next; 186 d->next = t; 187 if (c->mon != d->mon) { 188 m = c->mon; 189 c->mon = d->mon; 190 d->mon = m; 191 } 192 } 193 parrange(c->mon); 194 } 195 196 void 197 tag(unsigned ts) 198 { 199 if (selmon->sel && (ts &= TAGMASK)) { 200 selmon->sel->tags = ts; 201 focus(selmon->stack); 202 parrange(selmon); 203 pdrawbar(selmon); 204 } 205 } 206 207 void 208 view(unsigned ts) 209 { 210 selmon->tagset = ts & TAGMASK; 211 focus(selmon->stack); 212 parrange(selmon); 213 pdrawbar(selmon); 214 } 215 216 void 217 setworkspace(unsigned ws) 218 { 219 if (ws >= LENGTH(tags)) 220 return; 221 wssync(selmon); 222 selmon->altws = selmon->selws; 223 selmon->selws = ws; 224 wsload(selmon); 225 focus(selmon->stack); 226 parrange(selmon); 227 pdrawbar(selmon); 228 } 229 230 void 231 setlayout(const char *desc) 232 { 233 LytNode *lyt = NULL; 234 235 if (desc && !(lyt = lytparse(desc))) { 236 warn("failed to parse layout `%s'", desc); 237 return; 238 } 239 lytfree(selmon->lyt); 240 selmon->lyt = lyt; 241 updatelytdesc(selmon); 242 parrange(selmon); 243 pdrawbar(selmon); 244 } 245 246 LytNode * 247 master(LytNode *lyt) 248 { 249 LytNode *n = lyt; 250 251 if (!n) 252 return NULL; 253 while (n->fact < 0) { 254 if (n->type == '*' || n->p.chain == n) 255 return NULL; 256 n = n->p.chain; 257 } 258 return n; 259 } 260 261 void 262 incnmaster(int i) 263 { 264 LytNode *p, *n, *nn; 265 266 if (!(p = master(selmon->lyt))) 267 return; 268 if (i > 0) { 269 n = p->p.chain; 270 if (n != p) 271 while (!n->ending) 272 n = n->next; 273 while (i--) { 274 if (!(nn = malloc(sizeof(*nn)))) { 275 warn("error while adding masters:"); 276 break; 277 } 278 nn->type = '*'; 279 nn->ending = 1; 280 nn->trailing = 0; 281 nn->fact = -1; 282 nn->p.client = NULL; 283 nn->next = p; 284 if (n == p) { 285 n = p->p.chain = nn; 286 } else { 287 n->ending = 0; 288 n = n->next = nn; 289 } 290 } 291 } else { 292 while (p->p.chain != p && i++) { 293 n = p->p.chain; 294 if (n->ending) { 295 if (n->type != '*') 296 break; 297 free(n); 298 p->p.chain = p; 299 } else { 300 while (!n->next->ending) 301 n = n->next; 302 if (n->next->type != '*') 303 break; 304 free(n->next); 305 n->next = p; 306 n->ending = 1; 307 } 308 } 309 } 310 updatelytdesc(selmon); 311 lyttile(selmon); 312 pdrawbar(selmon); 313 } 314 315 void 316 incmfact(float f) 317 { 318 LytNode *p; 319 320 if (!(p = master(selmon->lyt))) 321 return; 322 f += p->fact; 323 p->fact = f < 0 ? 0 : f > 1 ? 1 : f; 324 updatelytdesc(selmon); 325 lyttile(selmon); 326 pdrawbar(selmon); 327 } 328 329 void 330 movebymouse(void) 331 { 332 int opx, opy, ocx, ocy; 333 int npx, npy, ncx, ncy; 334 Client *c = selmon->sel; 335 Time lasttime = 0; 336 Monitor *m; 337 XEvent ev; 338 339 if (!c || c->isfullscreen) 340 return; 341 ocx = c->x; 342 ocy = c->y; 343 if (XGrabPointer(dpy, root, False, MOUSEMASK, 344 GrabModeAsync, GrabModeAsync, 345 None, curmove, CurrentTime) != GrabSuccess) 346 return; 347 if (!getrootptr(&opx, &opy)) 348 return; 349 npx = opx; 350 npy = opy; 351 do { 352 XMaskEvent(dpy, MOUSEMASK|ExposureMask 353 |SubstructureRedirectMask, &ev); 354 switch(ev.type) { 355 case ConfigureRequest: 356 case Expose: 357 case MapRequest: 358 handler[ev.type](&ev); 359 break; 360 case MotionNotify: 361 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 362 continue; 363 lasttime = ev.xmotion.time; 364 npx = ev.xmotion.x; 365 npy = ev.xmotion.y; 366 ncx = ocx - opx + npx; 367 ncy = ocy - opy + npy; 368 if (abs(selmon->x - ncx) < snap) 369 ncx = selmon->x; 370 else if (abs((selmon->x + selmon->w) 371 - (ncx + WIDTH(c))) < snap) 372 ncx = selmon->x + selmon->w - WIDTH(c); 373 if (abs(WY(selmon) - ncy) < snap) 374 ncy = WY(selmon); 375 else if (abs((WY(selmon) + WH(selmon)) 376 - (ncy + HEIGHT(c))) < snap) 377 ncy = WY(selmon) + WH(selmon) - HEIGHT(c); 378 if (!c->isfloating && selmon->lyt 379 && (abs(ncx - c->x) > snap || abs(ncy - c->y) > snap)) 380 setfloating(c, 1); 381 if (!selmon->lyt || c->isfloating) { 382 if (c->x == ncx && c->y == ncy) 383 break; 384 c->x = ncx; 385 c->y = ncy; 386 configure(c, 0); 387 XSync(dpy, False); 388 } else if (abs(ncx - c->x) > snap 389 || abs(ncy - c->y) > snap) { 390 setfloating(c, 1); 391 } 392 break; 393 } 394 } while (ev.type != ButtonRelease); 395 XUngrabPointer(dpy, CurrentTime); 396 if (!(m = pttomon(c->x + WIDTH(c) / 2, c->y + HEIGHT(c) / 2))) 397 m = pttomon(npx, npy); 398 if (m && m != selmon) { 399 sendtomon(c, m); 400 focus(c); 401 } 402 } 403 404 void 405 resizebymouse(void) 406 { 407 int nw, nh; 408 Client *c = selmon->sel; 409 Time lasttime = 0; 410 Monitor *m; 411 XEvent ev; 412 413 if (!c || c->isfullscreen) 414 return; 415 if (XGrabPointer(dpy, root, False, MOUSEMASK, 416 GrabModeAsync, GrabModeAsync, 417 None, curresize, CurrentTime) != GrabSuccess) 418 return; 419 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, 420 c->w + borderw - 1, c->h + borderw - 1); 421 do { 422 XMaskEvent(dpy, MOUSEMASK|ExposureMask 423 |SubstructureRedirectMask, &ev); 424 switch(ev.type) { 425 case ConfigureRequest: 426 case Expose: 427 case MapRequest: 428 handler[ev.type](&ev); 429 break; 430 case MotionNotify: 431 if ((ev.xmotion.time - lasttime) <= (1000 / 60)) 432 continue; 433 lasttime = ev.xmotion.time; 434 435 nw = MAX(ev.xmotion.x - c->x - 2 * borderw + 1, 1); 436 nh = MAX(ev.xmotion.y - c->y - 2 * borderw + 1, 1); 437 if (!selmon->lyt || c->isfloating) { 438 applysizehints(c, &nw, &nh); 439 if (c->w == nw && c->h == nh) 440 break; 441 c->w = nw; 442 c->h = nh; 443 configure(c, 1); 444 XSync(dpy, False); 445 } else if (abs(nw - c->w) > snap 446 || abs(nh - c->h) > snap) { 447 setfloating(c, 1); 448 } 449 break; 450 } 451 } while (ev.type != ButtonRelease); 452 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, 453 c->w + borderw - 1, c->h + borderw - 1); 454 XUngrabPointer(dpy, CurrentTime); 455 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) 456 ; 457 m = pttomon(c->x + WIDTH(c) / 2, c->y + HEIGHT(c) / 2); 458 if (m && m != selmon) { 459 sendtomon(c, m); 460 focus(c); 461 } 462 } 463 464 void 465 togglescratch(char **cmd) 466 { 467 Client *c, *d; 468 469 for (c = selmon->stack; c && !c->isscratch; c = c->snext) 470 ; 471 if (!c) { 472 if (cmd) 473 spawn(cmd); 474 return; 475 } 476 for (d = c; d && (!d->isscratch || !ISVISIBLE(d)); d = d->snext) 477 ; 478 if (d) { 479 d->tags &= ~selmon->tagset; 480 if (selmon->sel == d) 481 focus(selmon->stack); 482 if (d != c) { 483 detachstack(d); 484 attachstack(d); 485 } 486 } else { 487 if (!(c->tags |= selmon->tagset)) 488 return; 489 focus(c); 490 } 491 parrange(selmon); 492 pdrawbar(selmon); 493 } 494 495 void 496 cyclescratch(void) 497 { 498 Client *c; 499 500 if (!selmon->sel || !selmon->sel->isscratch) { 501 c = selmon->clients; 502 while (c && (!c->isscratch || ISVISIBLE(c))) 503 c = c->next; 504 if (!c) 505 return; 506 } else { 507 c = selmon->sel; 508 do { 509 c = c->next ? c->next : selmon->clients; 510 } while (c != selmon->sel && (!c->isscratch || ISVISIBLE(c))); 511 if (c == selmon->sel) 512 return; 513 selmon->sel->tags &= ~selmon->tagset; 514 } 515 if (!(c->tags |= selmon->tagset)) 516 return; 517 focus(c); 518 parrange(selmon); 519 pdrawbar(selmon); 520 } 521 522 void 523 newmenu(void) 524 { 525 int fds[2]; 526 527 if (menufpw) 528 fclose(menufpw); 529 if (menufpr) 530 fclose(menufpr); 531 menufpw = menufpr = NULL; 532 switch (depart(fds)) { 533 case -1: 534 warn("failed to start a menu"); 535 return; 536 case 0: 537 execmenu(); 538 return; 539 default: 540 break; 541 } 542 if (!(menufpr = fdopen(fds[0], "r")) 543 || !(menufpw = fdopen(fds[1], "w"))) { 544 warn("failed to start a menu: fdopen:"); 545 if (menufpr) 546 fclose(menufpr); 547 else 548 close(fds[0]); 549 close(fds[1]); 550 return; 551 } 552 } 553 554 void 555 menuput(const char *fmt, ...) 556 { 557 va_list ap; 558 559 if (!menufpw) 560 return; 561 va_start(ap, fmt); 562 vfprintf(menufpw, fmt, ap); 563 fputc('\n', menufpw); 564 va_end(ap); 565 } 566 567 char * 568 menuget(void) 569 { 570 static char *line = NULL; 571 static size_t n; 572 ssize_t len; 573 574 if (menufpw) { 575 fclose(menufpw); 576 menufpw = NULL; 577 } 578 if ((len = getline(&line, &n, menufpr)) != -1) { 579 if (len && line[len - 1] == '\n') 580 line[len - 1] = '\0'; 581 return line; 582 } 583 fclose(menufpr); 584 menufpr = NULL; 585 free(line); 586 line = NULL; 587 n = 0; 588 return NULL; 589 }