1319 XSync(dpy, False); |
1282 XSync(dpy, False); |
1320 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
1283 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
1321 } |
1284 } |
1322 |
1285 |
1323 static void |
1286 static void |
1324 scan(void) { |
1287 run(void) { |
1325 unsigned int i, num; |
|
1326 Window *wins, d1, d2; |
|
1327 XWindowAttributes wa; |
|
1328 |
|
1329 wins = NULL; |
|
1330 if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { |
|
1331 for(i = 0; i < num; i++) { |
|
1332 if(!XGetWindowAttributes(dpy, wins[i], &wa) |
|
1333 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) |
|
1334 continue; |
|
1335 if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState) |
|
1336 manage(wins[i], &wa); |
|
1337 } |
|
1338 for(i = 0; i < num; i++) { /* now the transients */ |
|
1339 if(!XGetWindowAttributes(dpy, wins[i], &wa)) |
|
1340 continue; |
|
1341 if(XGetTransientForHint(dpy, wins[i], &d1) |
|
1342 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) |
|
1343 manage(wins[i], &wa); |
|
1344 } |
|
1345 } |
|
1346 if(wins) |
|
1347 XFree(wins); |
|
1348 } |
|
1349 |
|
1350 static void |
|
1351 setclientstate(Client *c, long state) { |
|
1352 long data[] = {state, None}; |
|
1353 |
|
1354 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
|
1355 PropModeReplace, (unsigned char *)data, 2); |
|
1356 } |
|
1357 |
|
1358 static void |
|
1359 setlayout(const char *arg) { |
|
1360 unsigned int i; |
|
1361 |
|
1362 if(!arg) { |
|
1363 if(++ltidx == nlayouts) |
|
1364 ltidx = 0;; |
|
1365 } |
|
1366 else { |
|
1367 for(i = 0; i < nlayouts; i++) |
|
1368 if(!strcmp(arg, layouts[i].symbol)) |
|
1369 break; |
|
1370 if(i == nlayouts) |
|
1371 return; |
|
1372 ltidx = i; |
|
1373 } |
|
1374 if(sel) |
|
1375 arrange(); |
|
1376 else |
|
1377 drawbar(); |
|
1378 } |
|
1379 |
|
1380 static void |
|
1381 setmwfact(const char *arg) { |
|
1382 double delta; |
|
1383 |
|
1384 if(!isarrange(tile)) |
|
1385 return; |
|
1386 /* arg handling, manipulate mwfact */ |
|
1387 if(arg == NULL) |
|
1388 mwfact = MWFACT; |
|
1389 else if(1 == sscanf(arg, "%lf", &delta)) { |
|
1390 if(arg[0] != '+' && arg[0] != '-') |
|
1391 mwfact = delta; |
|
1392 else |
|
1393 mwfact += delta; |
|
1394 if(mwfact < 0.1) |
|
1395 mwfact = 0.1; |
|
1396 else if(mwfact > 0.9) |
|
1397 mwfact = 0.9; |
|
1398 } |
|
1399 arrange(); |
|
1400 } |
|
1401 |
|
1402 static void |
|
1403 setup(void) { |
|
1404 int i, j; |
|
1405 unsigned int mask; |
|
1406 Window w; |
|
1407 XModifierKeymap *modmap; |
|
1408 XSetWindowAttributes wa; |
|
1409 |
|
1410 /* init atoms */ |
|
1411 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); |
|
1412 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|
1413 wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False); |
|
1414 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); |
|
1415 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); |
|
1416 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); |
|
1417 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, |
|
1418 PropModeReplace, (unsigned char *) netatom, NetLast); |
|
1419 /* init cursors */ |
|
1420 cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); |
|
1421 cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); |
|
1422 cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); |
|
1423 /* init modifier map */ |
|
1424 modmap = XGetModifierMapping(dpy); |
|
1425 for (i = 0; i < 8; i++) |
|
1426 for (j = 0; j < modmap->max_keypermod; j++) { |
|
1427 if(modmap->modifiermap[i * modmap->max_keypermod + j] |
|
1428 == XKeysymToKeycode(dpy, XK_Num_Lock)) |
|
1429 numlockmask = (1 << i); |
|
1430 } |
|
1431 XFreeModifiermap(modmap); |
|
1432 /* select for events */ |
|
1433 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
|
1434 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask; |
|
1435 wa.cursor = cursor[CurNormal]; |
|
1436 XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); |
|
1437 XSelectInput(dpy, root, wa.event_mask); |
|
1438 keypress(NULL); /* grabkeys */ |
|
1439 compileregs(); |
|
1440 for(ntags = 0; tags[ntags]; ntags++); |
|
1441 seltags = emallocz(sizeof(Bool) * ntags); |
|
1442 seltags[0] = True; |
|
1443 /* geometry */ |
|
1444 sx = sy = 0; |
|
1445 sw = DisplayWidth(dpy, screen); |
|
1446 sh = DisplayHeight(dpy, screen); |
|
1447 initstyle(); |
|
1448 initlayouts(); |
|
1449 initbar(); |
|
1450 /* multihead support */ |
|
1451 selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); |
|
1452 } |
|
1453 |
|
1454 static void |
|
1455 spawn(const char *arg) { |
|
1456 static char *shell = NULL; |
|
1457 |
|
1458 if(!shell && !(shell = getenv("SHELL"))) |
|
1459 shell = "/bin/sh"; |
|
1460 if(!arg) |
|
1461 return; |
|
1462 /* The double-fork construct avoids zombie processes and keeps the code |
|
1463 * clean from stupid signal handlers. */ |
|
1464 if(fork() == 0) { |
|
1465 if(fork() == 0) { |
|
1466 if(dpy) |
|
1467 close(ConnectionNumber(dpy)); |
|
1468 setsid(); |
|
1469 execl(shell, shell, "-c", arg, (char *)NULL); |
|
1470 fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); |
|
1471 perror(" failed"); |
|
1472 } |
|
1473 exit(0); |
|
1474 } |
|
1475 wait(0); |
|
1476 } |
|
1477 |
|
1478 static void |
|
1479 tag(const char *arg) { |
|
1480 unsigned int i; |
|
1481 |
|
1482 if(!sel) |
|
1483 return; |
|
1484 for(i = 0; i < ntags; i++) |
|
1485 sel->tags[i] = arg == NULL; |
|
1486 i = idxoftag(arg); |
|
1487 if(i >= 0 && i < ntags) |
|
1488 sel->tags[i] = True; |
|
1489 arrange(); |
|
1490 } |
|
1491 |
|
1492 static unsigned int |
|
1493 textnw(const char *text, unsigned int len) { |
|
1494 XRectangle r; |
|
1495 |
|
1496 if(dc.font.set) { |
|
1497 XmbTextExtents(dc.font.set, text, len, NULL, &r); |
|
1498 return r.width; |
|
1499 } |
|
1500 return XTextWidth(dc.font.xfont, text, len); |
|
1501 } |
|
1502 |
|
1503 static unsigned int |
|
1504 textw(const char *text) { |
|
1505 return textnw(text, strlen(text)) + dc.font.height; |
|
1506 } |
|
1507 |
|
1508 static void |
|
1509 tile(void) { |
|
1510 unsigned int i, n, nx, ny, nw, nh, mw, th; |
|
1511 Client *c; |
|
1512 |
|
1513 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) |
|
1514 n++; |
|
1515 |
|
1516 /* window geoms */ |
|
1517 mw = (n == 1) ? waw : mwfact * waw; |
|
1518 th = (n > 1) ? wah / (n - 1) : 0; |
|
1519 if(n > 1 && th < bh) |
|
1520 th = wah; |
|
1521 |
|
1522 nx = wax; |
|
1523 ny = way; |
|
1524 for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { |
|
1525 c->ismax = False; |
|
1526 if(i == 0) { /* master */ |
|
1527 nw = mw - 2 * c->border; |
|
1528 nh = wah - 2 * c->border; |
|
1529 } |
|
1530 else { /* tile window */ |
|
1531 if(i == 1) { |
|
1532 ny = way; |
|
1533 nx += mw; |
|
1534 } |
|
1535 nw = waw - mw - 2 * c->border; |
|
1536 if(i + 1 == n) /* remainder */ |
|
1537 nh = (way + wah) - ny - 2 * c->border; |
|
1538 else |
|
1539 nh = th - 2 * c->border; |
|
1540 } |
|
1541 resize(c, nx, ny, nw, nh, RESIZEHINTS); |
|
1542 if(n > 1 && th != wah) |
|
1543 ny += nh + 2 * c->border; |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 static void |
|
1548 togglebar(const char *arg) { |
|
1549 if(bpos == BarOff) |
|
1550 bpos = (BARPOS == BarOff) ? BarTop : BARPOS; |
|
1551 else |
|
1552 bpos = BarOff; |
|
1553 updatebarpos(); |
|
1554 arrange(); |
|
1555 } |
|
1556 |
|
1557 static void |
|
1558 togglefloating(const char *arg) { |
|
1559 if(!sel) |
|
1560 return; |
|
1561 sel->isfloating = !sel->isfloating; |
|
1562 if(sel->isfloating) |
|
1563 resize(sel, sel->x, sel->y, sel->w, sel->h, True); |
|
1564 arrange(); |
|
1565 } |
|
1566 |
|
1567 static void |
|
1568 togglemax(const char *arg) { |
|
1569 XEvent ev; |
|
1570 |
|
1571 if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) |
|
1572 return; |
|
1573 if((sel->ismax = !sel->ismax)) { |
|
1574 sel->rx = sel->x; |
|
1575 sel->ry = sel->y; |
|
1576 sel->rw = sel->w; |
|
1577 sel->rh = sel->h; |
|
1578 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); |
|
1579 } |
|
1580 else |
|
1581 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); |
|
1582 drawbar(); |
|
1583 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1584 } |
|
1585 |
|
1586 static void |
|
1587 toggletag(const char *arg) { |
|
1588 unsigned int i, j; |
|
1589 |
|
1590 if(!sel) |
|
1591 return; |
|
1592 i = idxoftag(arg); |
|
1593 sel->tags[i] = !sel->tags[i]; |
|
1594 for(j = 0; j < ntags && !sel->tags[j]; j++); |
|
1595 if(j == ntags) |
|
1596 sel->tags[i] = True; |
|
1597 arrange(); |
|
1598 } |
|
1599 |
|
1600 static void |
|
1601 toggleview(const char *arg) { |
|
1602 unsigned int i, j; |
|
1603 |
|
1604 i = idxoftag(arg); |
|
1605 seltags[i] = !seltags[i]; |
|
1606 for(j = 0; j < ntags && !seltags[j]; j++); |
|
1607 if(j == ntags) |
|
1608 seltags[i] = True; /* cannot toggle last view */ |
|
1609 arrange(); |
|
1610 } |
|
1611 |
|
1612 static void |
|
1613 unban(Client *c) { |
|
1614 if(!c->isbanned) |
|
1615 return; |
|
1616 XMoveWindow(dpy, c->win, c->x, c->y); |
|
1617 c->isbanned = False; |
|
1618 } |
|
1619 |
|
1620 static void |
|
1621 unmanage(Client *c) { |
|
1622 XWindowChanges wc; |
|
1623 |
|
1624 wc.border_width = c->oldborder; |
|
1625 /* The server grab construct avoids race conditions. */ |
|
1626 XGrabServer(dpy); |
|
1627 XSetErrorHandler(xerrordummy); |
|
1628 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
|
1629 detach(c); |
|
1630 detachstack(c); |
|
1631 if(sel == c) |
|
1632 focus(NULL); |
|
1633 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
1634 setclientstate(c, WithdrawnState); |
|
1635 free(c->tags); |
|
1636 free(c); |
|
1637 XSync(dpy, False); |
|
1638 XSetErrorHandler(xerror); |
|
1639 XUngrabServer(dpy); |
|
1640 arrange(); |
|
1641 } |
|
1642 |
|
1643 static void |
|
1644 unmapnotify(XEvent *e) { |
|
1645 Client *c; |
|
1646 XUnmapEvent *ev = &e->xunmap; |
|
1647 |
|
1648 if((c = getclient(ev->window))) |
|
1649 unmanage(c); |
|
1650 } |
|
1651 |
|
1652 static void |
|
1653 updatebarpos(void) { |
|
1654 XEvent ev; |
|
1655 |
|
1656 wax = sx; |
|
1657 way = sy; |
|
1658 wah = sh; |
|
1659 waw = sw; |
|
1660 switch(bpos) { |
|
1661 default: |
|
1662 wah -= bh; |
|
1663 way += bh; |
|
1664 XMoveWindow(dpy, barwin, sx, sy); |
|
1665 break; |
|
1666 case BarBot: |
|
1667 wah -= bh; |
|
1668 XMoveWindow(dpy, barwin, sx, sy + wah); |
|
1669 break; |
|
1670 case BarOff: |
|
1671 XMoveWindow(dpy, barwin, sx, sy - bh); |
|
1672 break; |
|
1673 } |
|
1674 XSync(dpy, False); |
|
1675 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1676 } |
|
1677 |
|
1678 static void |
|
1679 updatesizehints(Client *c) { |
|
1680 long msize; |
|
1681 XSizeHints size; |
|
1682 |
|
1683 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) |
|
1684 size.flags = PSize; |
|
1685 c->flags = size.flags; |
|
1686 if(c->flags & PBaseSize) { |
|
1687 c->basew = size.base_width; |
|
1688 c->baseh = size.base_height; |
|
1689 } |
|
1690 else if(c->flags & PMinSize) { |
|
1691 c->basew = size.min_width; |
|
1692 c->baseh = size.min_height; |
|
1693 } |
|
1694 else |
|
1695 c->basew = c->baseh = 0; |
|
1696 if(c->flags & PResizeInc) { |
|
1697 c->incw = size.width_inc; |
|
1698 c->inch = size.height_inc; |
|
1699 } |
|
1700 else |
|
1701 c->incw = c->inch = 0; |
|
1702 if(c->flags & PMaxSize) { |
|
1703 c->maxw = size.max_width; |
|
1704 c->maxh = size.max_height; |
|
1705 } |
|
1706 else |
|
1707 c->maxw = c->maxh = 0; |
|
1708 if(c->flags & PMinSize) { |
|
1709 c->minw = size.min_width; |
|
1710 c->minh = size.min_height; |
|
1711 } |
|
1712 else if(c->flags & PBaseSize) { |
|
1713 c->minw = size.base_width; |
|
1714 c->minh = size.base_height; |
|
1715 } |
|
1716 else |
|
1717 c->minw = c->minh = 0; |
|
1718 if(c->flags & PAspect) { |
|
1719 c->minax = size.min_aspect.x; |
|
1720 c->maxax = size.max_aspect.x; |
|
1721 c->minay = size.min_aspect.y; |
|
1722 c->maxay = size.max_aspect.y; |
|
1723 } |
|
1724 else |
|
1725 c->minax = c->maxax = c->minay = c->maxay = 0; |
|
1726 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh |
|
1727 && c->maxw == c->minw && c->maxh == c->minh); |
|
1728 } |
|
1729 |
|
1730 static void |
|
1731 updatetitle(Client *c) { |
|
1732 if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
|
1733 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); |
|
1734 } |
|
1735 |
|
1736 /* There's no way to check accesses to destroyed windows, thus those cases are |
|
1737 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
|
1738 * default error handler, which may call exit. */ |
|
1739 static int |
|
1740 xerror(Display *dpy, XErrorEvent *ee) { |
|
1741 if(ee->error_code == BadWindow |
|
1742 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
|
1743 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
|
1744 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) |
|
1745 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) |
|
1746 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) |
|
1747 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) |
|
1748 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) |
|
1749 return 0; |
|
1750 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
|
1751 ee->request_code, ee->error_code); |
|
1752 return xerrorxlib(dpy, ee); /* may call exit */ |
|
1753 } |
|
1754 |
|
1755 static int |
|
1756 xerrordummy(Display *dsply, XErrorEvent *ee) { |
|
1757 return 0; |
|
1758 } |
|
1759 |
|
1760 /* Startup Error handler to check if another window manager |
|
1761 * is already running. */ |
|
1762 static int |
|
1763 xerrorstart(Display *dsply, XErrorEvent *ee) { |
|
1764 otherwm = True; |
|
1765 return -1; |
|
1766 } |
|
1767 |
|
1768 static void |
|
1769 view(const char *arg) { |
|
1770 unsigned int i; |
|
1771 |
|
1772 for(i = 0; i < ntags; i++) |
|
1773 seltags[i] = arg == NULL; |
|
1774 i = idxoftag(arg); |
|
1775 if(i >= 0 && i < ntags) |
|
1776 seltags[i] = True; |
|
1777 arrange(); |
|
1778 } |
|
1779 |
|
1780 static void |
|
1781 zoom(const char *arg) { |
|
1782 Client *c; |
|
1783 |
|
1784 if(!sel || !isarrange(tile) || sel->isfloating) |
|
1785 return; |
|
1786 if((c = sel) == nexttiled(clients)) |
|
1787 if(!(c = nexttiled(c->next))) |
|
1788 return; |
|
1789 detach(c); |
|
1790 attach(c); |
|
1791 focus(c); |
|
1792 arrange(); |
|
1793 } |
|
1794 |
|
1795 int |
|
1796 main(int argc, char *argv[]) { |
|
1797 char *p; |
1288 char *p; |
1798 int r, xfd; |
1289 int r, xfd; |
1799 fd_set rd; |
1290 fd_set rd; |
1800 XEvent ev; |
1291 XEvent ev; |
1801 |
1292 |
1802 if(argc == 2 && !strcmp("-v", argv[1])) |
|
1803 eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n"); |
|
1804 else if(argc != 1) |
|
1805 eprint("usage: dwm [-v]\n"); |
|
1806 |
|
1807 /* macros from config.h can be used at function level only */ |
|
1808 mwfact = MWFACT; |
|
1809 bpos = BARPOS; |
|
1810 |
|
1811 setlocale(LC_CTYPE, ""); |
|
1812 if(!(dpy = XOpenDisplay(0))) |
|
1813 eprint("dwm: cannot open display\n"); |
|
1814 xfd = ConnectionNumber(dpy); |
|
1815 screen = DefaultScreen(dpy); |
|
1816 root = RootWindow(dpy, screen); |
|
1817 otherwm = False; |
|
1818 XSetErrorHandler(xerrorstart); |
|
1819 /* this causes an error if some other window manager is running */ |
|
1820 XSelectInput(dpy, root, SubstructureRedirectMask); |
|
1821 XSync(dpy, False); |
|
1822 if(otherwm) |
|
1823 eprint("dwm: another window manager is already running\n"); |
|
1824 |
|
1825 XSync(dpy, False); |
|
1826 XSetErrorHandler(NULL); |
|
1827 xerrorxlib = XSetErrorHandler(xerror); |
|
1828 XSync(dpy, False); |
|
1829 setup(); |
|
1830 drawbar(); |
|
1831 scan(); |
|
1832 |
|
1833 /* main event loop, also reads status text from stdin */ |
1293 /* main event loop, also reads status text from stdin */ |
1834 XSync(dpy, False); |
1294 XSync(dpy, False); |
|
1295 xfd = ConnectionNumber(dpy); |
1835 readin = True; |
1296 readin = True; |
1836 while(running) { |
1297 while(running) { |
1837 FD_ZERO(&rd); |
1298 FD_ZERO(&rd); |
1838 if(readin) |
1299 if(readin) |
1839 FD_SET(STDIN_FILENO, &rd); |
1300 FD_SET(STDIN_FILENO, &rd); |
1866 XNextEvent(dpy, &ev); |
1327 XNextEvent(dpy, &ev); |
1867 if(handler[ev.type]) |
1328 if(handler[ev.type]) |
1868 (handler[ev.type])(&ev); /* call handler */ |
1329 (handler[ev.type])(&ev); /* call handler */ |
1869 } |
1330 } |
1870 } |
1331 } |
|
1332 } |
|
1333 |
|
1334 static void |
|
1335 scan(void) { |
|
1336 unsigned int i, num; |
|
1337 Window *wins, d1, d2; |
|
1338 XWindowAttributes wa; |
|
1339 |
|
1340 wins = NULL; |
|
1341 if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { |
|
1342 for(i = 0; i < num; i++) { |
|
1343 if(!XGetWindowAttributes(dpy, wins[i], &wa) |
|
1344 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) |
|
1345 continue; |
|
1346 if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState) |
|
1347 manage(wins[i], &wa); |
|
1348 } |
|
1349 for(i = 0; i < num; i++) { /* now the transients */ |
|
1350 if(!XGetWindowAttributes(dpy, wins[i], &wa)) |
|
1351 continue; |
|
1352 if(XGetTransientForHint(dpy, wins[i], &d1) |
|
1353 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) |
|
1354 manage(wins[i], &wa); |
|
1355 } |
|
1356 } |
|
1357 if(wins) |
|
1358 XFree(wins); |
|
1359 } |
|
1360 |
|
1361 static void |
|
1362 setclientstate(Client *c, long state) { |
|
1363 long data[] = {state, None}; |
|
1364 |
|
1365 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
|
1366 PropModeReplace, (unsigned char *)data, 2); |
|
1367 } |
|
1368 |
|
1369 static void |
|
1370 setlayout(const char *arg) { |
|
1371 unsigned int i; |
|
1372 |
|
1373 if(!arg) { |
|
1374 if(++ltidx == nlayouts) |
|
1375 ltidx = 0;; |
|
1376 } |
|
1377 else { |
|
1378 for(i = 0; i < nlayouts; i++) |
|
1379 if(!strcmp(arg, layouts[i].symbol)) |
|
1380 break; |
|
1381 if(i == nlayouts) |
|
1382 return; |
|
1383 ltidx = i; |
|
1384 } |
|
1385 if(sel) |
|
1386 arrange(); |
|
1387 else |
|
1388 drawbar(); |
|
1389 } |
|
1390 |
|
1391 static void |
|
1392 setmwfact(const char *arg) { |
|
1393 double delta; |
|
1394 |
|
1395 if(!isarrange(tile)) |
|
1396 return; |
|
1397 /* arg handling, manipulate mwfact */ |
|
1398 if(arg == NULL) |
|
1399 mwfact = MWFACT; |
|
1400 else if(1 == sscanf(arg, "%lf", &delta)) { |
|
1401 if(arg[0] != '+' && arg[0] != '-') |
|
1402 mwfact = delta; |
|
1403 else |
|
1404 mwfact += delta; |
|
1405 if(mwfact < 0.1) |
|
1406 mwfact = 0.1; |
|
1407 else if(mwfact > 0.9) |
|
1408 mwfact = 0.9; |
|
1409 } |
|
1410 arrange(); |
|
1411 } |
|
1412 |
|
1413 static void |
|
1414 setup(void) { |
|
1415 unsigned int i, j, mask; |
|
1416 Window w; |
|
1417 XModifierKeymap *modmap; |
|
1418 XSetWindowAttributes wa; |
|
1419 |
|
1420 /* init atoms */ |
|
1421 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); |
|
1422 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|
1423 wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False); |
|
1424 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); |
|
1425 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); |
|
1426 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); |
|
1427 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, |
|
1428 PropModeReplace, (unsigned char *) netatom, NetLast); |
|
1429 |
|
1430 /* init cursors */ |
|
1431 cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); |
|
1432 cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); |
|
1433 cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); |
|
1434 |
|
1435 /* init geometry */ |
|
1436 sx = sy = 0; |
|
1437 sw = DisplayWidth(dpy, screen); |
|
1438 sh = DisplayHeight(dpy, screen); |
|
1439 |
|
1440 /* init modifier map */ |
|
1441 modmap = XGetModifierMapping(dpy); |
|
1442 for(i = 0; i < 8; i++) |
|
1443 for(j = 0; j < modmap->max_keypermod; j++) { |
|
1444 if(modmap->modifiermap[i * modmap->max_keypermod + j] |
|
1445 == XKeysymToKeycode(dpy, XK_Num_Lock)) |
|
1446 numlockmask = (1 << i); |
|
1447 } |
|
1448 XFreeModifiermap(modmap); |
|
1449 |
|
1450 /* select for events */ |
|
1451 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
|
1452 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask; |
|
1453 wa.cursor = cursor[CurNormal]; |
|
1454 XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); |
|
1455 XSelectInput(dpy, root, wa.event_mask); |
|
1456 |
|
1457 /* grab keys */ |
|
1458 keypress(NULL); |
|
1459 |
|
1460 /* init tags */ |
|
1461 compileregs(); |
|
1462 for(ntags = 0; tags[ntags]; ntags++); |
|
1463 seltags = emallocz(sizeof(Bool) * ntags); |
|
1464 seltags[0] = True; |
|
1465 |
|
1466 /* init appearance */ |
|
1467 dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR); |
|
1468 dc.norm[ColBG] = getcolor(NORMBGCOLOR); |
|
1469 dc.norm[ColFG] = getcolor(NORMFGCOLOR); |
|
1470 dc.sel[ColBorder] = getcolor(SELBORDERCOLOR); |
|
1471 dc.sel[ColBG] = getcolor(SELBGCOLOR); |
|
1472 dc.sel[ColFG] = getcolor(SELFGCOLOR); |
|
1473 initfont(FONT); |
|
1474 dc.h = bh = dc.font.height + 2; |
|
1475 |
|
1476 /* init layouts */ |
|
1477 mwfact = MWFACT; |
|
1478 nlayouts = sizeof layouts / sizeof layouts[0]; |
|
1479 for(blw = i = 0; i < nlayouts; i++) { |
|
1480 j = textw(layouts[i].symbol); |
|
1481 if(j > blw) |
|
1482 blw = j; |
|
1483 } |
|
1484 |
|
1485 /* init bar */ |
|
1486 bpos = BARPOS; |
|
1487 wa.override_redirect = 1; |
|
1488 wa.background_pixmap = ParentRelative; |
|
1489 wa.event_mask = ButtonPressMask | ExposureMask; |
|
1490 barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, |
|
1491 DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), |
|
1492 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
|
1493 XDefineCursor(dpy, barwin, cursor[CurNormal]); |
|
1494 updatebarpos(); |
|
1495 XMapRaised(dpy, barwin); |
|
1496 strcpy(stext, "dwm-"VERSION); |
|
1497 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); |
|
1498 dc.gc = XCreateGC(dpy, root, 0, 0); |
|
1499 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); |
|
1500 if(!dc.font.set) |
|
1501 XSetFont(dpy, dc.gc, dc.font.xfont->fid); |
|
1502 |
|
1503 /* multihead support */ |
|
1504 selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); |
|
1505 } |
|
1506 |
|
1507 static void |
|
1508 spawn(const char *arg) { |
|
1509 static char *shell = NULL; |
|
1510 |
|
1511 if(!shell && !(shell = getenv("SHELL"))) |
|
1512 shell = "/bin/sh"; |
|
1513 if(!arg) |
|
1514 return; |
|
1515 /* The double-fork construct avoids zombie processes and keeps the code |
|
1516 * clean from stupid signal handlers. */ |
|
1517 if(fork() == 0) { |
|
1518 if(fork() == 0) { |
|
1519 if(dpy) |
|
1520 close(ConnectionNumber(dpy)); |
|
1521 setsid(); |
|
1522 execl(shell, shell, "-c", arg, (char *)NULL); |
|
1523 fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); |
|
1524 perror(" failed"); |
|
1525 } |
|
1526 exit(0); |
|
1527 } |
|
1528 wait(0); |
|
1529 } |
|
1530 |
|
1531 static void |
|
1532 tag(const char *arg) { |
|
1533 unsigned int i; |
|
1534 |
|
1535 if(!sel) |
|
1536 return; |
|
1537 for(i = 0; i < ntags; i++) |
|
1538 sel->tags[i] = arg == NULL; |
|
1539 i = idxoftag(arg); |
|
1540 if(i >= 0 && i < ntags) |
|
1541 sel->tags[i] = True; |
|
1542 arrange(); |
|
1543 } |
|
1544 |
|
1545 static unsigned int |
|
1546 textnw(const char *text, unsigned int len) { |
|
1547 XRectangle r; |
|
1548 |
|
1549 if(dc.font.set) { |
|
1550 XmbTextExtents(dc.font.set, text, len, NULL, &r); |
|
1551 return r.width; |
|
1552 } |
|
1553 return XTextWidth(dc.font.xfont, text, len); |
|
1554 } |
|
1555 |
|
1556 static unsigned int |
|
1557 textw(const char *text) { |
|
1558 return textnw(text, strlen(text)) + dc.font.height; |
|
1559 } |
|
1560 |
|
1561 static void |
|
1562 tile(void) { |
|
1563 unsigned int i, n, nx, ny, nw, nh, mw, th; |
|
1564 Client *c; |
|
1565 |
|
1566 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) |
|
1567 n++; |
|
1568 |
|
1569 /* window geoms */ |
|
1570 mw = (n == 1) ? waw : mwfact * waw; |
|
1571 th = (n > 1) ? wah / (n - 1) : 0; |
|
1572 if(n > 1 && th < bh) |
|
1573 th = wah; |
|
1574 |
|
1575 nx = wax; |
|
1576 ny = way; |
|
1577 for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { |
|
1578 c->ismax = False; |
|
1579 if(i == 0) { /* master */ |
|
1580 nw = mw - 2 * c->border; |
|
1581 nh = wah - 2 * c->border; |
|
1582 } |
|
1583 else { /* tile window */ |
|
1584 if(i == 1) { |
|
1585 ny = way; |
|
1586 nx += mw; |
|
1587 } |
|
1588 nw = waw - mw - 2 * c->border; |
|
1589 if(i + 1 == n) /* remainder */ |
|
1590 nh = (way + wah) - ny - 2 * c->border; |
|
1591 else |
|
1592 nh = th - 2 * c->border; |
|
1593 } |
|
1594 resize(c, nx, ny, nw, nh, RESIZEHINTS); |
|
1595 if(n > 1 && th != wah) |
|
1596 ny += nh + 2 * c->border; |
|
1597 } |
|
1598 } |
|
1599 |
|
1600 static void |
|
1601 togglebar(const char *arg) { |
|
1602 if(bpos == BarOff) |
|
1603 bpos = (BARPOS == BarOff) ? BarTop : BARPOS; |
|
1604 else |
|
1605 bpos = BarOff; |
|
1606 updatebarpos(); |
|
1607 arrange(); |
|
1608 } |
|
1609 |
|
1610 static void |
|
1611 togglefloating(const char *arg) { |
|
1612 if(!sel) |
|
1613 return; |
|
1614 sel->isfloating = !sel->isfloating; |
|
1615 if(sel->isfloating) |
|
1616 resize(sel, sel->x, sel->y, sel->w, sel->h, True); |
|
1617 arrange(); |
|
1618 } |
|
1619 |
|
1620 static void |
|
1621 togglemax(const char *arg) { |
|
1622 XEvent ev; |
|
1623 |
|
1624 if(!sel || (!isarrange(floating) && !sel->isfloating) || sel->isfixed) |
|
1625 return; |
|
1626 if((sel->ismax = !sel->ismax)) { |
|
1627 sel->rx = sel->x; |
|
1628 sel->ry = sel->y; |
|
1629 sel->rw = sel->w; |
|
1630 sel->rh = sel->h; |
|
1631 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); |
|
1632 } |
|
1633 else |
|
1634 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); |
|
1635 drawbar(); |
|
1636 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1637 } |
|
1638 |
|
1639 static void |
|
1640 toggletag(const char *arg) { |
|
1641 unsigned int i, j; |
|
1642 |
|
1643 if(!sel) |
|
1644 return; |
|
1645 i = idxoftag(arg); |
|
1646 sel->tags[i] = !sel->tags[i]; |
|
1647 for(j = 0; j < ntags && !sel->tags[j]; j++); |
|
1648 if(j == ntags) |
|
1649 sel->tags[i] = True; |
|
1650 arrange(); |
|
1651 } |
|
1652 |
|
1653 static void |
|
1654 toggleview(const char *arg) { |
|
1655 unsigned int i, j; |
|
1656 |
|
1657 i = idxoftag(arg); |
|
1658 seltags[i] = !seltags[i]; |
|
1659 for(j = 0; j < ntags && !seltags[j]; j++); |
|
1660 if(j == ntags) |
|
1661 seltags[i] = True; /* cannot toggle last view */ |
|
1662 arrange(); |
|
1663 } |
|
1664 |
|
1665 static void |
|
1666 unban(Client *c) { |
|
1667 if(!c->isbanned) |
|
1668 return; |
|
1669 XMoveWindow(dpy, c->win, c->x, c->y); |
|
1670 c->isbanned = False; |
|
1671 } |
|
1672 |
|
1673 static void |
|
1674 unmanage(Client *c) { |
|
1675 XWindowChanges wc; |
|
1676 |
|
1677 wc.border_width = c->oldborder; |
|
1678 /* The server grab construct avoids race conditions. */ |
|
1679 XGrabServer(dpy); |
|
1680 XSetErrorHandler(xerrordummy); |
|
1681 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
|
1682 detach(c); |
|
1683 detachstack(c); |
|
1684 if(sel == c) |
|
1685 focus(NULL); |
|
1686 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
1687 setclientstate(c, WithdrawnState); |
|
1688 free(c->tags); |
|
1689 free(c); |
|
1690 XSync(dpy, False); |
|
1691 XSetErrorHandler(xerror); |
|
1692 XUngrabServer(dpy); |
|
1693 arrange(); |
|
1694 } |
|
1695 |
|
1696 static void |
|
1697 unmapnotify(XEvent *e) { |
|
1698 Client *c; |
|
1699 XUnmapEvent *ev = &e->xunmap; |
|
1700 |
|
1701 if((c = getclient(ev->window))) |
|
1702 unmanage(c); |
|
1703 } |
|
1704 |
|
1705 static void |
|
1706 updatebarpos(void) { |
|
1707 XEvent ev; |
|
1708 |
|
1709 wax = sx; |
|
1710 way = sy; |
|
1711 wah = sh; |
|
1712 waw = sw; |
|
1713 switch(bpos) { |
|
1714 default: |
|
1715 wah -= bh; |
|
1716 way += bh; |
|
1717 XMoveWindow(dpy, barwin, sx, sy); |
|
1718 break; |
|
1719 case BarBot: |
|
1720 wah -= bh; |
|
1721 XMoveWindow(dpy, barwin, sx, sy + wah); |
|
1722 break; |
|
1723 case BarOff: |
|
1724 XMoveWindow(dpy, barwin, sx, sy - bh); |
|
1725 break; |
|
1726 } |
|
1727 XSync(dpy, False); |
|
1728 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1729 } |
|
1730 |
|
1731 static void |
|
1732 updatesizehints(Client *c) { |
|
1733 long msize; |
|
1734 XSizeHints size; |
|
1735 |
|
1736 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) |
|
1737 size.flags = PSize; |
|
1738 c->flags = size.flags; |
|
1739 if(c->flags & PBaseSize) { |
|
1740 c->basew = size.base_width; |
|
1741 c->baseh = size.base_height; |
|
1742 } |
|
1743 else if(c->flags & PMinSize) { |
|
1744 c->basew = size.min_width; |
|
1745 c->baseh = size.min_height; |
|
1746 } |
|
1747 else |
|
1748 c->basew = c->baseh = 0; |
|
1749 if(c->flags & PResizeInc) { |
|
1750 c->incw = size.width_inc; |
|
1751 c->inch = size.height_inc; |
|
1752 } |
|
1753 else |
|
1754 c->incw = c->inch = 0; |
|
1755 if(c->flags & PMaxSize) { |
|
1756 c->maxw = size.max_width; |
|
1757 c->maxh = size.max_height; |
|
1758 } |
|
1759 else |
|
1760 c->maxw = c->maxh = 0; |
|
1761 if(c->flags & PMinSize) { |
|
1762 c->minw = size.min_width; |
|
1763 c->minh = size.min_height; |
|
1764 } |
|
1765 else if(c->flags & PBaseSize) { |
|
1766 c->minw = size.base_width; |
|
1767 c->minh = size.base_height; |
|
1768 } |
|
1769 else |
|
1770 c->minw = c->minh = 0; |
|
1771 if(c->flags & PAspect) { |
|
1772 c->minax = size.min_aspect.x; |
|
1773 c->maxax = size.max_aspect.x; |
|
1774 c->minay = size.min_aspect.y; |
|
1775 c->maxay = size.max_aspect.y; |
|
1776 } |
|
1777 else |
|
1778 c->minax = c->maxax = c->minay = c->maxay = 0; |
|
1779 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh |
|
1780 && c->maxw == c->minw && c->maxh == c->minh); |
|
1781 } |
|
1782 |
|
1783 static void |
|
1784 updatetitle(Client *c) { |
|
1785 if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
|
1786 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); |
|
1787 } |
|
1788 |
|
1789 /* There's no way to check accesses to destroyed windows, thus those cases are |
|
1790 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
|
1791 * default error handler, which may call exit. */ |
|
1792 static int |
|
1793 xerror(Display *dpy, XErrorEvent *ee) { |
|
1794 if(ee->error_code == BadWindow |
|
1795 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
|
1796 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
|
1797 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) |
|
1798 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) |
|
1799 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) |
|
1800 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) |
|
1801 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) |
|
1802 return 0; |
|
1803 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
|
1804 ee->request_code, ee->error_code); |
|
1805 return xerrorxlib(dpy, ee); /* may call exit */ |
|
1806 } |
|
1807 |
|
1808 static int |
|
1809 xerrordummy(Display *dsply, XErrorEvent *ee) { |
|
1810 return 0; |
|
1811 } |
|
1812 |
|
1813 /* Startup Error handler to check if another window manager |
|
1814 * is already running. */ |
|
1815 static int |
|
1816 xerrorstart(Display *dsply, XErrorEvent *ee) { |
|
1817 otherwm = True; |
|
1818 return -1; |
|
1819 } |
|
1820 |
|
1821 static void |
|
1822 view(const char *arg) { |
|
1823 unsigned int i; |
|
1824 |
|
1825 for(i = 0; i < ntags; i++) |
|
1826 seltags[i] = arg == NULL; |
|
1827 i = idxoftag(arg); |
|
1828 if(i >= 0 && i < ntags) |
|
1829 seltags[i] = True; |
|
1830 arrange(); |
|
1831 } |
|
1832 |
|
1833 static void |
|
1834 zoom(const char *arg) { |
|
1835 Client *c; |
|
1836 |
|
1837 if(!sel || !isarrange(tile) || sel->isfloating) |
|
1838 return; |
|
1839 if((c = sel) == nexttiled(clients)) |
|
1840 if(!(c = nexttiled(c->next))) |
|
1841 return; |
|
1842 detach(c); |
|
1843 attach(c); |
|
1844 focus(c); |
|
1845 arrange(); |
|
1846 } |
|
1847 |
|
1848 int |
|
1849 main(int argc, char *argv[]) { |
|
1850 if(argc == 2 && !strcmp("-v", argv[1])) |
|
1851 eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n"); |
|
1852 else if(argc != 1) |
|
1853 eprint("usage: dwm [-v]\n"); |
|
1854 |
|
1855 setlocale(LC_CTYPE, ""); |
|
1856 if(!(dpy = XOpenDisplay(0))) |
|
1857 eprint("dwm: cannot open display\n"); |
|
1858 screen = DefaultScreen(dpy); |
|
1859 root = RootWindow(dpy, screen); |
|
1860 |
|
1861 checkotherwm(); |
|
1862 setup(); |
|
1863 drawbar(); |
|
1864 scan(); |
|
1865 run(); |
1871 cleanup(); |
1866 cleanup(); |
|
1867 |
1872 XCloseDisplay(dpy); |
1868 XCloseDisplay(dpy); |
1873 return 0; |
1869 return 0; |
1874 } |
1870 } |