JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
038e6b9b9971bf2b564708a010459d39e064f3a1
[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 /* X structs */
17 Display *dpy;
18 Window root, barwin;
19 Atom wm_atom[WMLast], net_atom[NetLast];
20 Cursor cursor[CurLast];
21 XRectangle rect, barrect;
22 Bool running = True;
23 Client *client = NULL;
24
25 char *bartext, tag[256];
26 int screen, sel_screen;
27
28 /* draw structs */
29 Brush brush = {0};
30
31 enum { WM_PROTOCOL_DELWIN = 1 };
32
33 static Bool other_wm_running;
34 static int (*x_error_handler) (Display *, XErrorEvent *);
35 static char version[] = "gridwm - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n";
36
37 static void
38 usage()
39 {
40         fputs("usage: gridwm [-v]\n", stderr);
41         exit(1);
42 }
43
44 static void
45 scan_wins()
46 {
47         unsigned int i, num;
48         Window *wins;
49         XWindowAttributes wa;
50         Window d1, d2;
51
52         if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
53                 for(i = 0; i < num; i++) {
54                         if(!XGetWindowAttributes(dpy, wins[i], &wa))
55                                 continue;
56                         if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
57                                 continue;
58                         if(wa.map_state == IsViewable)
59                                 manage(create_client(wins[i], &wa));
60                 }
61         }
62         if(wins)
63                 XFree(wins);
64 }
65
66 static int
67 win_property(Window w, Atom a, Atom t, long l, unsigned char **prop)
68 {
69         Atom real;
70         int format;
71         unsigned long res, extra;
72         int status;
73
74         status = XGetWindowProperty(dpy, w, a, 0L, l, False, t, &real, &format,
75                         &res, &extra, prop);
76
77         if(status != Success || *prop == NULL) {
78                 return 0;
79         }
80         if(res == 0)
81                 free((void *) *prop);
82         return res;
83 }
84
85 int
86 win_proto(Window w)
87 {
88         Atom *protocols;
89         long res;
90         int protos = 0;
91         int i;
92
93         res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L,
94                         ((unsigned char **) &protocols));
95         if(res <= 0) {
96                 return protos;
97         }
98         for(i = 0; i < res; i++) {
99                 if(protocols[i] == wm_atom[WMDelete])
100                         protos |= WM_PROTOCOL_DELWIN;
101         }
102         free((char *) protocols);
103         return protos;
104 }
105
106 /*
107  * There's no way to check accesses to destroyed windows, thus
108  * those cases are ignored (especially on UnmapNotify's).
109  * Other types of errors call Xlib's default error handler, which
110  * calls exit().
111  */
112 static int
113 error_handler(Display *dpy, XErrorEvent *error)
114 {
115         if(error->error_code == BadWindow
116                         || (error->request_code == X_SetInputFocus
117                                 && error->error_code == BadMatch)
118                         || (error->request_code == X_PolyText8
119                                 && error->error_code == BadDrawable)
120                         || (error->request_code == X_PolyFillRectangle
121                                 && error->error_code == BadDrawable)
122                         || (error->request_code == X_PolySegment
123                                 && error->error_code == BadDrawable)
124                         || (error->request_code == X_ConfigureWindow
125                                 && error->error_code == BadMatch)
126                         || (error->request_code == X_GrabKey
127                                 && error->error_code == BadAccess))
128                 return 0;
129         fprintf(stderr, "gridwm: fatal error: request code=%d, error code=%d\n",
130                         error->request_code, error->error_code);
131         return x_error_handler(dpy, error); /* may call exit() */
132 }
133
134 /*
135  * Startup Error handler to check if another window manager
136  * is already running.
137  */
138 static int
139 startup_error_handler(Display *dpy, XErrorEvent *error)
140 {
141         other_wm_running = True;
142         return -1;
143 }
144
145 static void
146 cleanup()
147 {
148         /*
149         Client *c;
150         for(c=client; c; c=c->next)
151                 reparent_client(c, root, c->sel->rect.x, c->sel->rect.y);
152         XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
153         */
154 }
155
156 int
157 main(int argc, char *argv[])
158 {
159         int i;
160         XSetWindowAttributes wa;
161         unsigned int mask;
162         Window w;
163         XEvent ev;
164
165         /* command line args */
166         for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
167                 switch (argv[i][1]) {
168                 case 'v':
169                         fprintf(stdout, "%s", version);
170                         exit(0);
171                         break;
172                 default:
173                         usage();
174                         break;
175                 }
176         }
177
178         dpy = XOpenDisplay(0);
179         if(!dpy)
180                 error("gridwm: cannot connect X server\n");
181
182         screen = DefaultScreen(dpy);
183         root = RootWindow(dpy, screen);
184
185         /* check if another WM is already running */
186         other_wm_running = False;
187         XSetErrorHandler(startup_error_handler);
188         /* this causes an error if some other WM is running */
189         XSelectInput(dpy, root, SubstructureRedirectMask);
190         XFlush(dpy);
191
192         if(other_wm_running)
193                 error("gridwm: another window manager is already running\n");
194
195         rect.x = rect.y = 0;
196         rect.width = DisplayWidth(dpy, screen);
197         rect.height = DisplayHeight(dpy, screen);
198         sel_screen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
199
200         XSetErrorHandler(0);
201         x_error_handler = XSetErrorHandler(error_handler);
202
203         /* init atoms */
204         wm_atom[WMState] = XInternAtom(dpy, "WM_STATE", False);
205         wm_atom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
206         wm_atom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
207         net_atom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
208         net_atom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
209
210         XChangeProperty(dpy, root, net_atom[NetSupported], XA_ATOM, 32,
211                         PropModeReplace, (unsigned char *) net_atom, NetLast);
212
213
214         /* init cursors */
215         cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
216         cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
217         cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
218
219         update_keys();
220
221         brush.drawable = XCreatePixmap(dpy, root, rect.width, rect.height,
222                         DefaultDepth(dpy, screen));
223         brush.gc = XCreateGC(dpy, root, 0, 0);
224
225         /* style */
226         loadcolors(dpy, screen, &brush, BGCOLOR, FGCOLOR, BORDERCOLOR);
227         loadfont(dpy, &brush.font, FONT);
228
229         wa.override_redirect = 1;
230         wa.background_pixmap = ParentRelative;
231         wa.event_mask = ExposureMask;
232
233         barrect = rect;
234         barrect.height = labelheight(&brush.font);
235         barrect.y = rect.height - barrect.height;
236         barwin = XCreateWindow(dpy, root, barrect.x, barrect.y,
237                         barrect.width, barrect.height, 0, DefaultDepth(dpy, screen),
238                         CopyFromParent, DefaultVisual(dpy, screen),
239                         CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
240         bartext = NULL;
241         XDefineCursor(dpy, barwin, cursor[CurNormal]);
242         XMapRaised(dpy, barwin);
243         draw_bar();
244
245         wa.event_mask = SubstructureRedirectMask | EnterWindowMask | LeaveWindowMask;
246         wa.cursor = cursor[CurNormal];
247         XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
248
249         scan_wins();
250
251         while(running) {
252                 XNextEvent(dpy, &ev);
253                 if(handler[ev.type])
254                         (handler[ev.type]) (&ev); /* call handler */
255         }
256
257         cleanup();
258         XCloseDisplay(dpy);
259
260         return 0;
261 }