xtools

Check-in [14c3d4a281]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Initial Commit
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:14c3d4a28123d75e4c36fc8fc0158dc34d75ceb8e88dae1cb4f60833b3abc57b
User & Date: jmcclure 2018-01-03 19:29:16
Context
2018-01-03
21:15
Added info check-in: 00626f0bb2 user: jmcclure tags: trunk
19:29
Initial Commit check-in: 14c3d4a281 user: jmcclure tags: trunk
19:21
initial empty check-in check-in: 4a38f900c5 user: jmcclure tags: trunk
Changes

Added Makefile.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

CC       ?= gcc
PREFIX   ?= /usr

all:
	$(CC) -o xi xi.c $(CFLAGS) $(LDFLAGS)
	$(CC) -o wmutil wmutil.c $(CFLAGS) $(LDFLAGS) -lX11
	$(CC) -o tmuxwm tmuxwm.c $(CFLAGS) $(LDFLAGS) -lX11
	strip xi wmutil tmuxwm

install:
	install -Dm0755 xi $(DESTDIR)$(PREFIX)/bin/xi
	install -Dm0755 wmutil $(DESTDIR)$(PREFIX)/bin/wmutil
	install -Dm0755 tmuxwm $(DESTDIR)$(PREFIX)/bin/tmuxwm

clean:
	rm -f xi wmutil tmuxwm

Added tmuxwm.c.













































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* tmuxwm.c
 * --------
 * Minimalist WM with tmux session
 * --------
 *
 * Copyright (c) 2017 Jesse McClure <code@jessemcclure.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include <X11/XKBlib.h>

typedef struct Bind { KeySym sym; int mod; const char * const *args; } Bind;
typedef const char * const Arg[];

Arg tmux_client = (Arg) { "urxvt", "-e", "tmux", "new-session", "-AD", "-s", "TmuxWM", NULL };

static void cycle(void);

#include <X11/XF86keysym.h>
static Bind bind[] = {
	{ XK_Return, ShiftMask | Mod4Mask, tmux_client },
	{ XK_Return,             Mod4Mask, (Arg) { "tmux", "new-window", NULL } },
	{ XK_Tab,                Mod4Mask, (Arg) { "tmux", "next-window", NULL } },
	{ XK_space,              Mod4Mask, (Arg) { "plumb", NULL } },
	{ XK_1,                  Mod4Mask, (Arg) { "tmux", "select-window", "-t", "1", NULL } },
	{ XK_2,                  Mod4Mask, (Arg) { "tmux", "select-window", "-t", "2", NULL } },
	{ XK_3,                  Mod4Mask, (Arg) { "tmux", "select-window", "-t", "3", NULL } },
	{ XK_4,                  Mod4Mask, (Arg) { "tmux", "select-window", "-t", "4", NULL } },
	{ XK_5,                  Mod4Mask, (Arg) { "tmux", "select-window", "-t", "5", NULL } },
	/* Launchers / Misc */
	{ XK_w,                  Mod4Mask, (Arg) { "qutebrowser", NULL} },
	{ XK_F4,                 Mod1Mask, (Arg) { "wmutil", "kill", NULL} },
	{ XK_Up,                 Mod4Mask, (Arg) { "wmutil", "move", "Y", "-10", NULL} },
	{ XK_Down,               Mod4Mask, (Arg) { "wmutil", "move", "Y", "+10", NULL} },
	{ XK_Left,               Mod4Mask, (Arg) { "wmutil", "move", "X", "-10", NULL} },
	{ XK_Right,              Mod4Mask, (Arg) { "wmutil", "move", "X", "+10", NULL} },
	{ XK_Up,     ShiftMask | Mod4Mask, (Arg) { "wmutil", "move", "H", "-10", NULL} },
	{ XK_Down,   ShiftMask | Mod4Mask, (Arg) { "wmutil", "move", "H", "+10", NULL} },
	{ XK_Left,   ShiftMask | Mod4Mask, (Arg) { "wmutil", "move", "W", "-10", NULL} },
	{ XK_Right,  ShiftMask | Mod4Mask, (Arg) { "wmutil", "move", "W", "+10", NULL} },
	{ XF86XK_AudioRaiseVolume,      0, (Arg) { "amixer", "set", "Master", "2+", NULL} },
	{ XF86XK_AudioLowerVolume,      0, (Arg) { "amixer", "set", "Master", "2-", NULL} },
	{ XF86XK_AudioMute,             0, (Arg) { "amixer", "set", "Master", "toggle", NULL} },
	{ XF86XK_ScreenSaver,           0, (Arg) { "slock", NULL} },
};

static void configurerequest(XEvent *);
static void enternotify(XEvent *);
static void keypress(XEvent *);
static void maprequest(XEvent *);
static void unmapnotify(XEvent *);

static Display *dpy;
static Window root;
static int sw, sh;
static void (*handler[LASTEvent])(XEvent *) = {
	[ConfigureRequest]   = configurerequest,
	[EnterNotify]        = enternotify,
	[KeyPress]           = keypress,
	[MapRequest]         = maprequest,
};

static int xerror(Display *d, XErrorEvent *e) {
	char msg[256];
	XGetErrorText(d, e->error_code, msg, sizeof(msg));
	return fprintf(stderr,"[X11 %d:%d] %s\n", e->request_code, e->error_code, msg);
}

static int spawn(Arg args) {
	if (fork() != 0) return 1;
	close(ConnectionNumber(dpy));
	setsid();
	if (fork() != 0) exit(0);
	return execvp(args[0], (char * const *) args);
}

int main(int argc, const char **argv) {
	signal(SIGCHLD, SIG_IGN);
	if(!(dpy = XOpenDisplay(0x0))) return 1;
	XSetErrorHandler(xerror);
	root = DefaultRootWindow(dpy);
	XSetWindowBackground(dpy, root, 0x060812);
	XDefineCursor(dpy, root, XCreateFontCursor(dpy, 68));
	sw = DisplayWidth(dpy, DefaultScreen(dpy));
	sh = DisplayHeight(dpy, DefaultScreen(dpy));
	int i;
	XGrabKey(dpy, XKeysymToKeycode(dpy, XK_Tab), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
	for (i = 0; i < sizeof(bind)/sizeof(bind[0]); ++i)
		XGrabKey(dpy, XKeysymToKeycode(dpy, bind[i].sym), bind[i].mod, root, True, GrabModeAsync, GrabModeAsync);
	XSelectInput(dpy, root, SubstructureNotifyMask | SubstructureRedirectMask);
	XEvent ev;
	spawn(tmux_client);
	while (!XNextEvent(dpy,&ev))
		if (handler[ev.type]) handler[ev.type](&ev);
	return 0;
}

void configurerequest(XEvent *ev) {
	XConfigureRequestEvent *e = &ev->xconfigurerequest;
	int bpx = (e->x == 0 && e->y == 0 && e->width == sw && e->height == sh ? 0 : 2);
	XWindowChanges wc = { e->x, e->y, e->width, e->height, bpx, e->detail, Above };
	XConfigureWindow(dpy, e->window, e->value_mask | CWBorderWidth, &wc);
}

void enternotify(XEvent *ev) {
	XSetInputFocus(dpy, ev->xcrossing.window, RevertToPointerRoot, CurrentTime);
	XRaiseWindow(dpy, ev->xcrossing.window);
}

void keypress(XEvent *ev) {
	XKeyEvent *e = &ev->xkey;
	KeySym sym = XkbKeycodeToKeysym(dpy, e->keycode, 0, 0);
	int i;
	if (e->state == Mod1Mask && sym == XK_Tab) XCirculateSubwindowsUp(dpy, root);
	else for (i = 0; i < sizeof(bind)/sizeof(bind[0]); ++i)
		if (bind[i].sym == sym && bind[i].mod == e->state) spawn(bind[i].args);
}

void maprequest(XEvent *ev) {
	XMapRequestEvent *e = &ev->xmaprequest;
	XWindowAttributes wa;
	if (!XGetWindowAttributes(dpy, e->window, &wa) || wa.override_redirect) return;
	XWMHints *wmHint = XGetWMHints(dpy,e->window);
	if (!(wmHint && wmHint->flags & InputHint && !wmHint->input)) XSelectInput(dpy, e->window, EnterWindowMask);
	if (wmHint) XFree(wmHint);
	Window parent;
	if (XGetTransientForHint(dpy, e->window, &parent)) XMoveResizeWindow(dpy, e->window, wa.x, wa.y, wa.width, wa.height);
	else XMoveResizeWindow(dpy, e->window, 0, 0, sw, sh);
	XSetWindowBorder(dpy, e->window, 0x000000);
	XMapWindow(dpy, e->window);
}

Added wmutil.c.



































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* wmutil.c
 * --------
 * Window Manager Utilities
 * --------
 *
 * Copyright (c) 2017 Jesse McClure <code@jessemcclure.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

static Display *dpy;
static Window root;
static int sw, sh;

void send_message(Window win, const char *type, const char *msg) {
	XEvent ev;
	ev.type = ClientMessage;
	ev.xclient.window = win;
	ev.xclient.message_type = XInternAtom(dpy, type, False);
	ev.xclient.format = 32;
	ev.xclient.data.l[0] = XInternAtom(dpy, msg, False);;
	ev.xclient.data.l[1] = CurrentTime;
	XSendEvent(dpy, win, False, NoEventMask, &ev);
}

void kill(Window win) {
	send_message(win, "WM_PROTOCOLS", "WM_DELETE_WINDOW");
}

void external(Window win) {
	// TODO
}

void move_resize(Window win, const char *arg, int delta) {
	XWindowAttributes wa;
	XGetWindowAttributes(dpy, win, &wa);
	switch (arg[0]) {
		case 'h': case 'H': wa.height += delta; break;
		case 'w': case 'W': wa.width += delta; break;
		case 'x': case 'X': wa.x += delta; break;
		case 'y': case 'Y': wa.y += delta; break;
	}
	XMoveResizeWindow(dpy, win, wa.x, wa.y, wa.width, wa.height);
}

int main(int argc, char *const argv[]) {
	if (!(dpy = XOpenDisplay(0x0)) || argc < 2) return 1;
	root = DefaultRootWindow(dpy);
	sw = DisplayWidth(dpy, DefaultScreen(dpy));
	sh = DisplayHeight(dpy, DefaultScreen(dpy));
	Window win;
	int ig;
	if ((win=atoi(argv[1])) && argc > 2) argv++;
	else XGetInputFocus(dpy, &win, &ig);
	switch (argv[1][0]) {
		case 'e': case 'x': external(win); break;
		case 'k': kill(win); break;
		case 'm': if (argc > 3) move_resize(win, argv[2], atoi(argv[3])); break;
		case 's': if (argc > 3) send_message(win, argv[2], argv[3]); break;
	}
	XCloseDisplay(dpy);
	return 0;
}

Added xi.c.



















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/* xi.c
 * ----
 * Minimalist Xinit replacement
 * USAGE: xi $XDG_VTNR /path/to/wm
 * ----
 *
 * Copyright (c) 2017 Jesse McClure <code@jessemcclure.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <linux/limits.h>

static volatile int xrunning = 0;

void handler(int sig) {
	if (sig == SIGUSR1) xrunning = 1;
	if (sig == SIGCHLD || sig == SIGTERM) xrunning = 0;
}

int server(const char *dpy, const char *tty) {
	const char *xorg = "/usr/lib/xorg-server/Xorg";
	int pid;
	if ((pid=fork()) < 0) exit(1);
	else if (pid > 0) return pid;
	signal(SIGTTIN, SIG_IGN);
	signal(SIGTTOU, SIG_IGN);
	signal(SIGUSR1, SIG_IGN);
	execl(xorg, xorg, dpy, tty, "-keeptty", "-nolisten", "tcp", "-quiet", NULL);
	_exit(2);
}

int client(const char *dpy, const char *wm) {
	int pid;
	if ((pid=fork()) < 0) exit(3);
	else if (pid > 0) return pid;
	char xauth[PATH_MAX];
	snprintf(xauth, PATH_MAX, "%s/%s", getenv("HOME"), ".Xauthority");
	setenv("XAUTHORITY", xauth, 1);
	setenv("DISPLAY", dpy, 1);
	execlp(wm, wm, NULL);
	_exit(4);
}

int main(int argc, const char **argv) {
	if (argc != 3 || argv[1][0] < '1' || argv[1][0] > '9') {
		fprintf(stderr,"USAGE: %s vt wm\n", argv[0]);
		return 1;
	}
	char dpy[] = ":1";
	char tty[] = "vt1";
	dpy[1] = argv[1][0];
	tty[2] = argv[1][0];

	struct sigaction sa = { 0, 0, 0, 0, 0 };
	sa.sa_handler = handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sigaction(SIGUSR1, &sa, NULL);
	sigaction(SIGCHLD, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);

	int xpid = server(dpy, tty);
	while (!xrunning) pause();
	int cpid = client(dpy, argv[2]);
	while (xrunning) pause();

	kill(cpid, SIGTERM);
	waitpid(cpid, NULL, 0);
	kill(xpid, SIGTERM);
	waitpid(xpid, NULL, 0);
	return 0;
}