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