all repos — kuro @ 6fa747864efcb84863d09a9a937315e748a9f040

multiwindow text editor thing for plan9 with simple client VM and 9p interface

reinit; VM and 9p core mostly done
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iHUEABYKAB0WIQR2zYvweXfSPsSU6pP1Tg1AaVJx1AUCZa4ePwAKCRD1Tg1AaVJx
1HRHAQDx8HulbvhpLSiSZLPE1qZoXLmbX1eqt2d7jqmfA4E/zwD/bdt9YlfVlLpS
PfApgMrXRfcLeDkYVwErisQl94C9mQI=
=bAfZ
-----END PGP SIGNATURE-----
commit

6fa747864efcb84863d09a9a937315e748a9f040

11 files changed, 581 insertions(+), 0 deletions(-)

jump to
A .gitignore

@@ -0,0 +1,2 @@

+kuro +kurosrvr
A build

@@ -0,0 +1,7 @@

+#!/bin/rc + +mk + +mv 6.out kurosrvr +cp kurosrvr kuro +rm *.6
A client.c

@@ -0,0 +1,35 @@

+#include "dat.h" +#include "fns.h" + +static char mtpt[256] = {0}; + +void kuro9p_set_mtpt(char* path) { + strcpy(mtpt, path); +} + +void kuro9p_write(char* path, char* data, int len) { + int fd; + char fullpath[256]; + strcpy(fullpath, mtpt); + strcat(fullpath, path); + + fd = open(fullpath, OWRITE); + if (fd >= 0) { + write(fd, data, len); + close(fd); + } +} + +char* kuro9p_read(char* path, char* buf, int len) { + int fd; + char fullpath[256]; + strcpy(fullpath, mtpt); + strcat(fullpath, path); + + fd = open(fullpath, OREAD); + if (fd >= 0) { + read(fd, buf, len); + close(fd); + } + return buf; +}
A dat.h

@@ -0,0 +1,102 @@

+#include <u.h> +#include <libc.h> +#include <stdio.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include <draw.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> + +typedef enum { + CTL = 1 +} FileType; + +typedef enum { + KURO_RUNS = 0, + KURO_QUITS +} WindowStatus; + +typedef enum { + PORT_MOUSE, + PORT_RESIZE, + PORT_KBD, + PORT_STATUS, + TOTAL_PORTS +} Port; + +typedef enum { + INIT, + OPEN_FILE, + SAVE_FILE, + MOUSE, + KEYBOARD, + SCROLL, + CURSOR, + SELECT, + INSERT, + DELETE, + CUT, + COPY, + PASTE, + EXEC, + PLUMB, + INDICATE, + SPLIT, + PARTITION, + TOTAL_OPCODES +} Opcode; + +typedef enum { + TAGF, + BODYF, + SHELLF, + TOTAL_SUBF +} SubFrame; + +typedef struct Aux { + FileType type; + char* data; + int count; +} Aux; + +typedef struct { + Opcode opcode; + void* data; +} Instruction; + +typedef void (*Handler)(void*, void*); + +typedef struct { + uvlong id; + WindowStatus status; + Image* img; + Screen* screen; + char filepath[512]; + Rune* tag; + uint tag_len; + Rune* body; + uint body_len; +} KuroMemory; + +typedef struct { + uvlong id; + int fd; + Channel* cpu; /* chan(Instruction) */ + Handler* handlers; + KuroMemory* memory; + Channel* status; /* chan(WidowStatus) */ +} Node; + +typedef struct NodeRef NodeRef; + +struct NodeRef { + int fd[2]; + uvlong id; + NodeRef* next; +}; + +typedef struct { + NodeRef* data[256]; +} NodeTable;
A fns.h

@@ -0,0 +1,20 @@

+int strequ(char* s1, char* s2); +char* strcsw(char* s, char a, char b); +char* uvlong_to_hex(uvlong input, char* buf); + +void start_9p(char* mtpt); + +void kuro9p_set_mtpt(char* mtpt); +void kuro9p_write(char* path, char* data, int len); +char* kuro9p_read(char* path, char* buf, int len); + +void nodetbl_add(NodeTable* self, NodeRef* node); +void nodetbl_del(NodeTable* self, uvlong id); + +void node_setup(Node* self, uvlong id, int fd, Handler* handlers, KuroMemory* memory); +void node_execute(Node* self); +void node_loop(void* data); +Node* create_node(uvlong id, int fd, int new, char* filename); +void supervise_node(Node* self); +void node_cleanup(Node* self); +void memory_cleanup(KuroMemory* self);
A kuro.c

@@ -0,0 +1,75 @@

+#include "dat.h" +#include "fns.h" + +static char* mtpt; + +int threadmaybackground(void) { + return 1; +} + +void threadmain(int argc, char **argv) +{ + /* we want to strip any path elements off the executable name */ + char exe_name[256] = {0}; + char* exe_base; + + uvlong node_id = 0; + int client_fd = -1; + int newwin = 0; + + char client_9p_str[256] = {0}; + + strcpy(exe_name, argv[0]); + exe_base = utfrrune(exe_name, '/'); + if (exe_base) { + exe_base++; + } else { + exe_base = exe_name; + } + + /* if we are the server, serve the filetree on 9p */ + + if (strequ(exe_base, "kurosrvr")) { + if (argc > 1) { + start_9p(argv[1]); + } else { + sysfatal("usage: kurosrvr mtpt"); + } + } else if (strequ(exe_base, "kuro")) { + mtpt = getenv("KURO_MTPT"); + if (mtpt == nil) { + sysfatal("KURO_MTPT not set"); + } + + kuro9p_set_mtpt(mtpt); + print("KURO_MTPT=%s\n", mtpt); + + ARGBEGIN{ + case 'n': + newwin=1; + break; + case 'i': + sscanf(ARGF(), "%d", &node_id); + break; + case 'p': + sscanf(ARGF(), "%d", &client_fd); + break; + }ARGEND + + strcpy(client_9p_str, "new"); + if (argv[0]) { + strcat(client_9p_str, " "); + strcat(client_9p_str, argv[0]); + } + + if (node_id > 0 && client_fd >= 0) { + supervise_node(create_node(node_id, client_fd, newwin, argv[0])); + } else { + /* if node_id or client_id are missing, ask the server to fork us with them, and forward the filename */ + print("9pstr=%s\n", client_9p_str); + kuro9p_write("/ctl", client_9p_str, strlen(client_9p_str)); + } + } else { + sysfatal("invoke as kurosrvr to start the background service or kuro to start an application window"); + } +}
A mkfile

@@ -0,0 +1,24 @@

+</$objtype/mkfile +BIN=/$objtype/bin + +TARG=kuro + +OFILES=\ + kuro.$O\ + srv.$O\ + client.$O\ + util.$O\ + node.$O\ + nodetable.$O\ + +HFILES=dat.h\ + fns.h\ + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + +</sys/src/cmd/mkone + +$O.out: /$objtype/lib/libframe.a /$objtype/lib/libdraw.a /$objtype/lib/libthread.a
A node.c

@@ -0,0 +1,132 @@

+#include "dat.h" +#include "fns.h" + + +void node_setup(Node* self, uvlong id, int fd, Handler* handlers, KuroMemory* memory) { + if (self) { + self->id = id; + self->fd = fd; + self->handlers = handlers; + self->memory = memory; + self->cpu = chancreate(sizeof(Instruction), 0); + self->status = chancreate(sizeof(WindowStatus), 0); + } +} +void node_loop(void* data) { + Node* self = (Node*)data; + Instruction x; + + for (;;) { + recv(self->cpu, &x); + if (self->handlers[x.opcode]) { + (*(self->handlers[x.opcode]))(self, x.data); + switch(self->memory->status) { + case KURO_QUITS: + nbsend(self->status, 0); + threadexits(0); + default: + break; + } + flushimage(self->memory->screen->display, 1); + } + } +} + +void node_execute(Node* self) { + threadcreate(node_loop, self, 1024); +} + +Node* create_node(uvlong id, int fd, int new, char* filename) { + + if (new) { + newwindow("-dx 600 -dy 600"); + } + initdraw(nil, nil, "kuro"); + + KuroMemory* self = (KuroMemory*)malloc(sizeof(KuroMemory)); + Node* node = (Node*)malloc(sizeof(Node)); + Handler* handlers = (Handler*)malloc(TOTAL_OPCODES * sizeof(Handler*)); + + node_setup(node, id, fd, handlers, self); + + self->img = screen; + self->screen = _screen; + + strcpy(self->filepath, filename); + + /* node_execute runs the node on a separate thread */ + node_execute(node); + + return node; +} + +void supervise_node(Node* self) { + + Mousectl* mctl; + Keyboardctl* kctl; + Mouse mouse; + int resize[2]; + WindowStatus status; + Rune kbd; + + if ((mctl = initmouse(nil, screen)) == nil) { + sysfatal("couldn't initialize mctl"); + } + if ((kctl = initkeyboard(nil)) == nil) { + sysfatal("couldn't initialize kctl"); + } + + Alt alts[TOTAL_PORTS + 1] = { + {mctl->c, &mouse, CHANRCV}, + {mctl->resizec, resize, CHANRCV}, + {kctl->c, &kbd, CHANRCV}, + {self->status, &status, CHANRCV}, + {nil, nil, CHANEND} + }; + + for (;;) { + switch (alt(alts)) { + case PORT_MOUSE: + print("got a mouse event"); + break; + case PORT_RESIZE: + lockdisplay(display); + if (getwindow(display, Refnone) < 0) + sysfatal("couldn't resize"); + unlockdisplay(display); + print("got a resize event"); + break; + case PORT_KBD: + print("got a keypress"); + break; + case PORT_STATUS: + switch(status) { + case KURO_QUITS: + print("VM died - let's clean up the data"); + goto cleanup; + default: + break; + } + break; + } + } +cleanup: + node_cleanup(self); +} + +void node_cleanup(Node* self) { + if (self) { + memory_cleanup(self->memory); + chanfree(self->cpu); + chanfree(self->status); + /* TODO: send message on self->fd to tell the server to remove us from NodeTable and filetree */ + /* handlers array is shared between all the nodes */ + free(self); + } +} + +void memory_cleanup(KuroMemory* self) { + if (self) { + /* do we need to free the img and screen? */ + } +}
A nodetable.c

@@ -0,0 +1,42 @@

+#include "dat.h" +#include "fns.h" + +void nodetbl_add(NodeTable* self, NodeRef* node) { + int i; + NodeRef* n; + if (self && node) { + i = (int)(node->id%256); + n = self->data[i]; + if (n && n->next) { + n = n->next; + } + if (n) { + n->next = node; + } else { + n = node; + } + } +} + +void nodetbl_del(NodeTable* self, uvlong id) { + NodeRef* n; + NodeRef* nprev; + + if (self) { + for(int i = 0; i <= 255; i++) { + n = self->data[i]; + + while (n && n->id != id && n->next) { + nprev = n; + n = n->next; + } + if (n->id == id) { + if (nprev) { + nprev->next = n->next; + } + free(n); + break; + } + } + } +}
A srv.c

@@ -0,0 +1,122 @@

+#include "dat.h" +#include "fns.h" + +static NodeTable nodes; +static QLock id_lock; + +static uvlong get_next_id(void) { + static uvlong id = 0; + uvlong next; + qlock(&id_lock); + next = ++id; + qunlock(&id_lock); + return next; +} + +Aux* create_aux(FileType t) { + Aux* self = (Aux*)malloc(sizeof(Aux)); + self->type = t; + self->data = nil; + self->count = 0; + return self; +} + +void fs_destroy_file(File* f) { + Aux* a = (Aux*)f->aux; + if(a && a->data) { + free(a->data); + } + if (a) { + free (a); + } +} + +void kuro_read(Req* r) { + Aux* a = (Aux*)r->fid->file->aux; + switch (a->type) { + case CTL: + default: + respond(r, nil); + break; + } +} + +void new_window(char* filename, int new) { + int rw_fd[2]; + char client_fd[32] = {0}; + char client_id[32] = {0}; + uvlong id; + NodeRef* noderef; + + pipe(rw_fd); + id = get_next_id(); + + noderef = (NodeRef*)malloc(sizeof(NodeRef)); + noderef->id = id; + noderef->fd[0] = rw_fd[0]; + noderef->fd[1] = rw_fd[1]; + nodetbl_add(&nodes, noderef); + + sprintf(client_fd, "%d", rw_fd[1]); + sprintf(client_id, "%d", id); + + rfork(RFNAMEG); + char* a[] = { "kuro", "-p", client_fd, "-i", client_id, new ? "-n" : filename, new ? filename : 0, 0 }; + exec("./kuro", a); +} + +void write_ctl(Req* r) { + char cmd[16] = {0}; + char* c = r->ifcall.data; + int i; + + for (i = 0; i < r->ifcall.count && *c != ' ' && *c != '\n'; i++) { + strncat(cmd, c, 1); + c++; + } + + if (*c == ' ') { + c++; + } + + /* maybe don't do this... */ + strcsw(c, '\n', 0); + + /* diagnostics for now */ + print("cmd: %s\n", cmd); + print("arg: %s\n", c); + + if (strequ(cmd, "new")) { + new_window(c, 1); + respond(r, nil); + } else { + print("unknown command...\n"); + respond(r, nil); + } +} + +void kuro_write(Req* r) { + Aux* a = (Aux*)r->fid->file->aux; + switch (a->type) { + case CTL: + write_ctl(r); + break; + default: + respond(r, nil); + break; + } +} + +void start_9p(char* mtpt) { + if (mtpt) { + putenv("KURO_MTPT", mtpt); + Srv srv = { .read = kuro_read, .write = kuro_write }; + Tree* tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file); + srv.tree = tree; + closefile(createfile(tree->root, "ctl", nil, DMAPPEND | 0600, create_aux(CTL))); + closefile(createfile(tree->root, "nodes", nil, DMDIR | 0600, nil)); + closefile(tree->root); + /* TODO: figure out how to kill the server cleanly so we don't need MREPL */ + threadpostmountsrv(&srv, nil, mtpt, MREPL | MCREATE); + } +}
A util.c

@@ -0,0 +1,20 @@

+#include "dat.h" +#include "fns.h" + +int strequ(char* s1, char* s2) { + return strcmp(s1, s2) == 0; +} + +char* strcsw(char* s, char a, char b) { + int i = 0; + char c; + while ((c = s[i])) { + s[i++] = c == a ? b : c; + } + return s; +} + +char* uvlong_to_hex(uvlong input, char* buf) { + sprintf(buf, "%08%x\0", input); + return buf; +}