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