chance license to GPLv2
[awesome.git] / awesome.c
blobb096349abb4b035459ee2680c226267ca807d24f
1 /*
2 * awesome.c - awesome main functions
3 *
4 * Copyright © 2007 Julien Danjou <julien@danjou.info>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
22 #include <errno.h>
23 #include <locale.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <sys/select.h>
27 #include <X11/cursorfont.h>
28 #include <X11/keysym.h>
29 #include <X11/Xatom.h>
30 #include <X11/Xproto.h>
31 #include <X11/Xutil.h>
33 #include "awesome.h"
34 #include "event.h"
35 #include "layout.h"
36 #include "tag.h"
38 int wax, way, waw, wah;
39 Client *clients = NULL;
40 Client *sel = NULL;
41 Client *stack = NULL;
42 Cursor cursor[CurLast];
43 DC dc;
45 /* static */
47 static int (*xerrorxlib) (Display *, XErrorEvent *);
48 static Bool otherwm = False, readin = True;
49 static Bool running = True;
52 Bool
53 gettextprop(Display *disp, Window w, Atom atom, char *text, unsigned int size)
55 char **list = NULL;
56 int n;
58 XTextProperty name;
60 if(!text || size == 0)
61 return False;
63 text[0] = '\0';
64 XGetTextProperty(disp, w, &name, atom);
66 if(!name.nitems)
67 return False;
69 if(name.encoding == XA_STRING)
70 strncpy(text, (char *) name.value, size - 1);
71 else if(XmbTextPropertyToTextList(disp, &name, &list, &n) >= Success && n > 0 && *list)
73 strncpy(text, *list, size - 1);
74 XFreeStringList(list);
77 text[size - 1] = '\0';
78 XFree(name.value);
80 return True;
83 static void
84 cleanup(Display *disp, awesome_config *awesomeconf)
86 close(STDIN_FILENO);
87 while(stack)
89 unban(stack);
90 unmanage(stack, &dc, NormalState, awesomeconf);
92 if(dc.font.set)
93 XFreeFontSet(disp, dc.font.set);
94 else
95 XFreeFont(disp, dc.font.xfont);
96 XUngrabKey(disp, AnyKey, AnyModifier, DefaultRootWindow(disp));
97 XFreePixmap(disp, dc.drawable);
98 XFreeGC(disp, dc.gc);
99 XDestroyWindow(disp, awesomeconf->statusbar.window);
100 XFreeCursor(disp, cursor[CurNormal]);
101 XFreeCursor(disp, cursor[CurResize]);
102 XFreeCursor(disp, cursor[CurMove]);
103 XSetInputFocus(disp, PointerRoot, RevertToPointerRoot, CurrentTime);
104 XSync(disp, False);
107 static long
108 getstate(Display *disp, Window w)
110 int format, status;
111 long result = -1;
112 unsigned char *p = NULL;
113 unsigned long n, extra;
114 Atom real;
115 status = XGetWindowProperty(disp, w, XInternAtom(disp, "WM_STATE", False),
116 0L, 2L, False, XInternAtom(disp, "WM_STATE", False),
117 &real, &format, &n, &extra, (unsigned char **) &p);
118 if(status != Success)
119 return -1;
120 if(n != 0)
121 result = *p;
122 XFree(p);
123 return result;
126 static void
127 scan(Display *disp, awesome_config *awesomeconf)
129 unsigned int i, num;
130 Window *wins, d1, d2;
131 XWindowAttributes wa;
133 wins = NULL;
134 if(XQueryTree(disp, DefaultRootWindow(disp), &d1, &d2, &wins, &num))
135 for(i = 0; i < num; i++)
137 if(!XGetWindowAttributes(disp, wins[i], &wa)
138 || wa.override_redirect || XGetTransientForHint(disp, wins[i], &d1))
139 continue;
140 if(wa.map_state == IsViewable || getstate(disp, wins[i]) == IconicState)
141 manage(disp, &dc, wins[i], &wa, awesomeconf);
143 /* now the transients */
144 for(i = 0; i < num; i++)
146 if(!XGetWindowAttributes(disp, wins[i], &wa))
147 continue;
148 if(XGetTransientForHint(disp, wins[i], &d1)
149 && (wa.map_state == IsViewable || getstate(disp, wins[i]) == IconicState))
150 manage(disp, &dc, wins[i], &wa, awesomeconf);
152 if(wins)
153 XFree(wins);
158 enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */
159 Atom netatom[NetWMName];
160 /** Setup everything before running
161 * \param disp Display ref
162 * \param awesomeconf awesome config ref
163 * \todo clean things...
165 static void
166 setup(Display *disp, awesome_config *awesomeconf)
168 XSetWindowAttributes wa;
170 netatom[NetSupported] = XInternAtom(disp, "_NET_SUPPORTED", False);
171 netatom[NetWMName] = XInternAtom(disp, "_NET_WM_NAME", False);
172 XChangeProperty(disp, DefaultRootWindow(disp), netatom[NetSupported],
173 XA_ATOM, 32, PropModeReplace, (unsigned char *) netatom, NetLast);
174 /* init cursors */
175 cursor[CurNormal] = XCreateFontCursor(disp, XC_left_ptr);
176 cursor[CurResize] = XCreateFontCursor(disp, XC_sizing);
177 cursor[CurMove] = XCreateFontCursor(disp, XC_fleur);
178 /* select for events */
179 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
180 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
181 wa.cursor = cursor[CurNormal];
182 XChangeWindowAttributes(disp, DefaultRootWindow(disp), CWEventMask | CWCursor, &wa);
183 XSelectInput(disp, DefaultRootWindow(disp), wa.event_mask);
184 grabkeys(disp, awesomeconf);
185 compileregs(awesomeconf->rules, awesomeconf->nrules);
186 /* bar */
187 dc.h = awesomeconf->statusbar.height = dc.font.height + 2;
188 wa.override_redirect = 1;
189 wa.background_pixmap = ParentRelative;
190 wa.event_mask = ButtonPressMask | ExposureMask;
191 awesomeconf->statusbar.window = XCreateWindow(disp, DefaultRootWindow(disp), 0, 0, DisplayWidth(disp, DefaultScreen(disp)), awesomeconf->statusbar.height, 0,
192 DefaultDepth(disp, DefaultScreen(disp)), CopyFromParent,
193 DefaultVisual(disp, DefaultScreen(disp)),
194 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
195 XDefineCursor(disp, awesomeconf->statusbar.window, cursor[CurNormal]);
196 updatebarpos(disp, awesomeconf->statusbar);
197 XMapRaised(disp, awesomeconf->statusbar.window);
198 /* pixmap for everything */
199 dc.drawable = XCreatePixmap(disp, DefaultRootWindow(disp), DisplayWidth(disp, DefaultScreen(disp)), awesomeconf->statusbar.height, DefaultDepth(disp, DefaultScreen(disp)));
200 dc.gc = XCreateGC(disp, DefaultRootWindow(disp), 0, 0);
201 XSetLineAttributes(disp, dc.gc, 1, LineSolid, CapButt, JoinMiter);
202 if(!dc.font.set)
203 XSetFont(disp, dc.gc, dc.font.xfont->fid);
204 loadawesomeprops(disp, awesomeconf);
208 * Startup Error handler to check if another window manager
209 * is already running.
211 static int
212 xerrorstart(Display * dsply __attribute__ ((unused)), XErrorEvent * ee __attribute__ ((unused)))
214 otherwm = True;
215 return -1;
218 /* extern */
220 void
221 uicb_quit(Display *disp __attribute__ ((unused)),
222 awesome_config *awesomeconf __attribute__((unused)),
223 const char *arg __attribute__ ((unused)))
225 readin = running = False;
228 void
229 updatebarpos(Display *disp, Statusbar statusbar)
231 XEvent ev;
233 wax = 0;
234 way = 0;
235 wah = DisplayHeight(disp, DefaultScreen(disp));
236 waw = DisplayWidth(disp, DefaultScreen(disp));
237 switch (statusbar.position)
239 default:
240 wah -= statusbar.height;
241 way += statusbar.height;
242 XMoveWindow(disp, statusbar.window, 0, 0);
243 break;
244 case BarBot:
245 wah -= statusbar.height;
246 XMoveWindow(disp, statusbar.window, 0, wah);
247 break;
248 case BarOff:
249 XMoveWindow(disp, statusbar.window, 0, 0 - statusbar.height);
250 break;
252 XSync(disp, False);
253 while(XCheckMaskEvent(disp, EnterWindowMask, &ev));
256 /* There's no way to check accesses to destroyed windows, thus those cases are
257 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
258 * default error handler, which may call exit.
261 xerror(Display * edpy, XErrorEvent * ee)
263 if(ee->error_code == BadWindow
264 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
265 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
266 || (ee->request_code == X_PolyFillRectangle
267 && ee->error_code == BadDrawable)
268 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
269 || (ee->request_code == X_ConfigureWindow
270 && ee->error_code == BadMatch) || (ee->request_code == X_GrabKey
271 && ee->error_code == BadAccess)
272 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
273 return 0;
274 fprintf(stderr, "awesome: fatal error: request code=%d, error code=%d\n",
275 ee->request_code, ee->error_code);
277 return xerrorxlib(edpy, ee); /* may call exit */
281 main(int argc, char *argv[])
283 char *p;
284 int r, xfd;
285 fd_set rd;
286 XEvent ev;
287 Display * dpy;
288 Window root;
289 awesome_config awesomeconf;
291 if(argc == 2 && !strcmp("-v", argv[1]))
292 eprint("awesome-" VERSION " © 2007 Julien Danjou\n");
293 else if(argc != 1)
294 eprint("usage: awesome [-v]\n");
295 setlocale(LC_CTYPE, "");
298 if(!(dpy = XOpenDisplay(NULL)))
299 eprint("awesome: cannot open display\n");
300 xfd = ConnectionNumber(dpy);
301 root = RootWindow(dpy, DefaultScreen(dpy));
302 XSetErrorHandler(xerrorstart);
304 /* this causes an error if some other window manager is running */
305 XSelectInput(dpy, root, SubstructureRedirectMask);
306 XSync(dpy, False);
308 if(otherwm)
309 eprint("awesome: another window manager is already running\n");
311 XSync(dpy, False);
312 XSetErrorHandler(NULL);
313 xerrorxlib = XSetErrorHandler(xerror);
314 XSync(dpy, False);
315 parse_config(dpy, DefaultScreen(dpy), &dc, &awesomeconf);
316 setup(dpy, &awesomeconf);
317 drawstatus(dpy, &awesomeconf);
318 scan(dpy, &awesomeconf);
319 XSync(dpy, False);
321 void (*handler[LASTEvent]) (XEvent *, awesome_config *) =
323 [ButtonPress] = handle_event_buttonpress,
324 [ConfigureRequest] = handle_event_configurerequest,
325 [ConfigureNotify] = handle_event_configurenotify,
326 [DestroyNotify] = handle_event_destroynotify,
327 [EnterNotify] = handle_event_enternotify,
328 [LeaveNotify] = handle_event_leavenotify,
329 [Expose] = handle_event_expose,
330 [KeyPress] = handle_event_keypress,
331 [MappingNotify] = handle_event_mappingnotify,
332 [MapRequest] = handle_event_maprequest,
333 [PropertyNotify] = handle_event_propertynotify,
334 [UnmapNotify] = handle_event_unmapnotify
338 /* main event loop, also reads status text from stdin */
339 while(running)
341 FD_ZERO(&rd);
342 if(readin)
343 FD_SET(STDIN_FILENO, &rd);
344 FD_SET(xfd, &rd);
345 if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1)
347 if(errno == EINTR)
348 continue;
349 eprint("select failed\n");
351 if(FD_ISSET(STDIN_FILENO, &rd))
353 switch (r = read(STDIN_FILENO, awesomeconf.statustext, sizeof(awesomeconf.statustext) - 1))
355 case -1:
356 strncpy(awesomeconf.statustext, strerror(errno), sizeof(awesomeconf.statustext) - 1);
357 awesomeconf.statustext[sizeof(awesomeconf.statustext) - 1] = '\0';
358 readin = False;
359 break;
360 case 0:
361 strncpy(awesomeconf.statustext, "EOF", 4);
362 readin = False;
363 break;
364 default:
365 for(awesomeconf.statustext[r] = '\0', p = awesomeconf.statustext + a_strlen(awesomeconf.statustext) - 1;
366 p >= awesomeconf.statustext && *p == '\n'; *p-- = '\0');
367 for(; p >= awesomeconf.statustext && *p != '\n'; --p);
368 if(p > awesomeconf.statustext)
369 strncpy(awesomeconf.statustext, p + 1, sizeof(awesomeconf.statustext));
371 drawstatus(dpy, &awesomeconf);
374 while(XPending(dpy))
376 XNextEvent(dpy, &ev);
377 if(handler[ev.type])
378 (handler[ev.type]) (&ev, &awesomeconf); /* call handler */
381 cleanup(dpy, &awesomeconf);
382 XCloseDisplay(dpy);
384 return 0;