cosmectic
[awesome.git] / awesome.c
blobd6d4d521ac20cc5c921c9ade886d693421ab8da9
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>
32 #include <X11/extensions/shape.h>
33 #include <X11/extensions/Xrandr.h>
35 #include "awesome.h"
36 #include "event.h"
37 #include "layout.h"
38 #include "tag.h"
39 #include "screen.h"
41 Client *clients = NULL;
42 Client *sel = NULL;
43 Client *stack = NULL;
44 DC dc;
46 /* static */
48 static int (*xerrorxlib) (Display *, XErrorEvent *);
49 static Bool readin = True, running = True;
51 static void
52 cleanup(Display *disp, DC *drawcontext, awesome_config *awesomeconf)
54 close(STDIN_FILENO);
55 while(stack)
57 unban(stack);
58 unmanage(stack, drawcontext, NormalState, awesomeconf);
60 if(drawcontext->font.set)
61 XFreeFontSet(disp, drawcontext->font.set);
62 else
63 XFreeFont(disp, drawcontext->font.xfont);
64 XUngrabKey(disp, AnyKey, AnyModifier, DefaultRootWindow(disp));
65 XFreePixmap(disp, drawcontext->drawable);
66 XFreeGC(disp, drawcontext->gc);
67 XDestroyWindow(disp, awesomeconf->statusbar.window);
68 XFreeCursor(disp, drawcontext->cursor[CurNormal]);
69 XFreeCursor(disp, drawcontext->cursor[CurResize]);
70 XFreeCursor(disp, drawcontext->cursor[CurMove]);
71 XSetInputFocus(disp, PointerRoot, RevertToPointerRoot, CurrentTime);
72 XSync(disp, False);
75 static long
76 getstate(Display *disp, Window w)
78 int format, status;
79 long result = -1;
80 unsigned char *p = NULL;
81 unsigned long n, extra;
82 Atom real;
83 status = XGetWindowProperty(disp, w, XInternAtom(disp, "WM_STATE", False),
84 0L, 2L, False, XInternAtom(disp, "WM_STATE", False),
85 &real, &format, &n, &extra, (unsigned char **) &p);
86 if(status != Success)
87 return -1;
88 if(n != 0)
89 result = *p;
90 XFree(p);
91 return result;
94 static void
95 scan(Display *disp, DC *drawcontext, awesome_config *awesomeconf)
97 unsigned int i, num;
98 Window *wins, d1, d2;
99 XWindowAttributes wa;
101 wins = NULL;
102 if(XQueryTree(disp, DefaultRootWindow(disp), &d1, &d2, &wins, &num))
103 for(i = 0; i < num; i++)
105 if(!XGetWindowAttributes(disp, wins[i], &wa)
106 || wa.override_redirect || XGetTransientForHint(disp, wins[i], &d1))
107 continue;
108 if(wa.map_state == IsViewable || getstate(disp, wins[i]) == IconicState)
109 manage(disp, drawcontext, wins[i], &wa, awesomeconf);
111 /* now the transients */
112 for(i = 0; i < num; i++)
114 if(!XGetWindowAttributes(disp, wins[i], &wa))
115 continue;
116 if(XGetTransientForHint(disp, wins[i], &d1)
117 && (wa.map_state == IsViewable || getstate(disp, wins[i]) == IconicState))
118 manage(disp, drawcontext, wins[i], &wa, awesomeconf);
120 if(wins)
121 XFree(wins);
126 enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */
127 Atom netatom[NetWMName];
128 /** Setup everything before running
129 * \param disp Display ref
130 * \param awesomeconf awesome config ref
131 * \todo clean things...
133 static void
134 setup(Display *disp, DC *drawcontext, awesome_config *awesomeconf)
136 XSetWindowAttributes wa;
138 netatom[NetSupported] = XInternAtom(disp, "_NET_SUPPORTED", False);
139 netatom[NetWMName] = XInternAtom(disp, "_NET_WM_NAME", False);
140 XChangeProperty(disp, DefaultRootWindow(disp), netatom[NetSupported],
141 XA_ATOM, 32, PropModeReplace, (unsigned char *) netatom, NetLast);
142 /* init cursors */
143 drawcontext->cursor[CurNormal] = XCreateFontCursor(disp, XC_left_ptr);
144 drawcontext->cursor[CurResize] = XCreateFontCursor(disp, XC_sizing);
145 drawcontext->cursor[CurMove] = XCreateFontCursor(disp, XC_fleur);
146 /* select for events */
147 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
148 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
149 wa.cursor = drawcontext->cursor[CurNormal];
150 XChangeWindowAttributes(disp, DefaultRootWindow(disp), CWEventMask | CWCursor, &wa);
151 XSelectInput(disp, DefaultRootWindow(disp), wa.event_mask);
152 grabkeys(disp, awesomeconf);
153 compileregs(awesomeconf->rules, awesomeconf->nrules);
154 /* bar */
155 drawcontext->h = awesomeconf->statusbar.height = drawcontext->font.height + 2;
156 wa.override_redirect = 1;
157 wa.background_pixmap = ParentRelative;
158 wa.event_mask = ButtonPressMask | ExposureMask;
159 awesomeconf->statusbar.window = XCreateWindow(disp, DefaultRootWindow(disp), 0, 0, DisplayWidth(disp, DefaultScreen(disp)), awesomeconf->statusbar.height, 0,
160 DefaultDepth(disp, DefaultScreen(disp)), CopyFromParent,
161 DefaultVisual(disp, DefaultScreen(disp)),
162 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
163 XDefineCursor(disp, awesomeconf->statusbar.window, drawcontext->cursor[CurNormal]);
164 updatebarpos(disp, awesomeconf->statusbar);
165 XMapRaised(disp, awesomeconf->statusbar.window);
166 /* pixmap for everything */
167 drawcontext->drawable = XCreatePixmap(disp, DefaultRootWindow(disp), DisplayWidth(disp, DefaultScreen(disp)), awesomeconf->statusbar.height, DefaultDepth(disp, DefaultScreen(disp)));
168 drawcontext->gc = XCreateGC(disp, DefaultRootWindow(disp), 0, 0);
169 XSetLineAttributes(disp, drawcontext->gc, 1, LineSolid, CapButt, JoinMiter);
170 if(!drawcontext->font.set)
171 XSetFont(disp, drawcontext->gc, drawcontext->font.xfont->fid);
172 loadawesomeprops(disp, awesomeconf);
176 * Startup Error handler to check if another window manager
177 * is already running.
179 static int __attribute__ ((noreturn))
180 xerrorstart(Display * dsply __attribute__ ((unused)), XErrorEvent * ee __attribute__ ((unused)))
182 eprint("awesome: another window manager is already running\n");
185 /* extern */
187 void
188 uicb_quit(Display *disp __attribute__ ((unused)),
189 DC *drawcontext __attribute__ ((unused)),
190 awesome_config *awesomeconf __attribute__((unused)),
191 const char *arg __attribute__ ((unused)))
193 readin = running = False;
197 get_windows_area_x(Statusbar statusbar __attribute__ ((unused)))
199 return 0;
203 get_windows_area_y(Statusbar statusbar)
205 if(statusbar.position == BarTop)
206 return statusbar.height;
208 return 0;
212 get_windows_area_height(Display *disp, Statusbar statusbar)
214 if(statusbar.position == BarTop || statusbar.position == BarBot)
215 return DisplayHeight(disp, DefaultScreen(disp)) - statusbar.height;
217 return DisplayHeight(disp, DefaultScreen(disp));
221 get_windows_area_width(Display *disp,
222 Statusbar statusbar __attribute__ ((unused)))
224 return DisplayWidth(disp, DefaultScreen(disp));
227 void
228 updatebarpos(Display *disp, Statusbar statusbar)
230 XEvent ev;
231 ScreenInfo *si;
233 switch (statusbar.position)
235 default:
236 XMoveWindow(disp, statusbar.window, 0, 0);
237 break;
238 case BarBot:
239 si = get_display_info(disp, statusbar);
240 XMoveWindow(disp, statusbar.window, 0, si->height);
241 XFree(si);
242 break;
243 case BarOff:
244 XMoveWindow(disp, statusbar.window, 0, 0 - statusbar.height);
245 break;
247 XSync(disp, False);
248 while(XCheckMaskEvent(disp, EnterWindowMask, &ev));
251 /* There's no way to check accesses to destroyed windows, thus those cases are
252 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
253 * default error handler, which may call exit.
256 xerror(Display * edpy, XErrorEvent * ee)
258 if(ee->error_code == BadWindow
259 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
260 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
261 || (ee->request_code == X_PolyFillRectangle
262 && ee->error_code == BadDrawable)
263 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
264 || (ee->request_code == X_ConfigureWindow
265 && ee->error_code == BadMatch) || (ee->request_code == X_GrabKey
266 && ee->error_code == BadAccess)
267 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
268 return 0;
269 fprintf(stderr, "awesome: fatal error: request code=%d, error code=%d\n",
270 ee->request_code, ee->error_code);
272 return xerrorxlib(edpy, ee); /* may call exit */
276 main(int argc, char *argv[])
278 char *p;
279 int r, xfd, e_dummy;
280 fd_set rd;
281 XEvent ev;
282 Display * dpy;
283 Window root;
284 awesome_config awesomeconf;
285 int shape_event, randr_event_base;
287 if(argc == 2 && !strcmp("-v", argv[1]))
288 eprint("awesome-" VERSION " © 2007 Julien Danjou\n");
289 else if(argc != 1)
290 eprint("usage: awesome [-v]\n");
291 setlocale(LC_CTYPE, "");
294 if(!(dpy = XOpenDisplay(NULL)))
295 eprint("awesome: cannot open display\n");
296 xfd = ConnectionNumber(dpy);
297 root = RootWindow(dpy, DefaultScreen(dpy));
298 XSetErrorHandler(xerrorstart);
300 /* this causes an error if some other window manager is running */
301 XSelectInput(dpy, root, SubstructureRedirectMask);
302 XSync(dpy, False);
304 XSync(dpy, False);
305 XSetErrorHandler(NULL);
306 xerrorxlib = XSetErrorHandler(xerror);
307 XSync(dpy, False);
308 parse_config(dpy, DefaultScreen(dpy), &dc, &awesomeconf);
309 setup(dpy, &dc, &awesomeconf);
310 drawstatusbar(dpy, &dc, &awesomeconf);
312 void (*handler[LASTEvent]) (XEvent *, awesome_config *) =
314 [ButtonPress] = handle_event_buttonpress,
315 [ConfigureRequest] = handle_event_configurerequest,
316 [ConfigureNotify] = handle_event_configurenotify,
317 [DestroyNotify] = handle_event_destroynotify,
318 [EnterNotify] = handle_event_enternotify,
319 [LeaveNotify] = handle_event_leavenotify,
320 [Expose] = handle_event_expose,
321 [KeyPress] = handle_event_keypress,
322 [MappingNotify] = handle_event_mappingnotify,
323 [MapRequest] = handle_event_maprequest,
324 [PropertyNotify] = handle_event_propertynotify,
325 [UnmapNotify] = handle_event_unmapnotify,
328 /* check for shape extension */
329 if((awesomeconf.have_shape = XShapeQueryExtension(dpy, &shape_event, &e_dummy)))
330 handler[shape_event] = handle_event_shape;
332 /* check for randr extension */
333 if((awesomeconf.have_randr = XRRQueryExtension(dpy, &randr_event_base, &e_dummy)))
334 handler[randr_event_base + RRScreenChangeNotify] = handle_event_randr_screen_change_notify;
336 scan(dpy, &dc, &awesomeconf);
337 XSync(dpy, False);
339 /* main event loop, also reads status text from stdin */
340 while(running)
342 FD_ZERO(&rd);
343 if(readin)
344 FD_SET(STDIN_FILENO, &rd);
345 FD_SET(xfd, &rd);
346 if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1)
348 if(errno == EINTR)
349 continue;
350 eprint("select failed\n");
352 if(FD_ISSET(STDIN_FILENO, &rd))
354 switch (r = read(STDIN_FILENO, awesomeconf.statustext, sizeof(awesomeconf.statustext) - 1))
356 case -1:
357 strncpy(awesomeconf.statustext, strerror(errno), sizeof(awesomeconf.statustext) - 1);
358 awesomeconf.statustext[sizeof(awesomeconf.statustext) - 1] = '\0';
359 readin = False;
360 break;
361 case 0:
362 strncpy(awesomeconf.statustext, "EOF", 4);
363 readin = False;
364 break;
365 default:
366 for(awesomeconf.statustext[r] = '\0', p = awesomeconf.statustext + a_strlen(awesomeconf.statustext) - 1;
367 p >= awesomeconf.statustext && *p == '\n'; *p-- = '\0');
368 for(; p >= awesomeconf.statustext && *p != '\n'; --p);
369 if(p > awesomeconf.statustext)
370 strncpy(awesomeconf.statustext, p + 1, sizeof(awesomeconf.statustext));
372 drawstatusbar(dpy, &dc, &awesomeconf);
375 while(XPending(dpy))
377 XNextEvent(dpy, &ev);
378 if(handler[ev.type])
379 (handler[ev.type]) (&ev, &awesomeconf); /* call handler */
382 cleanup(dpy, &dc, &awesomeconf);
383 XCloseDisplay(dpy);
385 return 0;