all repos — dwm @ 48b6e9a3968e54a87f022c8e68b5bec5423cb75f

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