OK, turing.

<- leave blank

Thu Sep 29 23:47:21 EDT 2022

diff 3a47c8bfbe9298fed2b6dcc5ac19a7af4c96c52f uncommitted
--- a//sys/src/cmd/ktrans/main.c
+++ b//sys/src/cmd/ktrans/main.c
@@ -2,6 +2,7 @@
 #include <libc.h>
 #include <ctype.h>
 #include <bio.h>
+#include <plumb.h>
 #include <thread.h>
 #include "hash.h"

@@ -198,7 +199,6 @@
 };

 int deflang;
-
 Hmap *natural;
 Hmap *hira, *kata, *jisho;
 Hmap *cyril;
@@ -275,32 +275,28 @@
	return hmapget(*h, s, m);
 }

-typedef struct Msg Msg;
-struct Msg {
- char code;
- char buf[64];
-};
-static Channel *dictch;
-static Channel *output;
-static Channel *input;
-static char backspace[64];
+enum { Msgsize = 64 };
+static Channel *dictch;
+static Channel *output;
+static Channel *input;
+static char backspace[Msgsize];

 static int
 emitutf(Channel *out, char *u, int nrune)
 {
- Msg m;
+ char b[Msgsize];
	char *e;

- m.code = 'c';
- e = pushutf(m.buf, m.buf + sizeof m.buf, u, nrune);
- send(out, &m);
- return e - m.buf;
+ b[0] = 'c';
+ e = pushutf(b+1, b + Msgsize - 1, u, nrune);
+ send(out, b);
+ return e - b;
 }

 static void
 dictthread(void*)
 {
- Msg m;
+ char m[Msgsize];
	Rune r;
	int n;
	char *p;
@@ -325,8 +321,8 @@
	resetstr(&last, &line, &okuri, nil);

	threadsetname("dict");
- while(recv(dictch, &m) != -1){
- for(p = m.buf; *p; p += n){
+ while(recv(dictch, m) != -1){
+ for(p = m+1; *p; p += n){
			n = chartorune(&r, p);
			if(r != ''){
				if(selected >= 0){
@@ -441,7 +437,7 @@
	}
 }

-int
+static int
 telexlkup(Str *line, Str *out)
 {
	Map lkup;
@@ -480,7 +476,7 @@
 keythread(void*)
 {
	int lang;
- Msg m;
+ char m[Msgsize];
	Map lkup;
	char *p;
	int n, ln, rn;
@@ -487,9 +483,7 @@
	Rune r;
	char peek[UTFmax+1];
	Str line, tbuf;
- int mode;

- mode = 0;
	peek[0] = lang = deflang;
	resetstr(&line, nil);
	if(lang == LangJP || lang == LangZH)
@@ -496,18 +490,18 @@
		emitutf(dictch, peek, 1);

	threadsetname("keytrans");
- while(recv(input, &m) != -1){
- if(m.code == 'z'){
+ while(recv(input, m) != -1){
+ if(m[0] == 'z'){
			emitutf(dictch, "", 1);
			resetstr(&line, nil);
			continue;
		}
- if(m.code != 'c'){
- send(output, &m);
+ if(m[0] != 'c'){
+ send(output, m);
			continue;
		}

- for(p = m.buf; *p; p += n){
+ for(p = m+1; *p; p += n){
			n = chartorune(&r, p);
			if(checklang(&lang, r)){
				emitutf(dictch, "", 1);
@@ -527,12 +521,8 @@
					resetstr(&line, nil);
					continue;
				}
- if(lang == LangJP && isupper(*p)){
+ if(lang == LangJP && isupper(*p))
					*p = tolower(*p);
- mode++;
- } else {
- mode = 0;
- }
			}

			emitutf(output, p, 1);
@@ -590,10 +580,10 @@
 static int kbdin;
 static int kbdout;

-void
+static void
 kbdtap(void*)
 {
- Msg msg;
+ char m[Msgsize];
	char buf[128];
	char *p, *e;
	int n;
@@ -600,14 +590,12 @@

	threadsetname("kbdtap");
	for(;;){
-Drop:
+ Drop:
		n = read(kbdin, buf, sizeof buf);
		if(n < 0)
			break;
		for(p = buf; p < buf+n;){
- msg.code = p[0];
- p++;
- switch(msg.code){
+ switch(*p){
			case 'c': case 'k': case 'K':
			case 'z':
				break;
@@ -614,38 +602,73 @@
			default:
				goto Drop;
			}
- e = utfecpy(msg.buf, msg.buf + sizeof msg.buf, p);
- p += e - msg.buf;
+ *m = *p++;
+ e = utfecpy(m + 1, m + Msgsize - 1, p);
+ p += e - m;
			p++;
- if(send(input, &msg) == -1)
+ if(send(input, m) == -1)
				return;
		}
	}
 }

-void
+static void
 kbdsink(void*)
 {
- Msg m;
+ char in[Msgsize];
+ char out[Msgsize];
	char *p;
+ int n;
	Rune rn;

+ out[0] = 'c';
	threadsetname("kbdsink");
- while(recv(output, &m) != -1){
- if(m.code != 'c'){
- fprint(kbdout, "%c%s", m.code, m.buf);
+ while(recv(output, in) != -1){
+ if(in[0] != 'c'){
+ if(write(kbdout, in, strlen(in)+1) < 0)
+ break;
			continue;
		}
- p = m.buf;
- for(;;){
- p += chartorune(&rn, p);
+
+ for(p = in+1; *p; p += n){
+ n = chartorune(&rn, p);
			if(rn == Runeerror || rn == '\0')
				break;
- fprint(kbdout, "c%C", rn);
+ memmove(out+1, p, n);
+ out[1+n] = '\0';
+ if(write(kbdout, out, 1+n+1) < 0)
+ break;
		}
	}
 }

+static int plumbfd;
+
+static void
+plumbproc(void*)
+{
+ char m[Msgsize];
+ Plumbmsg *p;
+
+ threadsetname("plumbproc");
+ for(; p = plumbrecv(plumbfd); plumbfree(p)){
+ if(p->ndata > sizeof m - 1)
+ continue;
+ memmove(m, p->data, p->ndata);
+ m[p->ndata] = '\0';
+
+ m[1] = parselang(m);
+ if(m[1] == -1)
+ continue;
+ m[0] = 'c';
+ m[2] = '\0';
+
+ if(send(input, m) == -1)
+ break;
+ }
+ plumbfree(p);
+}
+
 void
 usage(void)
 {
@@ -704,9 +727,13 @@
	hangul = openmap("/lib/ktrans/hangul.map");
	telex = openmap("/lib/ktrans/telex.map");

- dictch = chancreate(sizeof(Msg), 0);
- input = chancreate(sizeof(Msg), 0);
- output = chancreate(sizeof(Msg), 0);
+ dictch = chancreate(Msgsize, 0);
+ input = chancreate(Msgsize, 0);
+ output = chancreate(Msgsize, 0);
+
+ plumbfd = plumbopen("lang", OREAD);
+ if(plumbfd >= 0)
+ proccreate(plumbproc, nil, mainstacksize);

	proccreate(kbdtap, nil, mainstacksize);
	proccreate(kbdsink, nil, mainstacksize);


Thu Sep 29 10:12:00 EDT 2022
03:00.0 Network controller: Broadcom Inc.  and subsidiaries BCM4331 802.11a/b/g/n
(rev 02)
	Subsystem: Apple Inc.  AirPort Extreme
	Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr-
	Stepping- SERR- FastB2B- DisINTx-
	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort-
	<TAbort- <MAbort- >SERR- <PERR- INTx-
	Latency: 0, Cache Line Size: 256 bytes
	Interrupt: pin A routed to IRQ 17
	Region 0: Memory at a0600000 (64-bit, non-prefetchable) [size=16K]
	Capabilities: <access denied>
	Kernel driver in use: wl
	Kernel modules: bcma, wl

~ ❯

Thu Sep 29 10:01:19 EDT 2022
~ ❯ lspci
00:00.0 Host bridge: Intel Corporation 2nd Generation Core Processor Family DRAM
Controller (rev 09)
00:01.0 PCI bridge: Intel Corporation Xeon E3-1200/2nd Generation Core Processor
Family PCI Express Root Port (rev 09)
00:01.1 PCI bridge: Intel Corporation Xeon E3-1200/2nd Generation Core Processor
Family PCI Express Root Port (rev 09)
00:02.0 VGA compatible controller: Intel Corporation 2nd Generation Core Processor
Family Integrated Graphics Controller (rev 09)
00:16.0 Communication controller: Intel Corporation 6 Series/C200 Series Chipset
Family MEI Controller #1 (rev 04)
00:1a.0 USB controller: Intel Corporation 6 Series/C200 Series Chipset Family USB
Universal Host Controller #5 (rev 05)
00:1a.7 USB controller: Intel Corporation 6 Series/C200 Series Chipset Family USB
Enhanced Host Controller #2 (rev 05)
00:1b.0 Audio device: Intel Corporation 6 Series/C200 Series Chipset Family High
Definition Audio Controller (rev 05)
00:1c.0 PCI bridge: Intel Corporation 6 Series/C200 Series Chipset Family PCI
Express Root Port 1 (rev b5)
00:1c.1 PCI bridge: Intel Corporation 6 Series/C200 Series Chipset Family PCI
Express Root Port 2 (rev b5)
00:1c.2 PCI bridge: Intel Corporation 6 Series/C200 Series Chipset Family PCI
Express Root Port 3 (rev b5)
00:1d.0 USB controller: Intel Corporation 6 Series/C200 Series Chipset Family USB
Universal Host Controller #1 (rev 05)
00:1d.7 USB controller: Intel Corporation 6 Series/C200 Series Chipset Family USB
Enhanced Host Controller #1 (rev 05)
00:1f.0 ISA bridge: Intel Corporation HM65 Express Chipset LPC Controller (rev 05)
00:1f.2 SATA controller: Intel Corporation 6 Series/C200 Series Chipset Family 6
port Mobile SATA AHCI Controller (rev 05)
00:1f.3 SMBus: Intel Corporation 6 Series/C200 Series Chipset Family SMBus
Controller (rev 05)
02:00.0 Ethernet controller: Broadcom Inc.  and subsidiaries NetXtreme BCM57765
Gigabit Ethernet PCIe (rev 10)
02:00.1 SD Host controller: Broadcom Inc.  and subsidiaries BCM57765/57785
SDXC/MMC Card Reader (rev 10)
03:00.0 Network controller: Broadcom Inc.  and subsidiaries BCM4331 802.11a/b/g/n
(rev 02)
04:00.0 FireWire (IEEE 1394): LSI Corporation FW643 [TrueFire] PCIe 1394b
Controller (rev 08)
05:00.0 PCI bridge: Intel Corporation CV82524 Thunderbolt Controller [Light Ridge
4C 2010]
06:00.0 PCI bridge: Intel Corporation CV82524 Thunderbolt Controller [Light Ridge
4C 2010]
06:03.0 PCI bridge: Intel Corporation CV82524 Thunderbolt Controller [Light Ridge
4C 2010]
06:04.0 PCI bridge: Intel Corporation CV82524 Thunderbolt Controller [Light Ridge
4C 2010]
06:05.0 PCI bridge: Intel Corporation CV82524 Thunderbolt Controller [Light Ridge
4C 2010]
06:06.0 PCI bridge: Intel Corporation CV82524 Thunderbolt Controller [Light Ridge
4C 2010]
07:00.0 System peripheral: Intel Corporation CV82524 Thunderbolt Controller [Light
Ridge 4C 2010]
~ ❯

Wed Sep 28 19:55:35 EDT 2022
diff 4b3a0ca15911333d92389daa47220c8742855f1f uncommitted
--- a//sys/src/cmd/bar.c
+++ b//sys/src/cmd/bar.c
@@ -200,9 +200,9 @@
	Binit(&b, 0, OREAD);
	for(;;){
		s = Brdstr(&b, '\n', 1);
- sendp(c, s ? s : strdup(""));
		if(s == nil)
			break;
+ sendp(c, s);
	}
	Bterm(&b);



Wed Sep 28 10:19:06 EDT 2022
/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved by Bram Moolenaar
 *
 * Do ":help uganda" in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

/*
 * os_plan9.c
 *
 * Plan 9 system-dependent routines.
 */

#include <signal.h>
#include <setjmp.h>
#include <fcntl.h>
#include <lib9.h>
#include <draw.h>
#include <keyboard.h>
#include <event.h>
#include "vim.h"

/* Vim defines Display.  We need it for libdraw.  */
#undef Display

/* We need to access the native Plan 9 alarm() since
 * it takes milliseconds rather than seconds.  */
extern int _ALARM(unsigned long);

enum {
    /* Text modes are in sync with term.c */
    TMODE_NORMAL = 0,
    TMODE_REVERSE = 1,
    TMODE_BOLD = 2,

    NCOLORS = 16
};

static int scr_inited;
static Point fontsize;
static Point shellsize;
static Rectangle scrollregion;
static int currow; /* TODO use a Point */
static int curcol;
static Image *cursorsave;
static Image *colortab[NCOLORS];
static Image *fgcolor;
static Image *bgcolor;
static Font *normalfont;
static Font *boldfont;
static Font *curfont;
static int curmode; /* TODO remove this, use cterm_normal_fg_color instead for
comparing */

/* Timeouts are handled using alarm() and a signal handler.
 * When an alarm happens during a syscall, the syscall is
 * interrupted.  We use setjmp() to handle control flow from
 * interrupted library functions.  */
static int interruptable;
static jmp_buf timeoutjmpbuf;

static void err9(char *msg) {
    char errs[256];
    /* TODO */
    errstr(errs, sizeof errs);
    fprintf(stderr, "%s: %s\n", msg, errs);
    exit(1);
}

/* Error handler for libdraw */
static void derr(Display*, char *msg) {
    if (interruptable && strcmp(msg, "eof on event pipe") == 0) {
	longjmp(timeoutjmpbuf, 1);
    }
    err9(msg);
}

int mch_has_wildcard(char_u *p) {
    for (; *p; mb_ptr_adv(p)) {
	if (*p == '\\' && p[1] != NUL) {
	    ++p;
	} else if (vim_strchr((char_u *)"*?[{`'$", *p) != NULL ||
		(*p == '~' && p[1] != NUL)) {
	    return TRUE;
	}
    }
    return FALSE;
}

int mch_has_exp_wildcard(char_u *p) {
    for (; *p; mb_ptr_adv(p))
    {
	if (*p == '\\' && p[1] != NUL)
	    ++p;
	else
	    if (vim_strchr((char_u *) "*?[{'", *p) != NULL)
		return TRUE;
    }
    return FALSE;
}

int mch_expandpath(garray_T *gap, char_u *pat, int flags) {
    return unix_expandpath(gap, pat, 0, flags, FALSE);
}

int mch_isdir(char_u *name) {
    struct stat st;
    if (stat((char*)name, &st) != 0) {
	return FALSE;
    }
    return S_ISDIR(st.st_mode) ? TRUE : FALSE;
}

static int executable_file(char_u *name)
{
    struct stat st;

    if (stat((char *)name, &st))
	return 0;
    return S_ISREG(st.st_mode) && mch_access((char *)name, X_OK) == 0;
}

int mch_isFullName(char_u *fname) {
    return fname[0] == '/' || fname[0] == '#';
}

int mch_can_exe(char_u *name) {
    char_u *buf;
    char_u *p, *e;
    int retval;

    /* If it's an absolute or relative path don't need to use $path.  */
    if (mch_isFullName(name) || (name[0] == '.' && (name[1] == '/'
				      || (name[1] == '.' && name[2] == '/'))))
	return executable_file(name);

    p = (char_u *)getenv("path");
    if (p == NULL || *p == NUL)
	return -1;
    buf = alloc((unsigned)(STRLEN(name) + STRLEN(p) + 2));
    if (buf == NULL)
	return -1;

    /*
     * Walk through all entries in $PATH to check if "name" exists there and
     * is an executable file.
     */
    for (;;)
    {
	e = (char_u *)strchr((char *)p, '\01');
	if (e == NULL)
	    e = p + STRLEN(p);
	if (e - p <= 1) /* empty entry means current dir */
	    STRCPY(buf, "./");
	else
	{
	    vim_strncpy(buf, p, e - p);
	    add_pathsep(buf);
	}
	STRCAT(buf, name);
	retval = executable_file(buf);
	if (retval == 1)
	    break;

	if (*e != '\01')
	    break;
	p = e + 1;
    }

    vim_free(buf);
    return retval;
}

int mch_dirname(char_u *buf, int len) {
    return (getcwd((char*)buf, len) ? OK : FAIL);
}

long mch_getperm(char_u *name) {
    struct stat st;
    if (stat((char*)name, &st) < 0) {
	return -1;
    }
    return st.st_mode;
}

int mch_setperm(char_u *name, long perm) {
    return chmod((char*)name, (mode_t)perm) == 0 ? OK : FAIL;
}

int mch_remove(char_u *name) {
    return remove((char*)name);
}

void mch_hide(char_u*) {
    /* Do nothing */
}

/* getuser() and sysname() can be implemented using this.  */
static int read_value_from_file(char *fname, char_u *s, int len) {
    int fd;
    int n;
    fd = open(fname, O_RDONLY);
    if (fd < 0) {
	vim_strncpy(s, (char_u*)"none", len - 1);
	return FAIL;
    }
    n = read(fd, s, len - 1);
    if (n <= 0) {
	vim_strncpy(s, (char_u*)"none", len - 1);
    } else {
	s[n] = '\0';
    }
    close(fd);
    return (n > 0) ? OK : FAIL;
}

int mch_get_user_name(char_u *s, int len) {
    return read_value_from_file("/dev/user", s, len);
}

void mch_get_host_name(char_u *s, int len) {
    read_value_from_file("/dev/sysname", s, len);
}

void mch_settmode(int) {
    /* Do nothing */
}

int mch_screenmode(char_u*) {
    /* Always fail */
    EMSG(_(e_screenmode));
    return FAIL;
}

static void scr_stamp_cursor(void) {
    struct{
	Rectangle r;
	Point p;
    }drawdat={
	Rect(0,0, fontsize.x, fontsize.y),
	Pt(screen->clipr.min.x + curcol * fontsize.x, screen->clipr.min.y +
	currow * fontsize.y),
    };
    struct{
	Rectangle r;
	int px;
	Image *c;
	Point p;
    }bordat={
	Rect(screen->clipr.min.x + curcol * fontsize.x,
	     screen->clipr.min.y + currow * fontsize.y,
	     screen->clipr.min.x + (curcol + 1) * fontsize.x,
	     screen->clipr.min.y + (currow + 1) * fontsize.y),
	2,
	colortab[cterm_normal_fg_color - 1],
	ZP,
    };
    if (cursorsave){
	draw(cursorsave, drawdat.r, screen, nil, drawdat.p);
	border(screen, bordat.r, bordat.px, bordat.c, bordat.p);
    }

// if (cursorsave) {
// draw(cursorsave, Rect(0, 0, fontsize.x, fontsize.y),
// screen, nil, Pt(screen->clipr.min.x + curcol * fontsize.x,
screen->clipr.min.y + currow * fontsize.y));
// border(screen, Rect(screen->clipr.min.x + curcol * fontsize.x,
screen->clipr.min.y + currow * fontsize.y, screen->clipr.min.x + (curcol +
1) * fontsize.x, screen->clipr.min.y + (currow + 1) * fontsize.y),
// 2, colortab[cterm_normal_fg_color - 1], ZP);
// }
}

static void scr_unstamp_cursor(void) {
    if (cursorsave) {
	Rectangle r;
	r = Rect(screen->clipr.min.x + curcol * fontsize.x,
		screen->clipr.min.y + currow * fontsize.y,
		screen->clipr.min.x + (curcol + 1) * fontsize.x,
		screen->clipr.min.y + (currow + 1) * fontsize.y);
	draw(screen, r, cursorsave, nil, ZP);
    }
}

static void scr_pos(int row, int col) {
    currow = row;
    curcol = col;
}

static void scr_clear(void) {
    scr_pos(0, 0);
    draw(screen, screen->clipr, bgcolor, nil, ZP);
    draw(cursorsave, Rect(0, 0, fontsize.x, fontsize.y), bgcolor, nil, ZP);
}

static void scr_scroll_down(int nlines) {
    Rectangle r;
    Point pt;

    /* Copy up visible part of scroll region */
    r = Rect(screen->clipr.min.x + scrollregion.min.x * fontsize.x,
	    screen->clipr.min.y + scrollregion.min.y * fontsize.y,
	    screen->clipr.min.x + scrollregion.max.x * fontsize.x,
	    screen->clipr.min.y + (scrollregion.max.y - nlines) * fontsize.y);
    pt = Pt(screen->clipr.min.x + scrollregion.min.x * fontsize.x,
	    screen->clipr.min.y + (scrollregion.min.y + nlines) * fontsize.y);
    draw(screen, r, screen, nil, pt);

    /* Clear newly exposed part of scroll region */
    r.min.y = r.max.y;
    r.max.y = screen->clipr.min.y + scrollregion.max.y * fontsize.y;
    draw(screen, r, bgcolor, nil, ZP);
}

static void scr_scroll_up(int nlines) {
    Rectangle r;
    Point pt;

    /* Copy down visible part of scroll region */
    r = Rect(screen->clipr.min.x + scrollregion.min.x * fontsize.x,
	    screen->clipr.min.y + (scrollregion.min.y + nlines) * fontsize.y,
	    screen->clipr.min.x + scrollregion.max.x * fontsize.x,
	    screen->clipr.min.y + scrollregion.max.y * fontsize.y);
    pt = Pt(screen->clipr.min.x + scrollregion.min.x * fontsize.x,
	    screen->clipr.min.y + scrollregion.min.y * fontsize.y);
    draw(screen, r, screen, nil, pt);

    /* Clear newly exposed part of scroll region */
    r.max.y = r.min.y;
    r.min.y = screen->clipr.min.y + scrollregion.min.y * fontsize.y;
    draw(screen, r, bgcolor, nil, ZP);
}

static void scr_set_color(Image **cp, int c) {
    if (c >= 0 && c < NCOLORS) {
	*cp = colortab[c];
    }
}

void mch_set_normal_colors(void) {
    char_u *p;
    int n;

    if (cterm_normal_fg_color == 0) {
	cterm_normal_fg_color = 1;
    }
    if (cterm_normal_bg_color == 0) {
	cterm_normal_bg_color = 16;
    }
    if (T_ME[0] == ESC && T_ME[1] == '|')
    {
	p = T_ME + 2;
	n = getdigits(&p);
	if (*p == 'm' && n > 0)
	{
	    cterm_normal_fg_color = (n & 0xf) + 1;
	    cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
	}
    }

    scr_set_color(&bgcolor, cterm_normal_bg_color - 1);
    scr_set_color(&fgcolor, cterm_normal_fg_color - 1);
}

static void scr_tmode(int mode) {
    Image *tmp;
    if ((curmode & TMODE_REVERSE) != (mode & TMODE_REVERSE)) {
	tmp = fgcolor;
	fgcolor = bgcolor;
	bgcolor = tmp;
    }
    if (mode & TMODE_BOLD) {
	curfont = boldfont;
    } else {
	curfont = normalfont;
    }
    if (mode == TMODE_NORMAL) {
	scr_set_color(&bgcolor, cterm_normal_bg_color - 1);
	scr_set_color(&fgcolor, cterm_normal_fg_color - 1);
    }
    curmode = mode;
}

/* Read a number and return bytes consumed.  */
static int scr_escape_number(char *p, int len, int *n) {
    int num;
    int chlen;
    if (len == 0) {
	return -1;
    }
    num = 0;
    chlen = 0;
    while (len && isdigit(*p)) {
	num = num * 10 + (*p - '0');
	p++;
	len--;
	chlen++;
    }
    *n = num;
    return chlen;
}

/* Handle escape sequence and return number of bytes consumed.  */
static int scr_escape_sequence(char *p, int len) {
    int nlines;
    int n1;
    int n2;
    int i;
    int chlen;

    if (len == 0) {
	return 0;
    }

    chlen = 1;
    switch (*p) {
	case 'J': /* clear screen */
	    scr_clear();
	    break;

	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	    n1 = -1;
	    n2 = -1;

	    /* First number */
	    i = scr_escape_number(p, len, &n1);
	    if (i == -1) {
		/* TODO */
		fprintf(stderr, "scr_escape_sequence: escape at end of string\n");
		exit(1);
	    }
	    p += i;
	    len -= i;
	    chlen += i;

	    /* Optional second number */
	    if (len && *p == ';') {
		p += 1;
		len -= 1;
		chlen += 1;
		i = scr_escape_number(p, len, &n2);
		if (i == -1) {
		    /* TODO */
		    fprintf(stderr, "scr_escape_sequence: missing second
		    number\n");
		    exit(1);
		}
		p += i;
		len -= i;
		chlen += i;
	    }

	    if (len == 0) {
		/* TODO */
		fprintf(stderr, "scr_escape_sequence: early end of escape
		sequence\n");
		exit(1);
	    }

	    switch (*p) {
		case 'b': /* background color */
		    scr_set_color(&bgcolor, n1);
		    break;

		case 'f': /* foreground color */
		    scr_set_color(&fgcolor, n1);
		    break;

		case 'H': /* cursor motion */
		    scr_pos(n1, n2);
		    break;

		case 'm': /* mode */
		    scr_tmode(n1);
		    break;

		case 'R': /* scroll region */
		    scrollregion.min.y = n1;
		    scrollregion.max.y = n2 + 1;
		    break;

		case 'V': /* scroll region vertical */
		    scrollregion.min.x = n1;
		    scrollregion.max.x = n2 + 1;
		    break;

		default:
		    /* TODO */
		    fprintf(stderr, "scr_escape_sequence: unimplemented (p=%c)\n",
		    *p);
		    exit(1);
	    }
	    break;

	case 'K': /* clear to end of line */
	    draw(screen, Rect(screen->clipr.min.x + curcol * fontsize.x,
			screen->clipr.min.y + currow * fontsize.y,
			screen->clipr.max.x,
			screen->clipr.min.y + (currow + 1) * fontsize.y),
		    bgcolor, nil, ZP);
	    break;

	case 'L': /* add new blank line */
	    p++;
	    nlines = 1;
	    while (len >= 3 && p[0] == '\x1b' && p[1] == '[' && p[2] == 'L') {
		nlines++;
		len -= 3;
		p += 3;
		chlen += 3;
	    }
	    /* TODO what if nlines >= scroll region size */
	    scr_scroll_up(nlines);
	    break;

	default:
	    /* TODO */
	    fprintf(stderr, "scr_escape_sequence: unhandled sequence (p=%c)\n",
	    *p);
	    exit(1);
    }

    return chlen;
}

/* Handle special characters.  */
static int write_special(char *p, int len) {
    int n;
    int nbytes;
    if (len == 0) {
	return 0;
    }
    nbytes = 0;
    while (len > 0) {
	switch (*p) {
	    case '\a': /* Bell */
		/* There is no bell on Plan 9.  */
		break;

	    case '\b': /* Backspace */
		if (curcol > scrollregion.min.x) {
		    scr_pos(currow, curcol - 1);
		} else if (currow > scrollregion.min.y) {
		    scr_pos(currow - 1, scrollregion.max.x - 1);
		}
		break;

	    case '\n': /* New line */
		for (n = 0; n < len && *p == '\n'; n++) {
		    p++;
		}
		if (currow + n >= scrollregion.max.y) {
		    scr_scroll_down(currow + n - scrollregion.max.y + 1);
		    scr_pos(scrollregion.max.y - 1, scrollregion.min.x);
		} else {
		    scr_pos(currow + n, scrollregion.min.x);
		}
		len -= n;
		nbytes += n;
		continue; /* process next character */

	    case '\r': /* Carriage return */
		curcol = scrollregion.min.x;
		break;

	    case '\x1b': /* Escape sequences */
		if (len > 1 && p[1] == '[') {
		    n = 2;
		    n += scr_escape_sequence(p + 2, len - 2);
		    p += n;
		    len -= n;
		    nbytes += n;
		    continue; /* process next character */
		}
		break;

	    default: /* Normal character */
		return nbytes;
	}
	p++;
	len--;
	nbytes++;
    }
    return nbytes;
}

/* Draw normal characters.  */
static int write_str(char *p, int len) {
    int nbytes;
    int n;
    int m;
    if (len == 0) {
	return 0;
    }
    for (nbytes = 0; nbytes < len &&
	    p[nbytes] != '\a' && p[nbytes] != '\b' &&
	    p[nbytes] != '\n' && p[nbytes] != '\r' &&
	    p[nbytes] != '\x1b';
	    len--, nbytes++)
	;
    n = nbytes;
    while (n > 0) {
	m = (curcol + n >= scrollregion.max.x) ?
	    scrollregion.max.x - curcol : n;
	if (m == 0) {
	    break;
	}
	stringnbg(screen, Pt(screen->clipr.min.x + curcol * fontsize.x,
		    screen->clipr.min.y + currow * fontsize.y),
		fgcolor, ZP, curfont, p, m, bgcolor, ZP);
	curcol += m;
	if (curcol == scrollregion.max.x) {
	    curcol = scrollregion.min.x;
	    if (currow == scrollregion.max.y - 1) {
		scr_scroll_down(1);
	    } else {
		currow++;
	    }
	}
	p += m;
	n -= m;
    }
    return nbytes;
}

void mch_write(char_u *p, int len) {
    int slen;
    scr_unstamp_cursor();
    while (len > 0) {
	/* Handle special characters.  */
	slen = write_special((char*)p, len);
	p += slen;
	len -= slen;

	/* Write as many normal characters as possible.  */
	slen = write_str((char*)p, len);
	p += slen;
	len -= slen;
    }
    scr_stamp_cursor();
    flushimage(display, 1);
}

static void sigalrm(int, char*, void*) {
    /* Do nothing */
}

/* Don't allow mouse events to accumulate.  */
static void drain_mouse_events(void) {
    while (ecanmouse()) {
	emouse();
    }
}

int RealWaitForChar(int, long msec, int*) {
    Rune rune;
    char utf[UTFmax];
    int len;

    drain_mouse_events();
    if (msec == 0 && !ecankbd()) {
	return 0;
    }
    if (msec > 0) {
	if (setjmp(timeoutjmpbuf)) {
	    /* We arrive here if the alarm occurred and a library
	     * function called drawerror() due to an interrupted
	     * syscall.  */
	    _ALARM(0);
	    interruptable = 0;
	    return 0;
	}
	interruptable = 1;
	_ALARM((unsigned long)msec);
    }
    /* TODO garbage collect */
    rune = ekbd();
    if (msec > 0) {
	_ALARM(0);
	interruptable = 0;
    }
    if (rune == Ctrl_C && ctrl_c_interrupts) {
	got_int = TRUE;
	return 0;
    }
    len = runetochar(utf, &rune);
    add_to_input_buf_csi((char_u*)utf, len); /* TODO escape K_SPECIAL?  */
    return len > 0;
}

int mch_inchar(char_u *buf, int maxlen, long msec, int) {
    if (vim_is_input_buf_empty() && RealWaitForChar(0, msec, NULL) == 0) {
	return 0;
    }
    return read_from_input_buf(buf, maxlen);
}

int mch_char_avail(void) {
    return ecankbd();
}

void mch_delay(long msec, int ignoreinput) {
    if (ignoreinput) {
	sleep(msec / 1000);
    } else {
	RealWaitForChar(0, msec, NULL);
    }
}

int mch_nodetype(char_u *name) {
    struct stat st;
    if (stat((char*)name, &st) < 0) {
	return NODE_NORMAL;
    }
    if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
	return NODE_NORMAL;
    }
    return NODE_WRITABLE;
}

int mch_FullName(char_u *fname, char_u *buf, int len, int force) {
    int l;
    char_u olddir[MAXPATHL];
    char_u *p;
    int retval = OK;

    /* expand it if forced or not an absolute path */
    if (force || !mch_isFullName(fname))
    {
	/*
	 * If the file name has a path, change to that directory for a moment,
	 * and then do the getwd() (and get back to where we were).
	 * This will get the correct path name with "../" things.
	 */
	if ((p = vim_strrchr(fname, '/')) != NULL)
	{
	    /* Only change directory when we are sure we can return to where
	     * we are now.  After doing "su" chdir(".") might not work.  */
	    if ( (mch_dirname(olddir, MAXPATHL) == FAIL ||
			mch_chdir((char *)olddir) != 0))
	    {
		p = NULL; /* can't get current dir: don't chdir */
		retval = FAIL;
	    }
	    else
	    {
		/* The directory is copied into buf[], to be able to remove
		 * the file name without changing it (could be a string in
		 * read-only memory) */
		if (p - fname >= len)
		    retval = FAIL;
		else
		{
		    vim_strncpy(buf, fname, p - fname);
		    if (mch_chdir((char *)buf))
			retval = FAIL;
		    else
			fname = p + 1;
		    *buf = NUL;
		}
	    }
	}
	if (mch_dirname(buf, len) == FAIL)
	{
	    retval = FAIL;
	    *buf = NUL;
	}
	if (p != NULL)
	{
	    l = mch_chdir((char *)olddir);
	    if (l != 0)
		EMSG(_(e_prev_dir));
	}

	l = STRLEN(buf);
	if (l >= len)
	    retval = FAIL;
	else
	{
	    if (l > 0 && buf[l - 1] != '/' && *fname != NUL
		    && STRCMP(fname, ".") != 0)
		STRCAT(buf, "/");
	}
    }

    /* Catch file names which are too long.  */
    if (retval == FAIL || STRLEN(buf) + STRLEN(fname) >= len)
	return FAIL;

    /* Do not append ".", "/dir/." is equal to "/dir".  */
    if (STRCMP(fname, ".") != 0)
	STRCAT(buf, fname);

    return OK;
}

long mch_get_pid(void) {
    return (long)getpid();
}

int mch_input_isatty(void) {
    return isatty(0) ? TRUE : FALSE;
}

int mch_setenv(char *var, char *value, int x) {
    char buf[100];
    int fd;
    int len;

    snprintf(buf, sizeof buf, "/env/%s", var);

    /* Check for overwrite */
    if (!x) {
	struct stat st;
	if (stat(buf, &st) == 0) {
	    return -1;
	}
    }

    /* Write the value */
    fd = creat(buf, 0666);
    if (fd < 0) {
	return -1;
    }
    len = strlen(value);
    if (write(fd, value, len) != len) {
	close(fd);
	return -1;
    }
    close(fd);
    return 0;
}

void mch_suspend(void) {
    suspend_shell();
}

static void update_shellsize(void) {
    shellsize = Pt((screen->clipr.max.x - screen->clipr.min.x) / fontsize.x,
		   (screen->clipr.max.y - screen->clipr.min.y) /
		   fontsize.y);
}

int mch_get_shellsize(void) {
    update_shellsize();
    Rows = shellsize.y;
    Columns = shellsize.x;
    scrollregion.min.x = 0;
    scrollregion.min.y = 0;
    scrollregion.max.x = shellsize.x;
    scrollregion.max.y = shellsize.y;
    return OK;
}

void mch_set_shellsize(void) {
    /* Do nothing */
}

void mch_new_shellsize(void) {
    /* Do nothing */
}

void mch_breakcheck(void) {
    if (scr_inited) {
	/* Read into input buffer and check for Ctrl-C.  */
	RealWaitForChar(0, 0, NULL);
    }
}

/* Called by libevent whenever a resize event occurs.  */
void eresized(int renew) {
    if (renew) {
	if (getwindow(display, Refnone) < 0) {
	    err9("resize failed");
	}
    }
    shell_resized();
}

static void init_colors(void) {
    int i;
    int colors[NCOLORS] = {
	/* Copied from hardcopy.c and adjusted for libdraw.  */
	0x000000ff, 0x0000c0ff, 0x008000ff, 0x004080ff,
	0xc00000ff, 0xc000c0ff, 0x808000ff, 0xc0c0c0ff,
	0x808080ff, 0x6060ffff, 0x00ff00ff, 0x00ffffff,
	0xff8080ff, 0xff40ffff, 0xffff00ff, 0xffffffff
    };
    for (i = 0; i < NCOLORS; i++) {
	colortab[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1,
	colors[i]);
	if (colortab[i] == nil) {
	    err9("allocimage failed");
	}
    }
    /* acme bg color */
    colortab[1] = allocimagemix(display, DPaleyellow, DWhite);
    mch_set_normal_colors();
}

static void free_colors(void) {
    int i;
    bgcolor = nil;
    fgcolor = nil;
    for (i = 0; i < NCOLORS; i++) {
	freeimage(colortab[i]);
	colortab[i] = nil;
    }
}

static void init_fonts(void) {
    char *f;
    char *stdfont[]={
	"/lib/font/bit/fixed/unicode.9x18.font",
	"/lib/font/bit/fixed/unicode.9x18B.font",
    };

    f = getenv("font");
    if(f == nil)
	normalfont = openfont(display, stdfont[0]);
    else
	normalfont = openfont(display, f);
    boldfont = openfont(display, f);
// boldfont = openfont(display, stdfont[1]);
    if (normalfont == nil || boldfont == nil)
	err9("openfont: %r");
    curfont = normalfont;
}

static void free_fonts(void) {
    freefont(normalfont);
    freefont(boldfont);
    curfont = nil;
}

void mch_early_init(void) {
    rfork(RFENVG | RFNOTEG);
}

int mch_check_win(int, char**) {
    return OK;
}

static void scr_init(void) {
    if (initdraw(derr, nil, "Vim") == -1) {
	err9("initdraw failed");
    }

    init_colors();
    init_fonts();
    fontsize = stringsize(curfont, "A");
    cursorsave = allocimage(display, Rect(0, 0, fontsize.x, fontsize.y),
    screen->chan, 0, DBlack);
    mch_get_shellsize();
    scr_clear();
    scr_stamp_cursor();
    flushimage(display, 1);

    /* Mouse events must be enabled to receive window resizes.  */
    einit(Emouse | Ekeyboard);
    scr_inited = TRUE;
}

void mch_init(void) {
    signal(SIGALRM, sigalrm);
    scr_init();

    /*
     * Force UTF-8 output no matter what the value of 'encoding' is.
     * did_set_string_option() in option.c prohibits changing 'termencoding'
     * to something else than UTF-8.
     */
    set_option_value((char_u *)"termencoding", 0L, (char_u *)"utf-8", 0);

#if defined(FEAT_CLIPBOARD)
    clip_init(TRUE);
#endif
}

void mch_exit(int r) {
    ml_close_all(TRUE); /* remove all memfiles */
    /* libdraw shuts down automatically on exit */
    exit(r);
}

#if defined(FEAT_TITLE)
void mch_settitle(char_u *title, char_u *) {
    int fd;
    fd = open("/dev/label", O_WRONLY);
    if (fd < 0) {
	/* Not running under rio.  */
	return;
    }
    write(fd, (char*)title, strlen((char*)title));
    close(fd);
}

void mch_restore_title(int) {
    /* No need to restore - libdraw does this automatically.  */
}

int mch_can_restore_title(void) {
    return TRUE;
}

int mch_can_restore_icon(void) {
    return FALSE;
}
#endif

#if defined(FEAT_CLIPBOARD)
int clip_mch_own_selection(VimClipboard*) {
    /* We never own the clipboard.  */
    return FAIL;
}

void clip_mch_lose_selection(VimClipboard*) {
    /* Do nothing */
}

void clip_mch_request_selection(VimClipboard *cbd) {
    int fd;
    char_u *data;
    char_u *newdata;
    long_u len; /* used length */
    long_u mlen; /* max allocated length */
    ssize_t n;
    int type;
    fd = open("/dev/snarf", O_RDONLY);
    if (fd < 0) {
	/* Not running under rio.  */
	return;
    }
    n = 0;
    len = 0;
    mlen = 0;
    data = NULL;
    do {
	len += n;
	mlen += 4096;
	newdata = vim_realloc(data, mlen);
	if (!newdata) {
	    n = -1;
	    break;
	}
	data = newdata;
	n = read(fd, data, 4096);
	/* TODO breakcheck?  */
    } while (n == 4096);
    if (n >= 0) {
	len += n;
	type = memchr(data, '\n', len) ? MLINE : MCHAR;
	clip_yank_selection(type, data, len, cbd);
    }
    vim_free(data);
    close(fd);
}

void clip_mch_set_selection(VimClipboard *cbd) {
    char_u *data;
    long_u len;
    int type;
    int fd;

    /* If the '*' register isn't already filled in, fill it in now.  */
    cbd->owned = TRUE;
    clip_get_selection(cbd);
    cbd->owned = FALSE;

    type = clip_convert_selection(&data, &len, cbd);
    if (type < 0) {
	return;
    }
    fd = open("/dev/snarf", O_WRONLY);
    if (fd >= 0) {
	write(fd, data, len);
	close(fd);
    }
    vim_free(data);
}
#endif

#if defined(FEAT_MOUSE)
void mch_setmouse(int) {
    /* Do nothing.  Mouse needed for clipboard.  */
}
#endif

static pid_t
forkwin(int hide){
	char spec[256];
	char *wsys;
	int wfd;
	pid_t pid;

	/* Fork child.  */
	pid = fork();
	if(pid != 0)
	    return pid;

	/* Mounting the window system creates a new window.  */
	wsys = getenv("wsys");
	if(!wsys){
		fprintf(stderr, "wsys not set\n");
		exit(1);
	}

	wfd = open(wsys, O_RDWR);
	if(wfd < 0){
		fprintf(stderr, "unable to open \"%s\"\n", wsys);
		exit(1);
	}


	/* We need a separate namespace from parent process.  */
	if(rfork(RFPROC|RFCNAMEG)){
	    printf("test");
	}

	snprintf(spec, sizeof spec, "new -pid %d -scroll %s", getpid(), hide ?
	"-hide" : "");
	if(mount(wfd, -1, "/mnt/wsys", MREPL, spec) < 0){
		fprintf(stderr, "unable to mount\n");
		exit(1);
	}
	if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
		fprintf(stderr, "unable to bind\n");
		exit(1);
	}

	/* Now reopen standard input, output, and error.  */
	freopen("/dev/cons", "r", stdin);
	setbuf(stdin, NULL);
	freopen("/dev/cons", "w", stdout);
	setbuf(stdout, NULL);
	freopen("/dev/cons", "w", stderr);
	setbuf(stderr, NULL);

	return pid;
}

/* new function - no idea wtf is going on here, but it works */
int
mch_call_shell(char_u *cmd, int options)
{
    char ch;
    pid_t pid;
    int status;
    int hide;
    int fd[2];

    pid = fork();
    if(pid < 0)
	return -1;
    if(pid == 0){
	if (cmd)
	    execl("/bin/rc", "rc", "-c", cmd, NULL);
	 else
	    execl("/bin/rc", "rc", NULL);

	exit(122);
    }
    waitpid(pid, &status, 0);
    return status;
}

/* old function - dunno wtf is going here */
int mch2_call_shell(char_u *cmd, int options) {
    char ch;
    pid_t pid;
    int status;
    int hide;

    USED(ch);
    USED(pid);
    USED(status);
    USED(hide);

    /* Non-interactive commands run in a hidden window.  */
    hide = options & SHELL_FILTER || options & SHELL_DOOUT;
    pid = forkwin(hide);
    if (pid == -1) {
	MSG_PUTS(_("\nCannot fork\n"));
	return -1;
    }
    if (pid == 0) {
	/* Fork again so that we can prompt the user to
	 * press a key before the window closes.  */
	pid = fork();
	if (pid == -1) {
	    exit(255);
	}
	if (pid == 0) {
	    if (cmd) {
		execl("/bin/rc", "rc", "-c", cmd, NULL);
	    } else {
		execl("/bin/rc", "rc", NULL);
	    }
	    exit(122); /* same as on UNIX Vim */
	} else {
	    waitpid(pid, &status, 0);
	    unmount(0, "/dev/");
	    if (!hide) {
		printf("\nPress RETURN to close this window...");
		read(0, &ch, 1);
	    }
	    exit(status);
	}
    }
    waitpid(pid, &status, 0);
    return status;
}


Tue Sep 27 20:35:04 EDT 2022
diff bed7f3f1a31946d417822613d01094ace514c73f uncommitted
--- a//sys/src/cmd/ktrans/main.c
+++ b//sys/src/cmd/ktrans/main.c
@@ -497,7 +497,7 @@

	threadsetname("keytrans");
	while(recv(input, &m) != -1){
- if(m.code == 'r'){
+ if(m.code == 'z'){
			emitutf(dictch, "", 1);
			resetstr(&line, nil);
			continue;
@@ -609,7 +609,7 @@
			p++;
			switch(msg.code){
			case 'c': case 'k': case 'K':
- case 'r':
+ case 'z':
				break;
			default:
				goto Drop;
@@ -684,6 +684,8 @@
	default:
		usage();
	}
+ if(write(kbdout, "z", 2) < 2)
+ sysfatal("failed to enable focus watch: %r");

	memset(backspace, '\b', sizeof backspace-1);
	backspace[sizeof backspace-1] = '\0';
--- a//sys/src/cmd/rio/dat.h
+++ b//sys/src/cmd/rio/dat.h
@@ -318,11 +318,14 @@
 enum{
	Tapon = 'b',
	Tapoff = 'e',
+ Tapwatch = 'z',
 };
-Channel *ctltap; /* on/off messages */
+Channel *ctltap; /* open/close */
+Channel *resptap; /* open/close err */
 Channel *fromtap; /* input from kbd tap program to window */
 Channel *totap; /* our keyboard input to tap program */
 Channel *wintap; /* tell the tapthread which Window to send to */
+
 Window *input;
 QLock all; /* BUG */
 Filsys *filsys;
--- a//sys/src/cmd/rio/rio.c
+++ b//sys/src/cmd/rio/rio.c
@@ -199,7 +199,8 @@
	totap = chancreate(sizeof(char*), 32);
	fromtap = chancreate(sizeof(char*), 32);
	wintap = chancreate(sizeof(Window*), 0);
- ctltap = chancreate(sizeof(Window*), 0);
+ ctltap = chancreate(sizeof(char*), 0);
+ resptap = chancreate(sizeof(char*), 0);
	proccreate(keyboardtap, nil, STACK);

	wscreen = allocscreen(screen, background, 0);
@@ -350,17 +351,53 @@
			write(window[i]->notefd, "hangup", 6);
 }

+static int tapseats[] = { [OREAD] Tapoff, [OWRITE] Tapoff };
+
+char*
+tapctlmsg(char *msg)
+{
+ int perm;
+
+ perm = msg[1];
+ switch(msg[0]){
+ case Tapwatch:
+ if(tapseats[OREAD] == Tapoff)
+ return "no active reader";
+ tapseats[OREAD] = Tapwatch;
+ break;
+ case Tapoff:
+ if(perm == ORDWR)
+ tapseats[OREAD] = Tapoff, tapseats[OWRITE] = Tapoff;
+ else
+ tapseats[perm] = Tapoff;
+ break;
+ case Tapon:
+ switch(perm){
+ case ORDWR:
+ if(tapseats[OREAD] != Tapoff || tapseats[OWRITE] != Tapoff)
+ return "seat taken";
+ tapseats[OREAD] = Tapon, tapseats[OWRITE] = Tapon;
+ break;
+ case OREAD: case OWRITE:
+ if(tapseats[perm] != Tapoff)
+ return "seat taken";
+ tapseats[perm] = Tapon;
+ break;
+ }
+ break;
+ }
+ return nil;
+}
+
 void
 keyboardtap(void*)
 {
	char *s, *ctl;
+ char *watched;
	Window *w, *cur;
- int mode;
- enum { Awin, Actl, Afrom, Adev, Ato, Ainp, NALT };
- enum { Mnorm, Mtap };

- threadsetname("keyboardtap");
-
+ threadsetname("keyboardtap");
+ enum { Awin, Actl, Afrom, Adev, Ato, Ainp, Awatch, NALT };
	static Alt alts[NALT+1];
	/* ctl */
	alts[Awin].c = wintap;
@@ -383,10 +420,13 @@
	alts[Ainp].c = nil;
	alts[Ainp].v = &s;
	alts[Ainp].op = CHANNOP;
+ alts[Awatch].c = totap;
+ alts[Awatch].v = &watched;
+ alts[Awatch].op = CHANNOP;
	alts[NALT].op = CHANEND;

	cur = nil;
- mode = Mnorm;
+ watched = nil;
	for(;;)
		switch(alt(alts)){
		case Awin:
@@ -393,20 +433,18 @@
			cur = w;
			if(cur != nil){
				alts[Ainp].c = cur->ck;
- break;
+ if(tapseats[OREAD] != Tapwatch)
+ break;
+ if(alts[Awatch].op == CHANSND)
+ free(watched);
+ watched = smprint("z%d", cur->id);
+ alts[Awatch].op = CHANSND;
			}
			if(alts[Ainp].op != CHANNOP || alts[Ato].op != CHANNOP)
				free(s);
			goto Reset;
		case Actl:
- switch(*ctl){
- case Tapon:
- mode = Mtap;
- break;
- case Tapoff:
- mode = Mnorm;
- break;
- }
+ sendp(resptap, tapctlmsg(ctl));
			free(ctl);
			break;
		case Afrom:
@@ -420,16 +458,19 @@
			alts[Ainp].op = CHANSND;
			break;
		case Adev:
- if(mode == Mnorm && cur == nil){
+ if(tapseats[OWRITE] == Tapoff && cur == nil){
				free(s);
				break;
			}
			alts[Afrom].op = CHANNOP;
			alts[Adev].op = CHANNOP;
- if(mode == Mnorm)
+ if(tapseats[OWRITE] == Tapoff)
				alts[Ainp].op = CHANSND;
			else
				alts[Ato].op = CHANSND;
+ break;
+ case Awatch:
+ alts[Awatch].op = CHANNOP;
			break;
		case Ainp:
			if(*s == 'k' || *s == 'K')
--- a//sys/src/cmd/rio/xfid.c
+++ b//sys/src/cmd/rio/xfid.c
@@ -247,6 +247,7 @@
 {
	Fcall t;
	Window *w;
+ char *s;

	w = x->f->w;
	if(w != nil && w->deleted){
@@ -312,8 +313,12 @@
		}
		break;
	case Qtap:
- chanprint(ctltap, "%c", Tapon);
- break;
+ chanprint(ctltap, "%c%c", Tapon, x->mode);
+ s = recvp(resptap);
+ if(s == nil)
+ break;
+ filsysrespond(x->fs, x, &t, s);
+ return;
	}
	t.qid = x->f->qid;
	t.iounit = messagesize-IOHDRSZ;
@@ -369,7 +374,8 @@
			w->wctlopen = FALSE;
		break;
	case Qtap:
- chanprint(ctltap, "%c", Tapoff);
+ chanprint(ctltap, "%c%c", Tapoff, x->f->mode);
+ recvp(resptap);
		break;
	}
	if(w)
@@ -382,7 +388,7 @@
 {
	Fcall fc;
	int cnt, qid, nb, nr;
- char err[ERRMAX], *p, *e;
+ char err[ERRMAX], *p, *e, *s;
	Point pt;
	Window *w;
	Rune *r;
@@ -581,12 +587,22 @@
		}
		e = x->data + cnt;
		for(p = x->data; p < e; p += strlen(p)+1){
- if(*p == '\0'){
+ switch(*p){
+ case '\0':
				fc.count = p - x->data;
				filsysrespond(x->fs, x, &fc, "null message
				type");
				return;
+ case Tapwatch:
+ chanprint(ctltap, "%s", p);
+ s = recvp(resptap);
+ if(s == nil)
+ break;
+ filsysrespond(x->fs, x, &fc, s);
+ return;
+ default:
+ chanprint(fromtap, "%s", p);
+ break;
			}
- chanprint(fromtap, "%s", p);
		}
		break;



Tue Sep 27 17:54:51 EDT 2022
#pragma once

#define T 1
#define NIL ((void *)0)

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long addr;
typedef unsigned int flag;

typedef struct { void *car, *cdr; } Cons;
typedef struct { addr size, flag used; } Mblk;

enum { FREE = 0, USED = 1 };


Tue Sep 27 09:00:59 EDT 2022
diff bed7f3f1a31946d417822613d01094ace514c73f uncommitted
--- a//sys/src/cmd/ktrans/main.c
+++ b//sys/src/cmd/ktrans/main.c
@@ -497,7 +497,7 @@

	threadsetname("keytrans");
	while(recv(input, &m) != -1){
- if(m.code == 'r'){
+ if(m.code == 'z'){
			emitutf(dictch, "", 1);
			resetstr(&line, nil);
			continue;
@@ -609,7 +609,7 @@
			p++;
			switch(msg.code){
			case 'c': case 'k': case 'K':
- case 'r':
+ case 'z':
				break;
			default:
				goto Drop;
@@ -684,6 +684,8 @@
	default:
		usage();
	}
+ if(write(kbdout, "z", 2) < 2)
+ sysfatal("failed to enable focus watch: %r");

	memset(backspace, '\b', sizeof backspace-1);
	backspace[sizeof backspace-1] = '\0';
--- a/sys/src/cmd/rio/dat.h
+++ b/sys/src/cmd/rio/dat.h
@@ -318,6 +318,7 @@
 enum{
	Tapon = 'b',
	Tapoff = 'e',
+ Tapwatch = 'z',
 };
 Channel *ctltap; /* on/off messages */
 Channel *fromtap; /* input from kbd tap program to window */
--- a/sys/src/cmd/rio/rio.c
+++ b/sys/src/cmd/rio/rio.c
@@ -199,7 +199,7 @@
	totap = chancreate(sizeof(char*), 32);
	fromtap = chancreate(sizeof(char*), 32);
	wintap = chancreate(sizeof(Window*), 0);
- ctltap = chancreate(sizeof(Window*), 0);
+ ctltap = chancreate(sizeof(char*), 0);
	proccreate(keyboardtap, nil, STACK);

	wscreen = allocscreen(screen, background, 0);
@@ -354,13 +354,13 @@
 keyboardtap(void*)
 {
	char *s, *ctl;
+ char *watched;
	Window *w, *cur;
- int mode;
- enum { Awin, Actl, Afrom, Adev, Ato, Ainp, NALT };
- enum { Mnorm, Mtap };
+ int mode, perm;
+ int seats[] = { [OREAD] 0, [OWRITE] 0 };

- threadsetname("keyboardtap");
-
+ threadsetname("keyboardtap");
+ enum { Awin, Actl, Afrom, Adev, Ato, Ainp, Awatch, NALT };
	static Alt alts[NALT+1];
	/* ctl */
	alts[Awin].c = wintap;
@@ -383,10 +383,14 @@
	alts[Ainp].c = nil;
	alts[Ainp].v = &s;
	alts[Ainp].op = CHANNOP;
+ alts[Awatch].c = totap;
+ alts[Awatch].v = &watched;
+ alts[Awatch].op = CHANNOP;
	alts[NALT].op = CHANEND;

	cur = nil;
- mode = Mnorm;
+ mode = Tapoff;
+ watched = nil;
	for(;;)
		switch(alt(alts)){
		case Awin:
@@ -393,21 +397,49 @@
			cur = w;
			if(cur != nil){
				alts[Ainp].c = cur->ck;
- break;
+ if(mode != Tapwatch)
+ break;
+ if(alts[Awatch].op == CHANSND)
+ free(watched);
+ watched = smprint("z%d", cur->id);
+ alts[Awatch].op = CHANSND;
			}
			if(alts[Ainp].op != CHANNOP || alts[Ato].op != CHANNOP)
				free(s);
			goto Reset;
		case Actl:
- switch(*ctl){
- case Tapon:
- mode = Mtap;
+ mode = ctl[0];
+ if(mode == Tapwatch){
+ free(ctl);
				break;
- case Tapoff:
- mode = Mnorm;
- break;
			}
+ perm = ctl[1];
			free(ctl);
+ if(mode == Tapoff){
+ if(perm == ORDWR)
+ memset(seats, 0, sizeof seats);
+ else
+ seats[perm] = 0;
+ break;
+ }
+ switch(perm){
+ case ORDWR:
+ if(seats[OREAD] != 0 || seats[OWRITE] != 0)
+ sendp(ctltap, "seat taken");
+ else {
+ seats[OREAD] = 1, seats[OWRITE] = 1;
+ sendp(ctltap, nil);
+ }
+ break;
+ case OREAD: case OWRITE:
+ if(seats[perm] != 0)
+ sendp(ctltap, "seat taken");
+ else {
+ seats[perm] = 1;
+ sendp(ctltap, nil);
+ }
+ break;
+ }
			break;
		case Afrom:
			if(cur == nil){
@@ -420,16 +452,19 @@
			alts[Ainp].op = CHANSND;
			break;
		case Adev:
- if(mode == Mnorm && cur == nil){
+ if(mode == Tapoff && cur == nil){
				free(s);
				break;
			}
			alts[Afrom].op = CHANNOP;
			alts[Adev].op = CHANNOP;
- if(mode == Mnorm)
+ if(mode == Tapoff)
				alts[Ainp].op = CHANSND;
			else
				alts[Ato].op = CHANSND;
+ break;
+ case Awatch:
+ alts[Awatch].op = CHANNOP;
			break;
		case Ainp:
			if(*s == 'k' || *s == 'K')
--- a/sys/src/cmd/rio/xfid.c
+++ b/sys/src/cmd/rio/xfid.c
@@ -247,6 +247,7 @@
 {
	Fcall t;
	Window *w;
+ char *s;

	w = x->f->w;
	if(w != nil && w->deleted){
@@ -312,8 +313,12 @@
		}
		break;
	case Qtap:
- chanprint(ctltap, "%c", Tapon);
- break;
+ chanprint(ctltap, "%c%c", Tapon, x->mode);
+ s = recvp(ctltap);
+ if(s == nil)
+ break;
+ filsysrespond(x->fs, x, &t, s);
+ return;
	}
	t.qid = x->f->qid;
	t.iounit = messagesize-IOHDRSZ;
@@ -369,7 +374,7 @@
			w->wctlopen = FALSE;
		break;
	case Qtap:
- chanprint(ctltap, "%c", Tapoff);
+ chanprint(ctltap, "%c%c", Tapoff, x->f->mode);
		break;
	}
	if(w)
@@ -581,12 +586,18 @@
		}
		e = x->data + cnt;
		for(p = x->data; p < e; p += strlen(p)+1){
- if(*p == '\0'){
+ switch(*p){
+ case '\0':
				fc.count = p - x->data;
				filsysrespond(x->fs, x, &fc, "null message
				type");
				return;
+ case Tapwatch:
+ chanprint(ctltap, "%s", p);
+ break;
+ default:
+ chanprint(fromtap, "%s", p);
+ break;
			}
- chanprint(fromtap, "%s", p);
		}
		break;



Mon Sep 26 23:57:50 EDT 2022
diff bed7f3f1a31946d417822613d01094ace514c73f uncommitted
--- a//sys/src/cmd/ktrans/main.c
+++ b//sys/src/cmd/ktrans/main.c
@@ -497,7 +497,7 @@

	threadsetname("keytrans");
	while(recv(input, &m) != -1){
- if(m.code == 'r'){
+ if(m.code == 'z'){
			emitutf(dictch, "", 1);
			resetstr(&line, nil);
			continue;
@@ -609,7 +609,7 @@
			p++;
			switch(msg.code){
			case 'c': case 'k': case 'K':
- case 'r':
+ case 'z':
				break;
			default:
				goto Drop;
@@ -684,6 +684,8 @@
	default:
		usage();
	}
+ if(write(kbdout, "z", 2) < 2)
+ sysfatal("failed to enable focus watch: %r");

	memset(backspace, '\b', sizeof backspace-1);
	backspace[sizeof backspace-1] = '\0';
--- a//sys/src/cmd/rio/dat.h
+++ b//sys/src/cmd/rio/dat.h
@@ -318,6 +318,7 @@
 enum{
	Tapon = 'b',
	Tapoff = 'e',
+ Tapwatch = 'z',
 };
 Channel *ctltap; /* on/off messages */
 Channel *fromtap; /* input from kbd tap program to window */
--- a//sys/src/cmd/rio/rio.c
+++ b//sys/src/cmd/rio/rio.c
@@ -354,10 +354,10 @@
 keyboardtap(void*)
 {
	char *s, *ctl;
+ char *watched;
	Window *w, *cur;
	int mode;
- enum { Awin, Actl, Afrom, Adev, Ato, Ainp, NALT };
- enum { Mnorm, Mtap };
+ enum { Awin, Actl, Afrom, Adev, Ato, Ainp, Awatch, NALT };

	threadsetname("keyboardtap");

@@ -383,10 +383,14 @@
	alts[Ainp].c = nil;
	alts[Ainp].v = &s;
	alts[Ainp].op = CHANNOP;
+ alts[Awatch].c = totap;
+ alts[Awatch].v = &watched;
+ alts[Awatch].op = CHANNOP;
	alts[NALT].op = CHANEND;

	cur = nil;
- mode = Mnorm;
+ mode = Tapoff;
+ watched = nil;
	for(;;)
		switch(alt(alts)){
		case Awin:
@@ -393,21 +397,19 @@
			cur = w;
			if(cur != nil){
				alts[Ainp].c = cur->ck;
- break;
+ if(mode != Tapwatch)
+ break;
+ if(alts[Awatch].op == CHANSND)
+ free(watched);
+ watched = smprint("z%d", cur->id);
+ alts[Awatch].op = CHANSND;
			}
			if(alts[Ainp].op != CHANNOP || alts[Ato].op != CHANNOP)
				free(s);
			goto Reset;
		case Actl:
- switch(*ctl){
- case Tapon:
- mode = Mtap;
- break;
- case Tapoff:
- mode = Mnorm;
- break;
- }
- free(ctl);
+ mode = *ctl;
+ free(ctl);
			break;
		case Afrom:
			if(cur == nil){
@@ -420,16 +422,19 @@
			alts[Ainp].op = CHANSND;
			break;
		case Adev:
- if(mode == Mnorm && cur == nil){
+ if(mode == Tapoff && cur == nil){
				free(s);
				break;
			}
			alts[Afrom].op = CHANNOP;
			alts[Adev].op = CHANNOP;
- if(mode == Mnorm)
+ if(mode == Tapoff)
				alts[Ainp].op = CHANSND;
			else
				alts[Ato].op = CHANSND;
+ break;
+ case Awatch:
+ alts[Awatch].op = CHANNOP;
			break;
		case Ainp:
			if(*s == 'k' || *s == 'K')
--- a//sys/src/cmd/rio/xfid.c
+++ b//sys/src/cmd/rio/xfid.c
@@ -581,12 +581,18 @@
		}
		e = x->data + cnt;
		for(p = x->data; p < e; p += strlen(p)+1){
- if(*p == '\0'){
+ switch(*p){
+ case '\0':
				fc.count = p - x->data;
				filsysrespond(x->fs, x, &fc, "null message
				type");
				return;
+ case Tapwatch:
+ chanprint(ctltap, "%s", p);
+ break;
+ default:
+ chanprint(fromtap, "%s", p);
+ break;
			}
- chanprint(fromtap, "%s", p);
		}
		break;



Mon Sep 26 16:34:14 EDT 2022
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GLES3/gl31.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gbm.h>

static const char* sumShader =
	"#version 310 es\n"
	"precision lowp float;\n"
	"layout(local_size_x = %d) in;\n"
	"layout(std430) buffer;\n"
	"layout(binding = 0) buffer Input0\n"
	"{\n"
	" float elements[];\n"
	"} input_data0;\n"
	"layout(binding = 1) readonly buffer Input1\n"
	"{\n"
	" float elements[];\n"
	"} input_data1;\n"
	"layout(binding = 2) writeonly buffer Output\n"
	"{\n"
	" float elements[];\n"
	"} output_data;\n"
	"void main()\n"
	"{\n"
	" int idx = int(gl_LocalInvocationID.x);\n"
	" int i, j, k, l, m, n, o, p, q;\n"
	" m = %d;\n"
	" l = input_data0.elements.length();\n"
	" i = 0;\n"
	" while ((j = (i * m + idx)) < l) {\n"
	" input_data0.elements[j] = input_data0.elements[j] *
	input_data1.elements[j];\n"
	" i++;\n"
	" }\n"
	" barrier();\n"
	" j = 1;\n"
	" k = 2;\n"
	" while (j < l) {\n"
	" i = 0;\n"
	" p = idx * k;\n"
	" q = m * k;\n"
	" n = p;\n"
	" o = n + j;\n"
	" while (o < l) {\n"
	" input_data0.elements[n] = input_data0.elements[n] +
	input_data0.elements[o];\n"
	" i = i + q;\n"
	" n = i + p;\n"
	" o = n + j;\n"
	" }\n"
	" j = k;\n"
	" k = k * 2;\n"
	" barrier();\n"
	" }\n"
	" if (idx == 0)\n"
	" output_data.elements[0] = input_data0.elements[0];\n"
	"}\n";

int
main(int argc, char **argv) {
	float *A;
	float *B;
	float *C;
	float sum;
	int i, j, k;
	struct timeval now;
	double a, b;
	GLint status;
	GLint length;
	char *log;
	GLuint BO[3];
	float *data;
	char *sumShaderString;
	int threads;

	int32_t fd = open ("/dev/dri/renderD128", O_RDWR);
	if (fd <= 0)
		return -3;

	struct gbm_device *gbm = gbm_create_device (fd);
	if (gbm == NULL)
		return -4;

	EGLDisplay dpy = eglGetPlatformDisplay (EGL_PLATFORM_GBM_MESA, gbm, NULL);
	if (dpy == NULL)
		return -5;

	EGLBoolean returnValue = eglInitialize(dpy, NULL, NULL);
	if (returnValue != EGL_TRUE) {
		printf("eglInitialize failed\n");
		return 0;
	}

	EGLConfig cfg;
	EGLint count;
	EGLint s_configAttribs[] = {
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
		EGL_NONE };
	if (eglChooseConfig(dpy, s_configAttribs, &cfg, 1, &count) == EGL_FALSE) {
		printf("eglChooseConfig failed\n");
		return 0;
	}

	EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
	EGLContext context = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT,
	context_attribs);
	if (context == EGL_NO_CONTEXT) {
		printf("eglCreateContext failed\n");
		return 0;
	}
	returnValue = eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
	context);
	if (returnValue != EGL_TRUE) {
		printf("eglMakeCurrent failed returned %d\n", returnValue);
		return 0;
	}

	GLuint sumShaderID = glCreateShader(GL_COMPUTE_SHADER);
	if (sumShaderID == 0) {
		printf("glGetError %d\n", glGetError());
		return -2;
	}

	glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &threads);
	fprintf(stderr, "GL_MAX_COMPUTE_WORK_GROUP_SIZE: %d\n", threads);

	sumShaderString = malloc(strlen(sumShader) + 256);
	snprintf(sumShaderString, strlen(sumShader) + 256 - 1, sumShader, threads,
	threads);
	glShaderSource(sumShaderID, 1, (const char**)&sumShaderString, NULL);
	glCompileShader(sumShaderID);
	glGetShaderiv(sumShaderID, GL_COMPILE_STATUS, &status);
	if (status == GL_FALSE) {
		fprintf(stderr, "COMPUTE\n");
		glGetShaderiv(sumShaderID, GL_INFO_LOG_LENGTH, &length);
		log = malloc(length+1);
		glGetShaderInfoLog(sumShaderID, length, &length, log);
		write(2, log, length);
		return -1;
	}

	GLuint sumShaderProgram = glCreateProgram();
	glAttachShader(sumShaderProgram, sumShaderID);
	glLinkProgram(sumShaderProgram);
	glGetShaderiv(sumShaderID, GL_LINK_STATUS, &status);
	if (status == GL_FALSE) {
		fprintf(stderr, "LINK\n");
		glGetProgramiv(sumShaderID, GL_INFO_LOG_LENGTH, &length);
		log = malloc(length+1);
		glGetProgramInfoLog(sumShaderID, length, &length, log);
		write(2, log, length);
		return -1;
	}

	srand(time(NULL));

	for (i = 10; i < 50; i++) {
		j = i * 1000;

		A = malloc(j * sizeof(float));
		B = malloc(j * sizeof(float));
		C = malloc(1 * sizeof(float));

		for (k = 0; k < j; k++) {
			A[k] = 0.125; //((float)rand() / RAND_MAX) - 0.5;
			B[k] = 8.0; //((float)rand() / RAND_MAX) - 0.5;
		}

		sum = 0;

		gettimeofday(&now, NULL);
		a = now.tv_sec * 1000000;
		a += now.tv_usec;
		for (k = 0; k < j; k++)
			sum += A[k] * B[k];
		gettimeofday(&now, NULL);
		b = now.tv_sec * 1000000;
		b += now.tv_usec;

		printf("CPU: %f, %f microseconds\n", sum, b - a);

		gettimeofday(&now, NULL);
		a = now.tv_sec * 1000000;
		a += now.tv_usec;

		glGenBuffers(3, BO);

		glBindBuffer(GL_SHADER_STORAGE_BUFFER, BO[0]);
		glBufferData(GL_SHADER_STORAGE_BUFFER, j * sizeof(GLfloat), A,
		GL_STATIC_DRAW);
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, BO[0]);

		glBindBuffer(GL_SHADER_STORAGE_BUFFER, BO[1]);
		glBufferData(GL_SHADER_STORAGE_BUFFER, j * sizeof(GLfloat), B,
		GL_STATIC_DRAW);
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, BO[1]);

		glBindBuffer(GL_SHADER_STORAGE_BUFFER, BO[2]);
		glBufferData(GL_SHADER_STORAGE_BUFFER, 1 * sizeof(GLfloat), C,
		GL_STATIC_DRAW);
		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, BO[2]);

		glUseProgram(sumShaderProgram);
		glDispatchCompute(1, 1, 1);
		glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

		glBindBuffer(GL_SHADER_STORAGE_BUFFER, BO[2]);
		float *out = (float*)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0,
		1 * sizeof(float), GL_MAP_READ_BIT);
		sum = out[0];
		glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
		glDeleteProgram(sumShaderProgram);

		glDeleteBuffers(3, BO);

		gettimeofday(&now, NULL);
		b = now.tv_sec * 1000000;
		b += now.tv_usec;

		printf("GPU: %f, %f microseconds\n", sum, b - a);

		free(A);
		free(B);
		free(C);
	}

	return 0;
}




Mon Sep 26 10:09:54 EDT 2022
sdf

next