wmii/main.c

485 lines
12 KiB
C

/* (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
* (C)opyright MMVI-MMVII Kris Maglione <fbsdaemon@gmail.com>
* See LICENSE file for license details.
*/
#include "wmii.h"
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
static Bool check_other_wm;
static int (*x_error_handler) (Display *, XErrorEvent *);
static char version[] = "wmiiwm - " VERSION ", (C)opyright MMVI-MMVII Kris Maglione\n";
static struct sigaction sa;
static void
usage() {
fatal("usage: wmiiwm -a <address> [-r <wmiirc>] [-v]\n");
}
static void
scan_wins() {
int i;
uint num;
Window *wins;
XWindowAttributes wa;
Window d1, d2;
if(XQueryTree(blz.dpy, blz.root, &d1, &d2, &wins, &num)) {
for(i = 0; i < num; i++) {
if(!XGetWindowAttributes(blz.dpy, wins[i], &wa))
continue;
if(wa.override_redirect || XGetTransientForHint(blz.dpy, wins[i], &d1))
continue;
if(wa.map_state == IsViewable)
manage_client(create_client(wins[i], &wa));
}
for(i = 0; i < num; i++) {
if(!XGetWindowAttributes(blz.dpy, wins[i], &wa))
continue;
if(XGetTransientForHint(blz.dpy, wins[i], &d1)
&& wa.map_state == IsViewable)
manage_client(create_client(wins[i], &wa));
}
}
if(wins)
XFree(wins);
}
static int
win_property(Window w, Atom a, Atom t, long l, uchar **prop) {
Atom real;
int format;
ulong res, extra;
int status;
status = XGetWindowProperty(blz.dpy, w, a, 0L, l, False, t, &real, &format,
&res, &extra, prop);
if(status != Success || *prop == 0) {
return 0;
}
if(res == 0) {
free((void *) *prop);
}
return res;
}
int
win_proto(Window w) {
Atom *protocols;
long res;
int protos = 0;
int i;
res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L,
((uchar **) &protocols));
if(res <= 0) {
return protos;
}
for(i = 0; i < res; i++) {
if(protocols[i] == wm_atom[WMDelete])
protos |= WM_PROTOCOL_DELWIN;
}
free((char *) protocols);
return protos;
}
static void
init_atoms() {
wm_atom[WMState] = XInternAtom(blz.dpy, "WM_STATE", False);
wm_atom[WMProtocols] = XInternAtom(blz.dpy, "WM_PROTOCOLS", False);
wm_atom[WMDelete] = XInternAtom(blz.dpy, "WM_DELETE_WINDOW", False);
net_atom[NetSupported] = XInternAtom(blz.dpy, "_NET_SUPPORTED", False);
net_atom[NetWMName] = XInternAtom(blz.dpy, "_NET_WM_NAME", False);
tags_atom = XInternAtom(blz.dpy, "_WIN_TAGS", False);
XChangeProperty(blz.dpy, blz.root, net_atom[NetSupported], XA_ATOM, 32,
PropModeReplace, (uchar *) net_atom, NetLast);
}
static void
init_cursors() {
Pixmap pix;
XColor black, dummy;
XAllocNamedColor(blz.dpy, DefaultColormap(blz.dpy, blz.screen), "black", &black, &dummy);
pix = XCreateBitmapFromData(blz.dpy, blz.root, (char[]){0}, 1, 1);
cursor[CurNormal] = XCreateFontCursor(blz.dpy, XC_left_ptr);
cursor[CurNECorner] = XCreateFontCursor(blz.dpy, XC_top_right_corner);
cursor[CurNWCorner] = XCreateFontCursor(blz.dpy, XC_top_left_corner);
cursor[CurSECorner] = XCreateFontCursor(blz.dpy, XC_bottom_right_corner);
cursor[CurSWCorner] = XCreateFontCursor(blz.dpy, XC_bottom_left_corner);
cursor[CurMove] = XCreateFontCursor(blz.dpy, XC_fleur);
cursor[CurInput] = XCreateFontCursor(blz.dpy, XC_xterm);
cursor[CurInvisible] = XCreatePixmapCursor(blz.dpy, pix, pix, &black, &black, 0, 0);
XFreePixmap(blz.dpy, pix);
}
static void
init_screen(WMScreen *screen) {
Window w;
int ret;
unsigned mask;
XGCValues gcv;
gcv.subwindow_mode = IncludeInferiors;
gcv.function = GXxor;
gcv.foreground = def.normcolor.bg;
gcv.plane_mask = AllPlanes;
gcv.graphics_exposures = False;
xorgc = XCreateGC(blz.dpy, blz.root, GCForeground | GCGraphicsExposures |
GCFunction | GCSubwindowMode | GCPlaneMask, &gcv);
screen->rect.x = screen->rect.y = 0;
screen->rect.width = DisplayWidth(blz.dpy, blz.screen);
screen->rect.height = DisplayHeight(blz.dpy, blz.screen);
def.snap = screen->rect.height / 63;
sel_screen = XQueryPointer(blz.dpy, blz.root, &w, &w, &ret, &ret, &ret, &ret, &mask);
}
/*
* There's no way to check accesses to destroyed windows, thus
* those cases are ignored (especially on UnmapNotify's).
* Other types of errors call Xlib's default error handler, which
* calls exit().
*/
int
wmii_error_handler(Display *dpy, XErrorEvent *error) {
if(check_other_wm)
fatal("wmiiwm: another window manager is already running\n");
if(error->error_code == BadWindow
|| (error->request_code == X_SetInputFocus
&& error->error_code == BadMatch)
|| (error->request_code == X_PolyText8
&& error->error_code == BadDrawable)
|| (error->request_code == X_PolyFillRectangle
&& error->error_code == BadDrawable)
|| (error->request_code == X_PolySegment
&& error->error_code == BadDrawable)
|| (error->request_code == X_ConfigureWindow
&& error->error_code == BadMatch)
|| (error->request_code == X_GrabKey
&& error->error_code == BadAccess))
return 0;
fprintf(stderr, "wmiiwm: fatal error: Xrequest code=%d, Xerror code=%d\n",
error->request_code, error->error_code);
return x_error_handler(blz.dpy, error); /* calls exit() */
}
static void
cleanup() {
Client *c;
for(c=client; c; c=c->next)
reparent_client(c, blz.root, c->sel->rect.x, c->sel->rect.y);
XSync(blz.dpy, False);
}
static void
cleanup_handler(int signal) {
sa.sa_handler = SIG_DFL;
sigaction(signal, &sa, nil);
switch(signal) {
case SIGINT:
srv.running = False;
break;
default:
cleanup();
XCloseDisplay(blz.dpy);
raise(signal);
break;
}
}
static void
sigchld_handler(int sig) {
int ret;
/* We only spawn one child */
wait(&ret);
}
static void
init_traps() {
char buf[1];
int fd[2];
if(pipe(fd) != 0)
fatal("Can't pipe(): %s\n", strerror(errno));
switch(fork()) {
case -1:
fatal("Can't fork(): %s\n", strerror(errno));
default:
close(fd[0]);
sa.sa_flags = 0;
sa.sa_handler = cleanup_handler;
sigaction(SIGINT, &sa, nil);
sigaction(SIGTERM, &sa, nil);
sigaction(SIGQUIT, &sa, nil);
sigaction(SIGHUP, &sa, nil);
break;
case 0:
close(fd[1]);
close(ConnectionNumber(blz.dpy));
setsid();
blz.dpy = XOpenDisplay(0);
if(!blz.dpy)
fatal("wmiiwm: cannot open display\n");
read(fd[0], buf, 1);
XSetInputFocus(blz.dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
XCloseDisplay(blz.dpy);
exit(0);
}
}
void
check_9pcon(IXPConn *c) {
serve_9pcon(c);
check_x_event(c);
}
int
main(int argc, char *argv[]) {
char *wmiirc, *errstr, *namespace, *tmp;
char address[1024], ns_path[1024];
WMScreen *s;
struct passwd *passwd;
int i;
XSetWindowAttributes wa;
passwd = getpwuid(getuid());
user = estrdup(passwd->pw_name);
wmiirc = nil;
tmp = getenv("WMII_NS_PATH");
if(tmp)
strncpy(ns_path, tmp, sizeof(ns_path));
else
snprintf(ns_path, sizeof(ns_path), "/tmp/ns.%s.%s", user, getenv("DISPLAY"));
tmp = getenv("WMII_ADDRESS");
if(tmp)
strncpy(address, tmp, sizeof(ns_path));
else
snprintf(address, sizeof(address), "unix!%s/wmii", ns_path);
/* command line args */
for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version);
exit(0);
break;
case 'V':
verbose = True;
break;
case 'a':
if(i + 1 < argc)
strncpy(address, argv[++i], sizeof(address));
else
usage();
break;
case 'r':
if(i + 1 < argc)
wmiirc = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
if(!address)
usage();
setenv("WMII_NS_PATH", ns_path, True);
setenv("WMII_ADDRESS", address, True);
setlocale(LC_CTYPE, "");
starting = True;
blz.dpy = XOpenDisplay(0);
if(!blz.dpy)
fatal("wmiiwm: cannot open dpy\n");
blz.screen = DefaultScreen(blz.dpy);
blz.root = RootWindow(blz.dpy, blz.screen);
check_other_wm = True;
x_error_handler = XSetErrorHandler(wmii_error_handler);
XSelectInput(blz.dpy, blz.root, SubstructureRedirectMask | EnterWindowMask);
XSync(blz.dpy, False);
check_other_wm = False;
init_traps();
/* Check namespace permissions */
if(!strncmp(address, "unix!", 5)) {
struct stat st;
namespace = estrdup(&address[5]);
for(i = strlen(namespace) - 1; i >= 0; i--)
if(namespace[i] == '/') break;
namespace[i+1] = '\0';
if(stat(namespace, &st))
fatal("wmiiwm: can't stat namespace directory \"%s\": %s\n",
namespace, strerror(errno));
if(getuid() != st.st_uid)
fatal("wmiiwm: namespace directory \"%s\" exists, "
"but is not owned by you",
namespace);
if(st.st_mode & 077)
fatal("wmiiwm: namespace directory \"%s\" exists, "
"but has group or world permissions",
namespace);
free(namespace);
}
errstr = nil;
i = ixp_create_sock(address, &errstr);
if(i < 0)
fatal("wmiiwm: fatal: %s\n", errstr);
/* start wmiirc */
if(wmiirc) {
int name_len = strlen(wmiirc) + 6;
char execstr[name_len];
signal(SIGCHLD, sigchld_handler);
switch(fork()) {
case 0:
if(setsid() == -1)
fatal("wmiiwm: can't setsid: %s\n", strerror(errno));
close(i);
close(ConnectionNumber(blz.dpy));
snprintf(execstr, name_len, "exec %s", wmiirc);
execl("/bin/sh", "sh", "-c", execstr, nil);
fatal("wmiiwm: can't exec \"%s\": %s\n", wmiirc, strerror(errno));
case -1:
perror("wmiiwm: cannot fork wmiirc");
default:
break;
}
}
ixp_server_open_conn(&srv, i, &p9srv, check_9pcon, nil);
ixp_server_open_conn(&srv, ConnectionNumber(blz.dpy), nil, check_x_event, nil);
view = nil;
client = nil;
key = nil;
memset(&def, 0, sizeof(def));
def.font.fontstr = estrdup(BLITZ_FONT);
def.border = 1;
def.colmode = Coldefault;
def.mod = Mod1Mask;
strncpy(def.grabmod, "Mod1", sizeof(def.grabmod));
strncpy(def.focuscolor.colstr, BLITZ_FOCUSCOLORS, sizeof(def.focuscolor.colstr));
strncpy(def.normcolor.colstr, BLITZ_NORMCOLORS, sizeof(def.normcolor.colstr));
loadcolor(&blz, &def.focuscolor);
loadcolor(&blz, &def.normcolor);
init_atoms();
init_cursors();
loadfont(&blz, &def.font);
init_lock_keys();
num_screens = 1;
screens = emallocz(num_screens * sizeof(*screens));
for(i = 0; i < num_screens; i++) {
s = &screens[i];
s->lbar = nil;
s->rbar = nil;
s->sel = nil;
init_screen(s);
pmap = XCreatePixmap(
/* display */ blz.dpy,
/* drawable */ blz.root,
/* width */ s->rect.width,
/* height */ s->rect.height,
/* depth */ DefaultDepth(blz.dpy, blz.screen)
);
wa.event_mask =
SubstructureRedirectMask
| EnterWindowMask
| LeaveWindowMask
| FocusChangeMask;
wa.cursor = cursor[CurNormal];
XChangeWindowAttributes(blz.dpy, blz.root, CWEventMask | CWCursor, &wa);
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask =
ExposureMask
| ButtonReleaseMask
| FocusChangeMask
| SubstructureRedirectMask
| SubstructureNotifyMask;
s->brect = s->rect;
s->brect.height = labelh(&def.font);
s->brect.y = s->rect.height - s->brect.height;
s->barwin = XCreateWindow(
/* display */ blz.dpy,
/* parent */ RootWindow(blz.dpy, blz.screen),
/* x */ s->brect.x,
/* y */ s->brect.y,
/* width */ s->brect.width,
/* height */ s->brect.height,
/*border_width*/0,
/* depth */ DefaultDepth(blz.dpy, blz.screen),
/* class */ CopyFromParent,
/* visual */ DefaultVisual(blz.dpy, blz.screen),
/* valuemask */ CWOverrideRedirect | CWBackPixmap | CWEventMask,
/* attrubutes */&wa
);
XSync(blz.dpy, False);
s->bbrush.blitz = &blz;
s->bbrush.gc = XCreateGC(blz.dpy, s->barwin, 0, 0);
s->bbrush.drawable = pmap;
s->bbrush.rect = s->brect;
s->bbrush.rect.x = 0;
s->bbrush.rect.y = 0;
s->bbrush.color = def.normcolor;
s->bbrush.font = &def.font;
s->bbrush.border = 1;
draw_bar(s);
XMapRaised(blz.dpy, s->barwin);
}
screen = &screens[0];
screen->focus = nil;
XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime);
scan_wins();
starting = False;
update_views();
if(view)
write_event("FocusTag %s\n", screen->sel->name);
check_x_event(nil);
errstr = ixp_server_loop(&srv);
if(errstr)
fprintf(stderr, "wmiiwm: fatal: %s\n", errstr);
cleanup();
XCloseDisplay(blz.dpy);
ixp_server_close(&srv);
if(errstr)
return 1;
return 0;
}