all repos — dwm @ 586f66331d1105be03c42e6faeae1672b974a98a

fork of suckless dynamic window manager

wm.c (view raw)

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