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