wm.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
  3. * See LICENSE file for license details.
  4. */
  5. #include <errno.h>
  6. #include <stdarg.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <sys/types.h>
  10. #include <sys/time.h>
  11. #include <X11/cursorfont.h>
  12. #include <X11/Xatom.h>
  13. #include <X11/Xproto.h>
  14. #include "wm.h"
  15. /********** CUSTOMIZE **********/
  16. char *tags[TLast] = {
  17. [Tscratch] = "scratch",
  18. [Tdev] = "dev",
  19. [Tirc] = "irc",
  20. [Twww] = "www",
  21. [Twork] = "work",
  22. };
  23. /* commands */
  24. static char *cmdwallpaper[] = {
  25. "feh", "--bg-scale", "/home/garbeam/wallpaper/bg.jpg", NULL
  26. };
  27. static char *cmdstatus[] = {
  28. "sh", "-c", "echo -n `date '+%Y-%m-%d %H:%M'`"
  29. " `uptime | sed 's/.*://; s/,//g'`"
  30. " `acpi | awk '{print $4}' | sed 's/,//'`", NULL
  31. };
  32. /********** CUSTOMIZE **********/
  33. /* X structs */
  34. Display *dpy;
  35. Window root, barwin;
  36. Atom wm_atom[WMLast], net_atom[NetLast];
  37. Cursor cursor[CurLast];
  38. Bool running = True;
  39. Bool issel;
  40. char stext[1024];
  41. int tsel = Tdev; /* default tag */
  42. int screen, sx, sy, sw, sh, bx, by, bw, bh;
  43. Brush brush = {0};
  44. Client *clients = NULL;
  45. Client *stack = NULL;
  46. static Bool other_wm_running;
  47. static const char version[] =
  48. "gridwm - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n";
  49. static int (*x_error_handler) (Display *, XErrorEvent *);
  50. static void
  51. usage() { error("usage: gridwm [-v]\n"); }
  52. static void
  53. scan_wins()
  54. {
  55. unsigned int i, num;
  56. Window *wins;
  57. XWindowAttributes wa;
  58. Window d1, d2;
  59. if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
  60. for(i = 0; i < num; i++) {
  61. if(!XGetWindowAttributes(dpy, wins[i], &wa))
  62. continue;
  63. if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
  64. continue;
  65. if(wa.map_state == IsViewable)
  66. manage(wins[i], &wa);
  67. }
  68. }
  69. if(wins)
  70. XFree(wins);
  71. }
  72. static int
  73. win_property(Window w, Atom a, Atom t, long l, unsigned char **prop)
  74. {
  75. Atom real;
  76. int format;
  77. unsigned long res, extra;
  78. int status;
  79. status = XGetWindowProperty(dpy, w, a, 0L, l, False, t, &real, &format,
  80. &res, &extra, prop);
  81. if(status != Success || *prop == 0) {
  82. return 0;
  83. }
  84. if(res == 0) {
  85. free((void *) *prop);
  86. }
  87. return res;
  88. }
  89. int
  90. win_proto(Window w)
  91. {
  92. unsigned char *protocols;
  93. long res;
  94. int protos = 0;
  95. int i;
  96. res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L, &protocols);
  97. if(res <= 0) {
  98. return protos;
  99. }
  100. for(i = 0; i < res; i++) {
  101. if(protocols[i] == wm_atom[WMDelete])
  102. protos |= WM_PROTOCOL_DELWIN;
  103. }
  104. free((char *) protocols);
  105. return protos;
  106. }
  107. void
  108. send_message(Window w, Atom a, long value)
  109. {
  110. XEvent e;
  111. e.type = ClientMessage;
  112. e.xclient.window = w;
  113. e.xclient.message_type = a;
  114. e.xclient.format = 32;
  115. e.xclient.data.l[0] = value;
  116. e.xclient.data.l[1] = CurrentTime;
  117. XSendEvent(dpy, w, False, NoEventMask, &e);
  118. XFlush(dpy);
  119. }
  120. /*
  121. * There's no way to check accesses to destroyed windows, thus
  122. * those cases are ignored (especially on UnmapNotify's).
  123. * Other types of errors call Xlib's default error handler, which
  124. * calls exit().
  125. */
  126. int
  127. error_handler(Display *dpy, XErrorEvent *error)
  128. {
  129. if(error->error_code == BadWindow
  130. || (error->request_code == X_SetInputFocus
  131. && error->error_code == BadMatch)
  132. || (error->request_code == X_PolyText8
  133. && error->error_code == BadDrawable)
  134. || (error->request_code == X_PolyFillRectangle
  135. && error->error_code == BadDrawable)
  136. || (error->request_code == X_PolySegment
  137. && error->error_code == BadDrawable)
  138. || (error->request_code == X_ConfigureWindow
  139. && error->error_code == BadMatch)
  140. || (error->request_code == X_GrabKey
  141. && error->error_code == BadAccess))
  142. return 0;
  143. fprintf(stderr, "gridwm: fatal error: request code=%d, error code=%d\n",
  144. error->request_code, error->error_code);
  145. return x_error_handler(dpy, error); /* may call exit() */
  146. }
  147. /*
  148. * Startup Error handler to check if another window manager
  149. * is already running.
  150. */
  151. static int
  152. startup_error_handler(Display *dpy, XErrorEvent *error)
  153. {
  154. other_wm_running = True;
  155. return -1;
  156. }
  157. static void
  158. cleanup()
  159. {
  160. while(clients)
  161. unmanage(clients);
  162. XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
  163. }
  164. void
  165. run(void *aux)
  166. {
  167. spawn(dpy, aux);
  168. }
  169. void
  170. quit(void *aux)
  171. {
  172. running = False;
  173. }
  174. int
  175. main(int argc, char *argv[])
  176. {
  177. int i;
  178. XSetWindowAttributes wa;
  179. unsigned int mask;
  180. Window w;
  181. XEvent ev;
  182. fd_set fds;
  183. struct timeval t, timeout = {
  184. .tv_usec = 0,
  185. .tv_sec = STATUSDELAY,
  186. };
  187. /* command line args */
  188. for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
  189. switch (argv[i][1]) {
  190. case 'v':
  191. fprintf(stdout, "%s", version);
  192. exit(0);
  193. break;
  194. default:
  195. usage();
  196. break;
  197. }
  198. }
  199. dpy = XOpenDisplay(0);
  200. if(!dpy)
  201. error("gridwm: cannot connect X server\n");
  202. screen = DefaultScreen(dpy);
  203. root = RootWindow(dpy, screen);
  204. /* check if another WM is already running */
  205. other_wm_running = False;
  206. XSetErrorHandler(startup_error_handler);
  207. /* this causes an error if some other WM is running */
  208. XSelectInput(dpy, root, SubstructureRedirectMask);
  209. XFlush(dpy);
  210. if(other_wm_running)
  211. error("gridwm: another window manager is already running\n");
  212. spawn(dpy, cmdwallpaper);
  213. sx = sy = 0;
  214. sw = DisplayWidth(dpy, screen);
  215. sh = DisplayHeight(dpy, screen);
  216. issel = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
  217. XSetErrorHandler(0);
  218. x_error_handler = XSetErrorHandler(error_handler);
  219. /* init atoms */
  220. wm_atom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
  221. wm_atom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  222. net_atom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
  223. net_atom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
  224. XChangeProperty(dpy, root, net_atom[NetSupported], XA_ATOM, 32,
  225. PropModeReplace, (unsigned char *) net_atom, NetLast);
  226. /* init cursors */
  227. cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
  228. cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
  229. cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
  230. update_keys();
  231. /* style */
  232. loadcolors(dpy, screen, &brush, BGCOLOR, FGCOLOR, BORDERCOLOR);
  233. loadfont(dpy, &brush.font, FONT);
  234. wa.override_redirect = 1;
  235. wa.background_pixmap = ParentRelative;
  236. wa.event_mask = ExposureMask;
  237. bx = by = 0;
  238. bw = sw;
  239. bh = texth(&brush.font);
  240. barwin = XCreateWindow(dpy, root, bx, by, bw, bh, 0, DefaultDepth(dpy, screen),
  241. CopyFromParent, DefaultVisual(dpy, screen),
  242. CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
  243. XDefineCursor(dpy, barwin, cursor[CurNormal]);
  244. XMapRaised(dpy, barwin);
  245. brush.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
  246. brush.gc = XCreateGC(dpy, root, 0, 0);
  247. pipe_spawn(stext, sizeof(stext), dpy, cmdstatus);
  248. draw_bar();
  249. wa.event_mask = SubstructureRedirectMask | EnterWindowMask \
  250. | LeaveWindowMask;
  251. wa.cursor = cursor[CurNormal];
  252. XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
  253. arrange = grid;
  254. scan_wins();
  255. while(running) {
  256. if(XPending(dpy) > 0) {
  257. XNextEvent(dpy, &ev);
  258. if(handler[ev.type])
  259. (handler[ev.type]) (&ev); /* call handler */
  260. continue;
  261. }
  262. FD_ZERO(&fds);
  263. FD_SET(ConnectionNumber(dpy), &fds);
  264. t = timeout;
  265. if(select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &t) > 0)
  266. continue;
  267. else if(errno != EINTR) {
  268. pipe_spawn(stext, sizeof(stext), dpy, cmdstatus);
  269. draw_bar();
  270. }
  271. }
  272. cleanup();
  273. XCloseDisplay(dpy);
  274. return 0;
  275. }