# Function calls in a single line. # # To run (on Linux): # $ ./translate_subx init.linux 0*.subx apps/subx-params.subx apps/calls.subx # $ mv a.elf apps/calls # # Example 1: # $ echo '(foo %eax)' | apps/calls # # . (foo %eax) # output has comments # ff 6/subop/push %eax # push # e8/call foo/disp32 # call # 81 0/subop/add %esp 4/imm32 # undo push # # Example 2: # $ echo '(foo Var1 *(eax + 4) "blah")' | apps/calls # # . (foo Var1 *(eax + 4) "blah") # 68/push "blah"/imm32 # ff 6/subop/push *(eax + 4) # push args in.. # 68/push Var1/imm32 # ..reverse order # e8/call foo/disp32 # 81 0/subop/add %esp 0xc/imm32 # undo pushes # # Calls always begin with '(' as the first non-whitespace on a line. == code Entry: # run tests if necessary, convert stdin if not # . prologue 89/<- %ebp 4/r32/esp # initialize heap # . Heap = new-segment(Heap-size) # . . push args 68/push Heap/imm32 ff 6/subop/push *Heap-size # . . call e8/call new-segment/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # - if argc > 1 and argv[1] == "test", then return run_tests() # if (argc <= 1) goto run-main 81 7/subop/compare *ebp 1/imm32 7e/jump-if-<= $subx-calls-main:interactive/disp8 # if (!kernel-string-equal?(argv[1], "test")) goto run-main # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 ff 6/subop/push *(ebp+8) # . . call e8/call kernel-string-equal?/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # . if (eax == false) goto run-main 3d/compare-eax-and 0/imm32/false 74/jump-if-= $subx-calls-main:interactive/disp8 # run-tests() e8/call run-tests/disp32 # syscall(exit, *Num-test-failures) 8b/-> *Num-test-failures 3/r32/ebx eb/jump $subx-calls-main:end/disp8 $subx-calls-main:interactive: # - otherwise convert stdin # subx-calls(Stdin, Stdout) # . . push args 68/push Stdout/imm32 68/push Stdin/imm32 # . . call e8/call subx-calls/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # syscall(exit, 0) bb/copy-to-ebx 0/imm32 $subx-calls-main:end: e8/call syscall_exit/disp32 subx-calls: # in: (addr buffered-file), out: (addr buffered-file) # pseudocode: # var line: (stream byte 512) # var words: (stream slice 16) # at most function name and 15 args # while true # clear-stream(line) # read-line-buffered(in, line) # if (line->write == 0) break # end of file # skip-chars-matching(line, ' ') # if line->data[line->read] != '(' # write-stream-data(out, line) # continue # # emit comment # write-buffered(out, "# . ") # write-stream-data(out, line) # # emit code # ++line->read to skip '(' # clear-stream(words) # words = parse-line(line) # emit-call(out, words) # flush(out) # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 56/push-esi # var line/esi: (stream byte 512) 81 5/subop/subtract %esp 0x200/imm32 68/push 0x200/imm32/length 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %esi 4/r32/esp # var words/edx: (stream slice 128) # 16 rows * 8 bytes/row 81 5/subop/subtract %esp 0x80/imm32 68/push 0x80/imm32/length 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %edx 4/r32/esp $subx-calls:loop: # clear-stream(line) # . . push args 56/push-esi # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 # read-line-buffered(in, line) # . . push args 56/push-esi ff 6/subop/push *(ebp+8) # . . call e8/call read-line-buffered/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 $subx-calls:check0: # if (line->write == 0) break 81 7/subop/compare *esi 0/imm32 0f 84/jump-if-= $subx-calls:break/disp32 # skip-chars-matching(line, ' ') # . . push args 68/push 0x20/imm32/space 56/push-esi # . . call e8/call skip-chars-matching/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # if (line->data[line->read] == '(') goto convert-call # . ecx = line->read 8b/-> *(esi+4) 1/r32/ecx # . eax = line->data[line->read] 31/xor-with %eax 0/r32/eax 8a/copy-byte *(esi+ecx+0xc) 0/r32/AL # . if (eax == '(') goto convert-call 3d/compare-eax-and 0x28/imm32/open-paren 74/jump-if-= $subx-calls:convert-call/disp8 $subx-calls:pass-through: # write-stream-data(out, line) # . . push args 56/push-esi ff 6/subop/push *(ebp+0xc) # . . call e8/call write-stream-data/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # continue eb/jump $subx-calls:loop/disp8 $subx-calls:convert-call: # - emit comment # write-buffered(out, "# . ") # . . push args 68/push "# . "/imm32 ff 6/subop/push *(ebp+0xc) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # write-stream-data(out, line) # . . push args 56/push-esi ff 6/subop/push *(ebp+0xc) # . . call e8/call write-stream-data/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # - emit code # ++line->read to skip '(' ff 0/subop/increment *(esi+4) # clear-stream(words) # . . push args 52/push-edx # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 # words = parse-line(line) # . . push args 52/push-edx 56/push-esi # . . call e8/call parse-line/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # emit-call(out, words) # . . push args 52/push-edx ff 6/subop/push *(ebp+0xc) # . . call e8/call emit-call/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # loop e9/jump $subx-calls:loop/disp32 $subx-calls:break: # flush(out) # . . push args ff 6/subop/push *(ebp+0xc) # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 $subx-calls:end: # . reclaim locals 81 0/subop/add %esp 0x298/imm32 # 0x20c + 0x8c # . restore registers 5e/pop-to-esi 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return parse-line: # line: (addr stream byte), words: (addr stream slice) # pseudocode: # var word-slice: slice # while true # word-slice = next-word-string-or-expression-without-metadata(line) # if slice-empty?(word-slice) # break # end of line # write-int(words, word-slice->start) # write-int(words, word-slice->end) # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 51/push-ecx # var word-slice/ecx: slice 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp $parse-line:loop: # word-slice = next-word-string-or-expression-without-metadata(line) # . . push args 51/push-ecx ff 6/subop/push *(ebp+8) # . . call e8/call next-word-string-or-expression-without-metadata/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 $parse-line:check1: # if (slice-empty?(word-slice)) break # . eax = slice-empty?(word-slice) # . . push args 51/push-ecx # . . call e8/call slice-empty?/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 # . if (eax != false) break 3d/compare-eax-and 0/imm32/false 0f 85/jump-if-!= $parse-line:end/disp32 #? # dump word-slice {{{ #? # . write(2/stderr, "w: ") #? # . . push args #? 68/push "w: "/imm32 #? 68/push 2/imm32/stderr #? # . . call #? e8/call write/disp32 #? # . . discard args #? 81 0/subop/add %esp 8/imm32 #? # . clear-stream($Stderr->buffer) #? # . . push args #? 68/push $Stderr->buffer/imm32 #? # . . call #? e8/call clear-stream/disp32 #? # . . discard args #? 81 0/subop/add %esp 4/imm32 #? # . write-slice-buffered(Stderr, word-slice) #? # . . push args #? 51/push-ecx #? 68/push Stderr/imm32 #? # . . call #? e8/call write-slice-buffered/disp32 #? # . . discard args #? 81 0/subop/add %esp 8/imm32 #? # . flush(Stderr) #? # . . push args #? 68/push Stderr/imm32 #? # . . call #? e8/call flush/disp32 #? # . . discard args #? 81 0/subop/add %esp 4/imm32 #? # . write(2/stderr, "$\n") #? # . . push args #? 68/push "$\n"/imm32 #? 68/push 2/imm32/stderr #? # . . call #? e8/call write/disp32 #? # . . discard args #? 81 0/subop/add %esp 8/imm32 #? # }}} $parse-line:write-word: # write-int(words, word-slice->start) # . . push args ff 6/subop/push *ecx ff 6/subop/push *(ebp+0xc) # . . call e8/call write-int/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # write-int(words, word-slice->end) # . . push args ff 6/subop/push *(ecx+4) ff 6/subop/push *(ebp+0xc) # . . call e8/call write-int/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # loop e9/jump $parse-line:loop/disp32 $parse-line:end: # . reclaim locals 81 0/subop/add %esp 8/imm32 # . restore registers 59/pop-to-ecx # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-call: # out: (addr buffered-file), words: (addr stream slice) # pseudocode: # if (words->write < 8) abort # curr = &words->data[words->write-8] # min = words->data # # emit pushes # while true # if (curr <= min) break # if slice-starts-with?(curr, "%x") # floating-point register # write-buffered(out, "81 5/subop/subtract %esp 4/imm32\n") # write-buffered(out, "f3 0f 11/<- *esp ") # write-byte-buffered(out, *(curr->start+4)) # write-buffered(out, "/x32\n") # else if *curr->start in '%' '*' # write-buffered(out, "ff 6/subop/push ") # write-slice-buffered(out, curr) # write-buffered(out, "\n") # else # write-buffered(out, "68/push ") # write-slice-buffered(out, curr) # write-buffered(out, "/imm32\n") # curr -= 8 # # emit call # write-buffered(out, "e8/call ") # write-slice-buffered(out, curr) # write-buffered(out, "/disp32\n") # # emit pops # write-buffered(out, "81 0/subop/add %esp ") # write-int32-hex-buffered(out, words->write >> 1 - 4) # write-buffered(out, "/imm32\n") # # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 52/push-edx 56/push-esi # esi = words 8b/-> *(ebp+0xc) 6/r32/esi # if (words->write < 8) abort # . ecx = words->write - 8 8b/-> *esi 1/r32/ecx 81 5/subop/subtract %ecx 8/imm32 0f 8c/jump-if-< $emit-call:error1/disp32 # var curr/ecx: (addr slice) = &words->data[words->write-8] 8d/copy-address *(esi+ecx+0xc) 1/r32/ecx # var min/edx: (addr byte) = words->data 8d/copy-address *(esi+0xc) 2/r32/edx # - emit pushes $emit-call:push-loop: # if (curr <= min) break 39/compare %ecx 2/r32/edx 0f 8e/jump-if-<= $emit-call:call-instruction/disp32 # if !slice-starts-with?(curr, "%x") goto next check # . eax = slice-starts-with?(curr, "%x") # . . push args 68/push "%x"/imm32 51/push-ecx # . . call e8/call slice-starts-with?/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # . if (eax != 0) goto next check 3d/compare-eax-and 0/imm32/false 74/jump-if-= $emit-call:push-int/disp8 # emit floating-point push # . write-buffered(out, "81 5/subop/subtract %esp 4/imm32\n") # . . push args 68/push "81 5/subop/subtract %esp 4/imm32\n"/imm32 ff 6/subop/push *(ebp+8) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # . write-buffered(out, "f3 0f 11/<- *esp ") # . . push args 68/push "f3 0f 11/<- *esp "/imm32 ff 6/subop/push *(ebp+8) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # . write-byte-buffered(out, *(curr->start+4)) # . . eax = *(curr->start+4) 8b/-> *ecx 0/r32/eax 8a/copy-byte *(eax+4) 0/r32/eax 81 4/subop/and %eax 0xff/imm32 # . . push args 50/push-eax ff 6/subop/push *(ebp+8) # . . call e8/call write-byte-buffered/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # . write-buffered(out, "/x32\n") 68/push "/x32\n"/imm32 ff 6/subop/push *(ebp+8) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # . continue e9/jump $emit-call:next-push/disp32 $emit-call:push-int: # if (*curr->start in '%' '*') goto push-rm32 # . var start/eax: (addr byte) = curr->start 8b/-> *ecx 0/r32/eax # . var c/eax: byte = *eax 8b/-> *eax 0/r32/eax 81 4/subop/and %eax 0xff/imm32 # . if (c == '%') goto push-rm32 3d/compare-eax-and 0x25/imm32/percent 74/jump-if-= $emit-call:push-rm32/disp8 # . if (c == '*') goto push-rm32 3d/compare-eax-and 0x2a
/* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */

#include "dwm.h"
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>

/* extern */

char stext[256];
int screen, sx, sy, sw, sh, wax, way, waw, wah;
unsigned int bh, ntags, numlockmask;
Atom wmatom[WMLast], netatom[NetLast];
Bool *seltag;
Bool selscreen = True;
Client *clients = NULL;
Client *sel = NULL;
Client *stack = NULL;
Cursor cursor[CurLast];
Display *dpy;
DC dc = {0};
Window root, barwin;

/* static */

static int (*xerrorxlib)(Display *, XErrorEvent *);
static Bool otherwm, readin;
static Bool running = True;

static void
cleanup(void) {
	close(STDIN_FILENO);
	while(stack) {
		if(stack->isbanned)
			XMoveWindow(dpy, stack->win, stack->x, stack->y);
		unmanage(stack);
	}
	if(dc.font.set)
		XFreeFontSet(dpy, dc.font.set);
	else
		XFreeFont(dpy, dc.font.xfont);
	XUngrabKey(dpy, AnyKey, AnyModifier, root);
	XFreePixmap(dpy, dc.drawable);
	XFreeGC(dpy, dc.gc);
	XDestroyWindow(dpy, barwin);
	XFreeCursor(dpy, cursor[CurNormal]);
	XFreeCursor(dpy, cursor[CurResize]);
	XFreeCursor(dpy, cursor[CurMove]);
	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
	XSync(dpy, False);
	free(seltag);
}

static unsigned long
initcolor(const char *colstr) {
	Colormap cmap = DefaultColormap(dpy, screen);
	XColor color;

	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
		eprint("error, cannot allocate color '%s'\n", colstr);
	return color.pixel;
}

static void
initfont(const char *fontstr) {
	char *def, **missing;
	int i, n;

	missing = NULL;
	if(dc.font.set)
		XFreeFontSet(dpy, dc.font.set);
	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
	if(missing) {
		while(n--)
			fprintf(stderr, "missing fontset: %s\n", missing[n]);
		XFreeStringList(missing);
	}
	if(dc.font.set) {
		XFontSetExtents *font_extents;
		XFontStruct **xfonts;
		char **font_names;
		dc.font.ascent = dc.font.descent = 0;
		font_extents = XExtentsOfFontSet(dc.font.set);
		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
		for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
			if(dc.font.ascent < (*xfonts)->ascent)
				dc.font.ascent = (*xfonts)->ascent;
			if(dc.font.descent < (*xfonts)->descent)
				dc.font.descent = (*xfonts)->descent;
			xfonts++;
		}
	}
	else {
		if(dc.font.xfont)
			XFreeFont(dpy, dc.font.xfont);
		dc.font.xfont = NULL;
		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)))
			eprint("error, cannot load font: '%s'\n", fontstr);
		dc.font.ascent = dc.font.xfont->ascent;
		dc.font.descent = dc.font.xfont->descent;
	}
	dc.font.height = dc.font.ascent + dc.font.descent;
}

static void
scan(void) {
	unsigned int i, num;
	Window *wins, d1, d2;
	XWindowAttributes wa;

	wins = NULL;
	if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
		for(i = 0; i < num; i++) {
			if(!XGetWindowAttributes(dpy, wins[i], &wa)
			|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
				continue;
			if(wa.map_state == IsViewable)
				manage(wins[i], &wa);
		}
	}
	if(wins)
		XFree(wins);
}

static void
setup(void) {
	int i, j;
	unsigned int mask;
	Window w;
	XModifierKeymap *modmap;
	XSetWindowAttributes wa;

	/* init atoms */
	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
	wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
	XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
			PropModeReplace, (unsigned char *) netatom, NetLast);
	/* init cursors */
	cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
	cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
	cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
	/* init modifier map */
	numlockmask = 0;
	modmap = XGetModifierMapping(dpy);
	for (i = 0; i < 8; i++)
		for (j = 0; j < modmap->max_keypermod; j++) {
			if(modmap->modifiermap[i * modmap->max_keypermod + j]
					== XKeysymToKeycode(dpy, XK_Num_Lock))
				numlockmask = (1 << i);
		}
	XFreeModifiermap(modmap);
	/* select for events */
	wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
		| EnterWindowMask | LeaveWindowMask;
	wa.cursor = cursor[CurNormal];
	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
	grabkeys();
	compileregs();
	for(ntags = 0; tags[ntags]; ntags++);
	seltag = emallocz(sizeof(Bool) * ntags);
	seltag[0] = True;
	/* style */
	dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
	dc.norm[ColBG] = initcolor(NORMBGCOLOR);
	dc.norm[ColFG] = initcolor(NORMFGCOLOR);
	dc.sel[ColBorder] = initcolor