1 /* See LICENSE file for copyright and license details. */ |
|
2 #include "dwm.h" |
|
3 #include <errno.h> |
|
4 #include <locale.h> |
|
5 #include <stdio.h> |
|
6 #include <stdlib.h> |
|
7 #include <string.h> |
|
8 #include <unistd.h> |
|
9 #include <sys/select.h> |
|
10 #include <X11/cursorfont.h> |
|
11 #include <X11/keysym.h> |
|
12 #include <X11/Xatom.h> |
|
13 #include <X11/Xproto.h> |
|
14 #include <X11/Xutil.h> |
|
15 |
|
16 /* extern */ |
|
17 |
|
18 char stext[256]; |
|
19 int screen, sx, sy, sw, sh, wax, way, waw, wah; |
|
20 unsigned int ntags; |
|
21 unsigned int numlockmask = 0; |
|
22 Atom wmatom[WMLast], netatom[NetLast]; |
|
23 Bool *seltags; |
|
24 Bool selscreen = True; |
|
25 Client *clients = NULL; |
|
26 Client *sel = NULL; |
|
27 Client *stack = NULL; |
|
28 Cursor cursor[CurLast]; |
|
29 Display *dpy; |
|
30 Window root; |
|
31 |
|
32 /* static */ |
|
33 |
|
34 static int (*xerrorxlib)(Display *, XErrorEvent *); |
|
35 static Bool otherwm, readin; |
|
36 static Bool running = True; |
|
37 |
|
38 static void |
|
39 cleanup(void) { |
|
40 close(STDIN_FILENO); |
|
41 while(stack) { |
|
42 unban(stack); |
|
43 unmanage(stack); |
|
44 } |
|
45 if(dc.font.set) |
|
46 XFreeFontSet(dpy, dc.font.set); |
|
47 else |
|
48 XFreeFont(dpy, dc.font.xfont); |
|
49 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
50 XFreePixmap(dpy, dc.drawable); |
|
51 XFreeGC(dpy, dc.gc); |
|
52 XDestroyWindow(dpy, barwin); |
|
53 XFreeCursor(dpy, cursor[CurNormal]); |
|
54 XFreeCursor(dpy, cursor[CurResize]); |
|
55 XFreeCursor(dpy, cursor[CurMove]); |
|
56 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
|
57 XSync(dpy, False); |
|
58 free(seltags); |
|
59 } |
|
60 |
|
61 static long |
|
62 getstate(Window w) { |
|
63 int format, status; |
|
64 long result = -1; |
|
65 unsigned char *p = NULL; |
|
66 unsigned long n, extra; |
|
67 Atom real; |
|
68 |
|
69 status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], |
|
70 &real, &format, &n, &extra, (unsigned char **)&p); |
|
71 if(status != Success) |
|
72 return -1; |
|
73 if(n != 0) |
|
74 result = *p; |
|
75 XFree(p); |
|
76 return result; |
|
77 } |
|
78 |
|
79 static void |
|
80 scan(void) { |
|
81 unsigned int i, num; |
|
82 Window *wins, d1, d2; |
|
83 XWindowAttributes wa; |
|
84 |
|
85 wins = NULL; |
|
86 if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { |
|
87 for(i = 0; i < num; i++) { |
|
88 if(!XGetWindowAttributes(dpy, wins[i], &wa) |
|
89 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) |
|
90 continue; |
|
91 if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState) |
|
92 manage(wins[i], &wa); |
|
93 } |
|
94 for(i = 0; i < num; i++) { /* now the transients */ |
|
95 if(!XGetWindowAttributes(dpy, wins[i], &wa)) |
|
96 continue; |
|
97 if(XGetTransientForHint(dpy, wins[i], &d1) |
|
98 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) |
|
99 manage(wins[i], &wa); |
|
100 } |
|
101 } |
|
102 if(wins) |
|
103 XFree(wins); |
|
104 } |
|
105 |
|
106 static void |
|
107 setup(void) { |
|
108 int i, j; |
|
109 unsigned int mask; |
|
110 Window w; |
|
111 XModifierKeymap *modmap; |
|
112 XSetWindowAttributes wa; |
|
113 |
|
114 /* init atoms */ |
|
115 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); |
|
116 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|
117 wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False); |
|
118 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); |
|
119 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); |
|
120 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); |
|
121 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, |
|
122 PropModeReplace, (unsigned char *) netatom, NetLast); |
|
123 /* init cursors */ |
|
124 cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); |
|
125 cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); |
|
126 cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); |
|
127 /* init modifier map */ |
|
128 modmap = XGetModifierMapping(dpy); |
|
129 for (i = 0; i < 8; i++) |
|
130 for (j = 0; j < modmap->max_keypermod; j++) { |
|
131 if(modmap->modifiermap[i * modmap->max_keypermod + j] |
|
132 == XKeysymToKeycode(dpy, XK_Num_Lock)) |
|
133 numlockmask = (1 << i); |
|
134 } |
|
135 XFreeModifiermap(modmap); |
|
136 /* select for events */ |
|
137 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
|
138 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask; |
|
139 wa.cursor = cursor[CurNormal]; |
|
140 XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); |
|
141 XSelectInput(dpy, root, wa.event_mask); |
|
142 grabkeys(); |
|
143 compileregs(); |
|
144 for(ntags = 0; tags[ntags]; ntags++); |
|
145 seltags = emallocz(sizeof(Bool) * ntags); |
|
146 seltags[0] = True; |
|
147 /* geometry */ |
|
148 sx = sy = 0; |
|
149 sw = DisplayWidth(dpy, screen); |
|
150 sh = DisplayHeight(dpy, screen); |
|
151 initstyle(); |
|
152 initlayouts(); |
|
153 initbar(); |
|
154 /* multihead support */ |
|
155 selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); |
|
156 } |
|
157 |
|
158 /* |
|
159 * Startup Error handler to check if another window manager |
|
160 * is already running. |
|
161 */ |
|
162 static int |
|
163 xerrorstart(Display *dsply, XErrorEvent *ee) { |
|
164 otherwm = True; |
|
165 return -1; |
|
166 } |
|
167 |
|
168 /* extern */ |
|
169 |
|
170 Bool |
|
171 gettextprop(Window w, Atom atom, char *text, unsigned int size) { |
|
172 char **list = NULL; |
|
173 int n; |
|
174 XTextProperty name; |
|
175 |
|
176 if(!text || size == 0) |
|
177 return False; |
|
178 text[0] = '\0'; |
|
179 XGetTextProperty(dpy, w, &name, atom); |
|
180 if(!name.nitems) |
|
181 return False; |
|
182 if(name.encoding == XA_STRING) |
|
183 strncpy(text, (char *)name.value, size - 1); |
|
184 else { |
|
185 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success |
|
186 && n > 0 && *list) |
|
187 { |
|
188 strncpy(text, *list, size - 1); |
|
189 XFreeStringList(list); |
|
190 } |
|
191 } |
|
192 text[size - 1] = '\0'; |
|
193 XFree(name.value); |
|
194 return True; |
|
195 } |
|
196 |
|
197 void |
|
198 quit(const char *arg) { |
|
199 readin = running = False; |
|
200 } |
|
201 |
|
202 /* There's no way to check accesses to destroyed windows, thus those cases are |
|
203 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
|
204 * default error handler, which may call exit. |
|
205 */ |
|
206 int |
|
207 xerror(Display *dpy, XErrorEvent *ee) { |
|
208 if(ee->error_code == BadWindow |
|
209 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
|
210 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
|
211 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) |
|
212 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) |
|
213 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) |
|
214 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) |
|
215 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) |
|
216 return 0; |
|
217 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
|
218 ee->request_code, ee->error_code); |
|
219 return xerrorxlib(dpy, ee); /* may call exit */ |
|
220 } |
|
221 |
|
222 int |
|
223 main(int argc, char *argv[]) { |
|
224 char *p; |
|
225 int r, xfd; |
|
226 fd_set rd; |
|
227 XEvent ev; |
|
228 |
|
229 if(argc == 2 && !strcmp("-v", argv[1])) |
|
230 eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n"); |
|
231 else if(argc != 1) |
|
232 eprint("usage: dwm [-v]\n"); |
|
233 setlocale(LC_CTYPE, ""); |
|
234 if(!(dpy = XOpenDisplay(0))) |
|
235 eprint("dwm: cannot open display\n"); |
|
236 xfd = ConnectionNumber(dpy); |
|
237 screen = DefaultScreen(dpy); |
|
238 root = RootWindow(dpy, screen); |
|
239 otherwm = False; |
|
240 XSetErrorHandler(xerrorstart); |
|
241 /* this causes an error if some other window manager is running */ |
|
242 XSelectInput(dpy, root, SubstructureRedirectMask); |
|
243 XSync(dpy, False); |
|
244 if(otherwm) |
|
245 eprint("dwm: another window manager is already running\n"); |
|
246 |
|
247 XSync(dpy, False); |
|
248 XSetErrorHandler(NULL); |
|
249 xerrorxlib = XSetErrorHandler(xerror); |
|
250 XSync(dpy, False); |
|
251 setup(); |
|
252 drawbar(); |
|
253 scan(); |
|
254 |
|
255 /* main event loop, also reads status text from stdin */ |
|
256 XSync(dpy, False); |
|
257 readin = True; |
|
258 while(running) { |
|
259 FD_ZERO(&rd); |
|
260 if(readin) |
|
261 FD_SET(STDIN_FILENO, &rd); |
|
262 FD_SET(xfd, &rd); |
|
263 if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { |
|
264 if(errno == EINTR) |
|
265 continue; |
|
266 eprint("select failed\n"); |
|
267 } |
|
268 if(FD_ISSET(STDIN_FILENO, &rd)) { |
|
269 switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { |
|
270 case -1: |
|
271 strncpy(stext, strerror(errno), sizeof stext - 1); |
|
272 stext[sizeof stext - 1] = '\0'; |
|
273 readin = False; |
|
274 break; |
|
275 case 0: |
|
276 strncpy(stext, "EOF", 4); |
|
277 readin = False; |
|
278 break; |
|
279 default: |
|
280 for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); |
|
281 for(; p >= stext && *p != '\n'; --p); |
|
282 if(p > stext) |
|
283 strncpy(stext, p + 1, sizeof stext); |
|
284 } |
|
285 drawbar(); |
|
286 } |
|
287 while(XPending(dpy)) { |
|
288 XNextEvent(dpy, &ev); |
|
289 if(handler[ev.type]) |
|
290 (handler[ev.type])(&ev); /* call handler */ |
|
291 } |
|
292 } |
|
293 cleanup(); |
|
294 XCloseDisplay(dpy); |
|
295 return 0; |
|
296 } |
|