From f8064ab70e8b4dcb1ca6bb15ffa75d569d1de502 Mon Sep 17 00:00:00 2001 From: Yuvia Date: Mon, 11 Aug 2025 12:31:26 +0000 Subject: [PATCH] this sucks! --- dat.h | 343 +++++++++++ data.c | 216 +++++++ fns.h | 36 ++ fsys.c | 701 +++++++++++++++++++++ mkfile | 25 + rio.c | 1352 +++++++++++++++++++++++++++++++++++++++++ scrl.c | 182 ++++++ time.c | 124 ++++ util.c | 159 +++++ wctl.c | 500 +++++++++++++++ wind.c | 1848 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ xfid.c | 916 ++++++++++++++++++++++++++++ 12 files changed, 6402 insertions(+) create mode 100644 dat.h create mode 100644 data.c create mode 100644 fns.h create mode 100644 fsys.c create mode 100644 mkfile create mode 100644 rio.c create mode 100644 scrl.c create mode 100644 time.c create mode 100644 util.c create mode 100644 wctl.c create mode 100644 wind.c create mode 100644 xfid.c diff --git a/dat.h b/dat.h new file mode 100644 index 0000000..498bccd --- /dev/null +++ b/dat.h @@ -0,0 +1,343 @@ +enum +{ + Qdir, /* /dev for this window */ + Qscreen, + Qsnarf, + Qwctl, + Qtap, + Qwsys, /* directory of window directories */ + Qwsysdir, /* window directory, child of wsys */ + + Qcons, + Qconsctl, + Qcursor, + Qwdir, + Qwinid, + Qwinname, + Qlabel, + Qkbd, + Qmouse, + Qtext, + Qwindow, + + QMAX, + Qglobal = Qcons, /* anything >= must have non nil window */ +}; + +#define STACK 8192 +#define MAXSNARF 100*1024 + +typedef struct Consreadmesg Consreadmesg; +typedef struct Conswritemesg Conswritemesg; +typedef struct Kbdreadmesg Kbdreadmesg; +typedef struct Stringpair Stringpair; +typedef struct Dirtab Dirtab; +typedef struct Fid Fid; +typedef struct Filsys Filsys; +typedef struct Mouseinfo Mouseinfo; +typedef struct Mousereadmesg Mousereadmesg; +typedef struct Mousestate Mousestate; +typedef struct Ref Ref; +typedef struct Timer Timer; +typedef struct Wctlmesg Wctlmesg; +typedef struct Window Window; +typedef struct Xfid Xfid; +typedef struct Tapmesg Tapmesg; + +enum +{ + Selborder = 4, /* border of selected window */ + Unselborder = 1, /* border of unselected window */ + Scrollwid = 12, /* width of scroll bar */ + Scrollgap = 4, /* gap right of scroll bar */ + BIG = 3, /* factor by which window dimension can exceed screen */ + TRUE = 1, + FALSE = 0, +}; + +#define QID(w,q) ((w<<8)|(q)) +#define WIN(q) ((((ulong)(q).path)>>8) & 0xFFFFFF) +#define FILE(q) (((ulong)(q).path) & 0xFF) + +enum /* control messages */ +{ + Wakeup, + Reshaped, + Topped, + Repaint, + Refresh, + Movemouse, + Rawon, + Rawoff, + Holdon, + Holdoff, + Truncate, + Deleted, + Exited, +}; + +struct Wctlmesg +{ + int type; + Rectangle r; + void *p; +}; + +struct Conswritemesg +{ + Channel *cw; /* chan(Stringpair) */ +}; + +struct Consreadmesg +{ + Channel *c1; /* chan(tuple(char*, int) == Stringpair) */ + Channel *c2; /* chan(tuple(char*, int) == Stringpair) */ +}; + +struct Mousereadmesg +{ + Channel *cm; /* chan(Mouse) */ +}; + +struct Stringpair /* rune and nrune or byte and nbyte */ +{ + void *s; + int ns; +}; + +struct Mousestate +{ + Mouse; + ulong counter; /* serial no. of mouse event */ +}; + +struct Mouseinfo +{ + Mousestate queue[16]; + int ri; /* read index into queue */ + int wi; /* write index */ + ulong counter; /* serial no. of last mouse event we received */ + ulong lastcounter; /* serial no. of last mouse event sent to client */ + int lastb; /* last button state we received */ + uchar qfull; /* filled the queue; no more recording until client comes back */ +}; + +struct Window +{ + Ref; + QLock; + Frame; + Image *i; /* window image, nil when deleted */ + Mousectl mc; + Mouseinfo mouse; + Channel *ck; /* chan(char*) */ + Channel *cctl; /* chan(Wctlmesg)[4] */ + Channel *conswrite; /* chan(Conswritemesg) */ + Channel *consread; /* chan(Consreadmesg) */ + Channel *mouseread; /* chan(Mousereadmesg) */ + Channel *wctlread; /* chan(Consreadmesg) */ + Channel *kbdread; /* chan(Consreadmesg) */ + Channel *complete; /* chan(Completion*) */ + Channel *gone; /* chan(char*) */ + uint nr; /* number of runes in window */ + uint maxr; /* number of runes allocated in r */ + Rune *r; + uint nraw; + Rune *raw; + uint org; + uint q0; + uint q1; + uint qh; + int id; + char name[32]; + uint namecount; + Rectangle scrollr; + /* + * Rio once used originwindow, so screenr could be different from i->r. + * Now they're always the same but the code doesn't assume so. + */ + Rectangle screenr; /* screen coordinates of window */ + int resized; + int wctlready; + Rectangle lastsr; + int topped; + int notefd; + uchar scrolling; + Cursor cursor; + Cursor *cursorp; + uchar holding; + uchar rawing; + uchar ctlopen; + uchar wctlopen; + uchar deleted; + uchar mouseopen; + uchar kbdopen; + uchar keyup; + uchar winnameread; + char *label; + char *dir; +}; + +void winctl(void*); +void winshell(void*); +Window* wlookid(int); +Window* wmk(Image*, Mousectl*, Channel*, Channel*, int); +Window* wpointto(Point); +Window* wtop(Point); +void wtopme(Window*); +void wbottomme(Window*); +char* wcontents(Window*, int*); +int wclose(Window*); +uint wbacknl(Window*, uint, uint); +void wcurrent(Window*); +void wuncurrent(Window*); +void wcut(Window*); +void wpaste(Window*); +void wplumb(Window*); +void wlook(Window*); +void wscrdraw(Window*); +void wscroll(Window*, int); +void wsend(Window*); +void wsendctlmesg(Window*, int, Rectangle, void*); +void wsetcursor(Window*, int); +void wsetname(Window*); +void wsetorigin(Window*, uint, int); +void wsetpid(Window*, int, int); +void wshow(Window*, uint); +void wsnarf(Window*); +void wscrsleep(Window*, uint); + +struct Dirtab +{ + char *name; + uchar type; + uint qid; + uint perm; +}; + +struct Fid +{ + int fid; + int busy; + int open; + int mode; + Qid qid; + Window *w; + Dirtab *dir; + Fid *next; + int nrpart; + uchar rpart[UTFmax]; +}; + +struct Xfid +{ + Ref; + Xfid *next; + Xfid *free; + Fcall; + Channel *c; /* chan(void(*)(Xfid*)) */ + Fid *f; + uchar *buf; + Filsys *fs; + int flushtag; /* our tag, so flush can find us */ + Channel *flushc; /* channel(int) to notify us we're being flushed */ +}; + +Channel* xfidinit(void); +void xfidctl(void*); +void xfidflush(Xfid*); +void xfidattach(Xfid*); +void xfidopen(Xfid*); +void xfidclose(Xfid*); +void xfidread(Xfid*); +void xfidwrite(Xfid*); + +enum +{ + Nhash = 16, +}; + +struct Filsys +{ + int cfd; + int sfd; + int pid; + char *user; + Channel *cxfidalloc; /* chan(Xfid*) */ + Channel *csyncflush; /* chan(int) */ + Fid *fids[Nhash]; +}; + +Filsys* filsysinit(Channel*); +int filsysmount(Filsys*, int); +Xfid* filsysrespond(Filsys*, Xfid*, Fcall*, char*); +void filsyscancel(Xfid*); + +void deletetimeoutproc(void*); + +struct Timer +{ + int dt; + int cancel; + Channel *c; /* chan(int) */ + Timer *next; +}; + +Font *font; +Mousectl *mousectl; +Mouse *mouse; +Display *display; +Image *view; +Screen *wscreen; +Cursor boxcursor; +Cursor crosscursor; +Cursor sightcursor; +Cursor whitearrow; +Cursor query; +Cursor *corners[9]; +Cursor skull; + +Image *background; +Image *cols[NCOL]; +Image *titlecol; +Image *lighttitlecol; +Image *dholdcol; +Image *holdcol; +Image *lightholdcol; +Image *paleholdcol; +Image *paletextcol; +Image *sizecol; +int reverse; /* there are no pastel paints in the dungeons and dragons world -- rob pike */ + +Window **window; +Window *wkeyboard; /* window of simulated keyboard */ +int nwindow; +int snarffd; +int gotscreen; +int servekbd; + +Channel *opentap; /* open fromtap or totap */ +Channel *closetap; /* close fromtap or totap */ +Channel *fromtap; /* keyboard output from the tap program */ +Channel *totap; /* our keyboard input to tap program */ + +Window *input; +QLock all; /* BUG */ +Filsys *filsys; +Window *hidden[100]; +int nhidden; +int nsnarf; +Rune* snarf; +int scrolling; +int maxtab; +Channel* winclosechan; +char *startdir; +int sweeping; +char srvpipe[]; +char srvwctl[]; +int errorshouldabort; +int menuing; /* menu action is pending; waiting for window to be indicated */ +int snarfversion; /* updated each time it is written */ +int messagesize; /* negotiated in 9P version setup */ +int shiftdown; +int debug; diff --git a/data.c b/data.c new file mode 100644 index 0000000..d65ca55 --- /dev/null +++ b/data.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +Cursor crosscursor = { + {-7, -7}, + {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, }, + {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE, + 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, } +}; + +Cursor boxcursor = { + {-7, -7}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, + 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, + {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, } +}; + +Cursor sightcursor = { + {-7, -7}, + {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, + 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, + 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, }, + {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, + 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, + 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, + 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, } +}; + +Cursor whitearrow = { + {0, 0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, + 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, + 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, + 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }, + {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, + 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, + 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, + 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, } +}; + +Cursor query = { + {-7,-7}, + {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, + 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, + 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, + 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, }, + {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, + 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, + 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, + 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } +}; + +Cursor tl = { + {-4, -4}, + {0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff, + 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, }, + {0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00, + 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, } +}; + +Cursor skull = { + {-7,-7}, + {0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8, + 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, + 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0, + 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27, + 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} +}; + +Cursor t = { + {-7, -8}, + {0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0, + 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80, + 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } +}; + +Cursor tr = { + {-11, -4}, + {0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1, + 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88, + 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88, + 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, }, + {0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e, + 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70, + 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, } +}; + +Cursor r = { + {-8, -7}, + {0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58, + 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02, + 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58, + 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, }, + {0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, + 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc, + 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80, + 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } +}; + +Cursor br = { + {-11, -11}, + {0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, + 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, + 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05, + 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, }, + {0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa, + 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, } +}; + +Cursor b = { + {-7, -7}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70, + 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, }, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, + 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } +}; + +Cursor bl = { + {-4, -11}, + {0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01, + 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, }, + {0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe, + 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, } +}; + +Cursor l = { + {-7, -7}, + {0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20, + 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20, + 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20, + 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, }, + {0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, + 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0, + 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0, + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, } +}; + +Cursor *corners[9] = { + &tl, &t, &tr, + &l, nil, &r, + &bl, &b, &br, +}; + +void +iconinit(void) +{ + background = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x777777FF); + + /* greys are multiples of 0x11111100+0xFF, 14* being palest */ + cols[BACK] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xFFFFFFFF^reverse); + cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF^reverse); + cols[TEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF^reverse); + cols[HTEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF); + if(!reverse) { + cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF); + titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen); + lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen); + } else { + cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPurpleblue); + titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPurpleblue); + lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x222222FF); + } + dholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue); + lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue); + paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue); + paletextcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF^reverse); + sizecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DRed); + + if(reverse == 0) + holdcol = dholdcol; + else + holdcol = paleholdcol; +} diff --git a/fns.h b/fns.h new file mode 100644 index 0000000..8562c2c --- /dev/null +++ b/fns.h @@ -0,0 +1,36 @@ +int whide(Window*); +int wunhide(Window*); +void freescrtemps(void); +int parsewctl(char**, Rectangle, Rectangle*, int*, int*, int*, int*, char**, char*, char*); +int writewctl(Xfid*, char*); +Window *new(Image*, int, int, int, char*, char*, char**); +void riosetcursor(Cursor*); +int min(int, int); +int max(int, int); +Rune* strrune(Rune*, Rune); +int isalnum(Rune); +int isspace(Rune); +void timerstop(Timer*); +void timercancel(Timer*); +Timer* timerstart(int); +void error(char*); +void killprocs(void); +int shutdown(void*, char*); +void iconinit(void); +void *erealloc(void*, uint); +void *emalloc(uint); +char *estrdup(char*); +void button3menu(void); +void button2menu(Window*); +void cvttorunes(char*, int, Rune*, int*, int*, int*); +/* was (byte*,int) runetobyte(Rune*, int); */ +char* runetobyte(Rune*, int, int*); +void putsnarf(void); +void getsnarf(void); +void timerinit(void); +int goodrect(Rectangle); +int inborder(Rectangle, Point); + +#define runemalloc(n) malloc((n)*sizeof(Rune)) +#define runerealloc(a, n) realloc(a, (n)*sizeof(Rune)) +#define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune)) diff --git a/fsys.c b/fsys.c new file mode 100644 index 0000000..d227430 --- /dev/null +++ b/fsys.c @@ -0,0 +1,701 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +char Eperm[] = "permission denied"; +char Eexist[] = "file does not exist"; +char Enotdir[] = "not a directory"; +char Ebadfcall[] = "bad fcall type"; +char Eoffset[] = "illegal offset"; +char Enomem[] = "out of memory"; + +int messagesize = 8192+IOHDRSZ; /* good start */ + +Dirtab dirtab[]= +{ + { ".", QTDIR, Qdir, 0500|DMDIR }, + { "screen", QTFILE, Qscreen, 0400 }, + { "snarf", QTFILE, Qsnarf, 0600 }, + { "wctl", QTFILE, Qwctl, 0600 }, + { "kbdtap", QTFILE, Qtap, 0660 }, + { "wsys", QTDIR, Qwsys, 0500|DMDIR }, + + { "cons", QTFILE, Qcons, 0600 }, + { "cursor", QTFILE, Qcursor, 0600 }, + { "consctl", QTFILE, Qconsctl, 0200 }, + { "winid", QTFILE, Qwinid, 0400 }, + { "winname", QTFILE, Qwinname, 0400 }, + { "label", QTFILE, Qlabel, 0600 }, + { "kbd", QTFILE, Qkbd, 0600 }, + { "mouse", QTFILE, Qmouse, 0600 }, + { "text", QTFILE, Qtext, 0600 }, + { "wdir", QTFILE, Qwdir, 0600 }, + { "window", QTFILE, Qwindow, 0400 }, + { nil, } +}; + +static uint getclock(void); +static void filsysproc(void*); +static Fid* newfid(Filsys*, int); +static int dostat(Filsys*, int, Dirtab*, uchar*, int, uint); + +int clockfd; +int firstmessage = 1; + +char srvpipe[64]; +char srvwctl[64]; + +static Xfid* filsysflush(Filsys*, Xfid*, Fid*); +static Xfid* filsysversion(Filsys*, Xfid*, Fid*); +static Xfid* filsysauth(Filsys*, Xfid*, Fid*); +static Xfid* filsysattach(Filsys*, Xfid*, Fid*); +static Xfid* filsyswalk(Filsys*, Xfid*, Fid*); +static Xfid* filsysopen(Filsys*, Xfid*, Fid*); +static Xfid* filsyscreate(Filsys*, Xfid*, Fid*); +static Xfid* filsysread(Filsys*, Xfid*, Fid*); +static Xfid* filsyswrite(Filsys*, Xfid*, Fid*); +static Xfid* filsysclunk(Filsys*, Xfid*, Fid*); +static Xfid* filsysremove(Filsys*, Xfid*, Fid*); +static Xfid* filsysstat(Filsys*, Xfid*, Fid*); +static Xfid* filsyswstat(Filsys*, Xfid*, Fid*); + +Xfid* (*fcall[Tmax])(Filsys*, Xfid*, Fid*) = +{ + [Tflush] = filsysflush, + [Tversion] = filsysversion, + [Tauth] = filsysauth, + [Tattach] = filsysattach, + [Twalk] = filsyswalk, + [Topen] = filsysopen, + [Tcreate] = filsyscreate, + [Tread] = filsysread, + [Twrite] = filsyswrite, + [Tclunk] = filsysclunk, + [Tremove]= filsysremove, + [Tstat] = filsysstat, + [Twstat] = filsyswstat, +}; + +void +post(char *name, char *envname, int srvfd) +{ + int fd; + char buf[32]; + + fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600); + if(fd < 0) + error(name); + snprint(buf, sizeof(buf), "%d", srvfd); + if(write(fd, buf, strlen(buf)) != strlen(buf)) + error("srv write"); + putenv(envname, name); +} + +/* + * Build pipe with OCEXEC set on second fd. + * Can't put it on both because we want to post one in /srv. + */ +int +cexecpipe(int *p0, int *p1) +{ + /* pipe the hard way to get close on exec */ + if(bind("#|", "/mnt/temp", MREPL) == -1) + return -1; + *p0 = open("/mnt/temp/data", ORDWR); + *p1 = open("/mnt/temp/data1", ORDWR|OCEXEC); + unmount(nil, "/mnt/temp"); + if(*p0<0 || *p1<0) + return -1; + return 0; +} + +Filsys* +filsysinit(Channel *cxfidalloc) +{ + Filsys *fs; + + fs = emalloc(sizeof(Filsys)); + if(cexecpipe(&fs->cfd, &fs->sfd) < 0) + goto Rescue; + fmtinstall('F', fcallfmt); + clockfd = open("/dev/time", OREAD|OCEXEC); + fs->user = getuser(); + fs->csyncflush = chancreate(sizeof(int), 0); + if(fs->csyncflush == nil) + error("chancreate syncflush"); + fs->cxfidalloc = cxfidalloc; + + proccreate(filsysproc, fs, 10000); + snprint(srvpipe, sizeof(srvpipe), "/srv/rio.%s.%lud", fs->user, (ulong)getpid()); + post(srvpipe, "wsys", fs->cfd); + + return fs; + +Rescue: + free(fs); + return nil; +} + +static +void +filsysproc(void *arg) +{ + int n; + Xfid *x; + Fid *f; + Fcall t; + uchar *buf; + Filsys *fs; + + threadsetname("FILSYSPROC"); + fs = arg; + fs->pid = getpid(); + x = nil; + for(;;){ + buf = malloc(messagesize+UTFmax); /* UTFmax for appending partial rune in xfidwrite */ + if(buf == nil) + error(Enomem); + n = read9pmsg(fs->sfd, buf, messagesize); + if(n <= 0){ + yield(); /* if threadexitsall'ing, will not return */ + fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n); + errorshouldabort = 0; + error("eof or i/o error on server channel"); + } + if(x == nil){ + send(fs->cxfidalloc, nil); + recv(fs->cxfidalloc, &x); + x->fs = fs; + } + x->buf = buf; + if(convM2S(buf, n, x) != n) + error("convert error in convM2S"); + if(debug) + fprint(2, "rio:<-%F\n", &x->Fcall); + if(fcall[x->type] == nil) + x = filsysrespond(fs, x, &t, Ebadfcall); + else{ + if(x->type==Tversion || x->type==Tauth) + f = nil; + else + f = newfid(fs, x->fid); + x->f = f; + x = (*fcall[x->type])(fs, x, f); + } + firstmessage = 0; + } +} + +/* + * Called only from a different FD group + */ +int +filsysmount(Filsys *fs, int id) +{ + char buf[32]; + + close(fs->sfd); /* close server end so mount won't hang if exiting */ + snprint(buf, sizeof buf, "%d", id); + if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) == -1){ + fprint(2, "mount failed: %r\n"); + return -1; + } + if(bind("/mnt/wsys", "/dev", MBEFORE) == -1){ + fprint(2, "bind failed: %r\n"); + return -1; + } + return 0; +} + +Xfid* +filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err) +{ + int n; + + if(err){ + t->type = Rerror; + t->ename = err; + }else + t->type = x->type+1; + t->fid = x->fid; + t->tag = x->tag; + if(x->buf == nil) + error("no buffer in respond"); + n = convS2M(t, x->buf, messagesize); + if(n <= 0) + error("convert error in convS2M"); + if(write(fs->sfd, x->buf, n) != n) + error("write error in respond"); + if(debug) + fprint(2, "rio:->%F\n", t); + free(x->buf); + x->buf = nil; + x->flushtag = -1; + return x; +} + +void +filsyscancel(Xfid *x) +{ + if(x->buf){ + free(x->buf); + x->buf = nil; + } +} + +static +Xfid* +filsysversion(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + if(!firstmessage) + return filsysrespond(x->fs, x, &t, "version request not first message"); + if(x->msize < 256) + return filsysrespond(x->fs, x, &t, "version: message size too small"); + messagesize = x->msize; + t.msize = messagesize; + t.version = "9P2000"; + if(strncmp(x->version, "9P", 2) != 0) + t.version = "unknown"; + return filsysrespond(fs, x, &t, nil); +} + +static +Xfid* +filsysauth(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, "rio: authentication not required"); +} + +static +Xfid* +filsysflush(Filsys *fs, Xfid *x, Fid*) +{ + sendp(x->c, xfidflush); + + /* + * flushes need to be replied in order. xfidflush() will + * awaken us when the flush has been queued. + */ + recv(fs->csyncflush, nil); + + return nil; +} + +static +Xfid* +filsysattach(Filsys *, Xfid *x, Fid *f) +{ + Fcall t; + + if(strcmp(x->uname, x->fs->user) != 0) + return filsysrespond(x->fs, x, &t, Eperm); + f->busy = TRUE; + f->open = FALSE; + f->qid.path = Qdir; + f->qid.type = QTDIR; + f->qid.vers = 0; + f->dir = dirtab; + f->nrpart = 0; + sendp(x->c, xfidattach); + return nil; +} + +static +int +numeric(char *s) +{ + for(; *s!='\0'; s++) + if(*s<'0' || '9'<*s) + return 0; + return 1; +} + +static +int +skipdir(char *name) +{ + /* don't serve these if it's provided in the environment */ + if(snarffd>=0 && strcmp(name, "snarf")==0) + return 1; + if(gotscreen && strcmp(name, "screen")==0) + return 1; + if(!servekbd && strcmp(name, "kbd")==0) + return 1; + return 0; +} + +static +Xfid* +filsyswalk(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + Fid *nf; + int i, id; + uchar type; + ulong path; + Dirtab *d, *dir; + Window *w; + char *err; + Qid qid; + + if(f->open) + return filsysrespond(fs, x, &t, "walk of open file"); + nf = nil; + if(x->fid != x->newfid){ + /* BUG: check exists */ + nf = newfid(fs, x->newfid); + if(nf->busy) + return filsysrespond(fs, x, &t, "clone to busy fid"); + nf->busy = TRUE; + nf->open = FALSE; + nf->dir = f->dir; + nf->qid = f->qid; + nf->w = f->w; + if(f->w != nil) + incref(f->w); + nf->nrpart = 0; /* not open, so must be zero */ + f = nf; /* walk f */ + } + + t.nwqid = 0; + err = nil; + + /* update f->qid, f->dir only if walk completes */ + qid = f->qid; + dir = f->dir; + + if(x->nwname > 0){ + for(i=0; inwname; i++){ + if((qid.type & QTDIR) == 0){ + err = Enotdir; + break; + } + if(strcmp(x->wname[i], "..") == 0){ + type = QTDIR; + path = Qdir; + dir = dirtab; + if(FILE(qid) == Qwsysdir) + path = Qwsys; + id = 0; + Accept: + if(i == MAXWELEM){ + err = "name too long"; + break; + } + qid.type = type; + qid.vers = 0; + qid.path = QID(id, path); + t.wqid[t.nwqid++] = qid; + continue; + } + + if(qid.path == Qwsys){ + /* is it a numeric name? */ + if(!numeric(x->wname[i])) + break; + /* yes: it's a directory */ + id = atoi(x->wname[i]); + qlock(&all); + w = wlookid(id); + if(w == nil){ + qunlock(&all); + break; + } + path = Qwsysdir; + type = QTDIR; + qunlock(&all); + incref(w); + if(f->w) + sendp(winclosechan, f->w); + f->w = w; + dir = dirtab; + goto Accept; + } + if(skipdir(x->wname[i])) + break; + id = WIN(f->qid); + d = dirtab; + d++; /* skip '.' */ + for(; d->name; d++) + if(strcmp(x->wname[i], d->name) == 0){ + if(f->w == nil && d->qid >= Qglobal) + break; + path = d->qid; + type = d->type; + dir = d; + goto Accept; + } + + break; /* file not found */ + } + + if(i==0 && err==nil) + err = Eexist; + } + + if(err!=nil || t.nwqidnwname){ + if(nf){ + if(nf->w) + sendp(winclosechan, nf->w); + nf->open = FALSE; + nf->busy = FALSE; + } + }else if(t.nwqid == x->nwname){ + f->dir = dir; + f->qid = qid; + } + + return filsysrespond(fs, x, &t, err); +} + +static +Xfid* +filsysopen(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + int m; + + /* can't truncate anything but Qtext, so just disregard */ + if(FILE(f->qid) != Qtext) + x->mode &= ~OTRUNC; + x->mode &= ~OCEXEC; + /* can't execute or remove anything */ + if(x->mode==OEXEC || (x->mode&ORCLOSE)) + goto Deny; + switch(x->mode & ~OTRUNC){ + default: + goto Deny; + case OREAD: + m = 0400; + break; + case OWRITE: + m = 0200; + break; + case ORDWR: + m = 0600; + break; + } + if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m) + goto Deny; + + sendp(x->c, xfidopen); + return nil; + + Deny: + return filsysrespond(fs, x, &t, Eperm); +} + +static +Xfid* +filsyscreate(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, Eperm); +} + +static +int +idcmp(void *a, void *b) +{ + return *(int*)a - *(int*)b; +} + +static +Xfid* +filsysread(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + uchar *b; + int i, n, o, e, len, j, k, *ids; + Dirtab *d, dt; + uint clock; + char buf[32]; + + if((f->qid.type & QTDIR) == 0){ + sendp(x->c, xfidread); + return nil; + } + o = x->offset; + e = x->offset+x->count; + clock = getclock(); + b = malloc(messagesize-IOHDRSZ); /* avoid memset of emalloc */ + if(b == nil) + return filsysrespond(fs, x, &t, Enomem); + n = 0; + switch(FILE(f->qid)){ + case Qdir: + case Qwsysdir: + d = dirtab; + d++; /* first entry is '.' */ + for(i=0; d->name!=nil && iname)) + continue; + if(f->w == nil && d->qid >= Qglobal) + continue; + len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock); + if(len <= BIT16SZ) + break; + if(i >= o) + n += len; + i += len; + } + break; + case Qwsys: + qlock(&all); + ids = emalloc(nwindow*sizeof(int)); + for(j=0; jid; + qunlock(&all); + qsort(ids, nwindow, sizeof ids[0], idcmp); + dt.name = buf; + for(i=0, j=0; jcount-n, clock); + if(len == 0) + break; + if(i >= o) + n += len; + j++; + } + free(ids); + break; + } + t.data = (char*)b; + t.count = n; + filsysrespond(fs, x, &t, nil); + free(b); + return x; +} + +static +Xfid* +filsyswrite(Filsys*, Xfid *x, Fid*) +{ + sendp(x->c, xfidwrite); + return nil; +} + +static +Xfid* +filsysclunk(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + + if(f->open){ + f->busy = FALSE; + f->open = FALSE; + sendp(x->c, xfidclose); + return nil; + } + if(f->w) + sendp(winclosechan, f->w); + f->busy = FALSE; + f->open = FALSE; + return filsysrespond(fs, x, &t, nil); +} + +static +Xfid* +filsysremove(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, Eperm); +} + +static +Xfid* +filsysstat(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + + t.stat = emalloc(messagesize-IOHDRSZ); + t.nstat = dostat(fs, WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock()); + x = filsysrespond(fs, x, &t, nil); + free(t.stat); + return x; +} + +static +Xfid* +filsyswstat(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, Eperm); +} + +static +Fid* +newfid(Filsys *fs, int fid) +{ + Fid *f, *ff, **fh; + + ff = nil; + fh = &fs->fids[fid&(Nhash-1)]; + for(f=*fh; f; f=f->next) + if(f->fid == fid) + return f; + else if(ff==nil && f->busy==FALSE) + ff = f; + if(ff){ + ff->fid = fid; + return ff; + } + f = emalloc(sizeof *f); + f->fid = fid; + f->next = *fh; + *fh = f; + return f; +} + +static +uint +getclock(void) +{ + char buf[32]; + + seek(clockfd, 0, 0); + read(clockfd, buf, sizeof buf); + return atoi(buf); +} + +static +int +dostat(Filsys *fs, int id, Dirtab *dir, uchar *buf, int nbuf, uint clock) +{ + Dir d; + + d.qid.path = QID(id, dir->qid); + if(dir->qid == Qsnarf) + d.qid.vers = snarfversion; + else + d.qid.vers = 0; + d.qid.type = dir->type; + d.mode = dir->perm; + d.length = 0; /* would be nice to do better */ + d.name = dir->name; + d.uid = fs->user; + d.gid = fs->user; + d.muid = fs->user; + d.atime = clock; + d.mtime = clock; + return convD2M(&d, buf, nbuf); +} diff --git a/mkfile b/mkfile new file mode 100644 index 0000000..858459d --- /dev/null +++ b/mkfile @@ -0,0 +1,25 @@ + syms + $CC -aa $CFLAGS *.c >>syms diff --git a/rio.c b/rio.c new file mode 100644 index 0000000..6ed77f8 --- /dev/null +++ b/rio.c @@ -0,0 +1,1352 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +/* + * WASHINGTON (AP) - The Food and Drug Administration warned + * consumers Wednesday not to use ``Rio'' hair relaxer products + * because they may cause severe hair loss or turn hair green.... + * The FDA urged consumers who have experienced problems with Rio + * to notify their local FDA office, local health department or the + * company at 1‑800‑543‑3002. + */ + +void resize(void); +void move(void); +void delete(void); +void hide(void); +void unhide(int); +void newtile(int); +void confirmexit(void); +Image* sweep(void); +Image* bandsize(Window*); +Image* drag(Window*); +void resized(void); +Channel *exitchan; /* chan(int) */ +Channel *winclosechan; /* chan(Window*); */ +Channel *kbdchan; /* chan(char*); */ +Rectangle viewr; +int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */ + +void mousethread(void*); +void keyboardtap(void*); +void winclosethread(void*); +void initcmd(void*); +Channel* initkbd(void); + +char *fontname; + +enum +{ + New, + Reshape, + Move, + Delete, + Hide, + Exit, +}; + +enum +{ + Cut, + Paste, + Snarf, + Plumb, + Look, + Send, + Scroll, +}; + +char *menu2str[] = { + [Cut] "cut", + [Paste] "paste", + [Snarf] "snarf", + [Plumb] "plumb", + [Look] "look", + [Send] "send", + [Scroll] "scroll", + nil +}; + +Menu menu2 = +{ + menu2str +}; + +int Hidden = Exit+1; + +char *menu3str[100] = { + [New] "New", + [Reshape] "Resize", + [Move] "Move", + [Delete] "Delete", + [Hide] "Hide", + [Exit] "Exit", + nil +}; + +Menu menu3 = +{ + menu3str +}; + +char *rcargv[] = { "rc", "-i", nil }; +char *kbdargv[] = { "rc", "-c", nil, nil }; + +int errorshouldabort = 0; + +void +derror(Display*, char *errorstr) +{ + error(errorstr); +} + +void +usage(void) +{ + fprint(2, "usage: rio [-b] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n"); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *initstr, *kbdin, *s; + char buf[256]; + Image *i; + Rectangle r; + + initstr = nil; + kbdin = nil; + maxtab = 0; + ARGBEGIN{ + case 'b': + reverse = ~0xFF; + break; + case 'f': + fontname = EARGF(usage()); + break; + case 'i': + initstr = EARGF(usage()); + break; + case 'k': + if(kbdin != nil) + usage(); + kbdin = EARGF(usage()); + break; + case 's': + scrolling = TRUE; + break; + case 'D': + debug++; + break; + default: + usage(); + }ARGEND + + if(getwd(buf, sizeof buf) == nil) + startdir = estrdup("."); + else + startdir = estrdup(buf); + if(fontname == nil) + fontname = getenv("font"); + s = getenv("tabstop"); + if(s != nil) + maxtab = strtol(s, nil, 0); + if(maxtab == 0) + maxtab = 4; + free(s); + + if(fontname){ + /* check font before barging ahead */ + if(access(fontname, 0) < 0){ + fprint(2, "rio: can't access %s: %r\n", fontname); + exits("font open"); + } + putenv("font", fontname); + } + + snarffd = open("/dev/snarf", OREAD|OCEXEC); + gotscreen = access("/dev/screen", AEXIST)==0; + + if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){ + fprint(2, "rio: can't open display: %r\n"); + exits("display open"); + } + iconinit(); + + exitchan = chancreate(sizeof(int), 0); + winclosechan = chancreate(sizeof(Window*), 0); + + view = screen; + viewr = view->r; + mousectl = initmouse(nil, screen); + if(mousectl == nil) + error("can't find mouse"); + mouse = mousectl; + kbdchan = initkbd(); + if(kbdchan == nil) + error("can't find keyboard"); + opentap = chancreate(sizeof(Channel*), 0); + closetap = chancreate(sizeof(Channel*), 0); + threadcreate(keyboardtap, nil, STACK); + + wscreen = allocscreen(screen, background, 0); + if(wscreen == nil) + error("can't allocate screen"); + draw(view, viewr, background, nil, ZP); + flushimage(display, 1); + + timerinit(); + threadcreate(mousethread, nil, STACK); + threadcreate(winclosethread, nil, STACK); + filsys = filsysinit(xfidinit()); + + if(filsys == nil) + fprint(2, "rio: can't create file system server: %r\n"); + else{ + errorshouldabort = 1; /* suicide if there's trouble after this */ + if(initstr) + proccreate(initcmd, initstr, STACK); + if(kbdin){ + kbdargv[2] = kbdin; + r = screen->r; + r.min.y = r.max.y-Dy(r)/3; + i = allocwindow(wscreen, r, Refbackup, DNofill); + wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv); + if(wkeyboard == nil) + error("can't create keyboard window"); + } + threadnotify(shutdown, 1); + recv(exitchan, nil); + } + killprocs(); + closedisplay(display); + threadexitsall(nil); +} + +/* + * /dev/snarf updates when the file is closed, so we must open our own + * fd here rather than use snarffd + */ +void +putsnarf(void) +{ + int fd, i, n; + + if(snarffd<0 || nsnarf==0) + return; + fd = open("/dev/snarf", OWRITE|OCEXEC); + if(fd < 0) + return; + /* snarf buffer could be huge, so fprint will truncate; do it in blocks */ + for(i=0; i= 256) + n = 256; + if(fprint(fd, "%.*S", n, snarf+i) < 0) + break; + } + close(fd); +} + +void +getsnarf(void) +{ + int i, n, nb, nulls; + char *s, *sn; + + if(snarffd < 0) + return; + sn = nil; + i = 0; + seek(snarffd, 0, 0); + for(;;){ + if(i > MAXSNARF) + break; + if((s = realloc(sn, i+1024+1)) == nil) + break; + sn = s; + if((n = read(snarffd, sn+i, 1024)) <= 0) + break; + i += n; + } + if(i == 0) + return; + sn[i] = 0; + if((snarf = runerealloc(snarf, i+1)) != nil) + cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls); + free(sn); +} + +void +initcmd(void *arg) +{ + char *cmd; + char *wsys; + int fd; + + cmd = arg; + rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG); + wsys = getenv("wsys"); + fd = open(wsys, ORDWR); + if(fd < 0) + fprint(2, "rio: failed to open wsys: %r\n"); + if(mount(fd, -1, "/mnt/wsys", MREPL, "none") < 0) + fprint(2, "rio: failed to mount wsys: %r\n"); + if(bind("/mnt/wsys", "/dev/", MBEFORE) < 0) + fprint(2, "rio: failed to bind wsys: %r\n"); + free(wsys); + close(fd); + procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil); + fprint(2, "rio: exec failed: %r\n"); + exits("exec"); +} + +char *oknotes[] = +{ + "delete", + "hangup", + "kill", + "exit", + nil +}; + +int +shutdown(void *, char *msg) +{ + int i; + static Lock shutdownlk; + + killprocs(); + for(i=0; oknotes[i]; i++) + if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){ + lock(&shutdownlk); /* only one can threadexitsall */ + threadexitsall(msg); + } + fprint(2, "rio %d: abort: %s\n", getpid(), msg); + abort(); +} + +void +killprocs(void) +{ + int i; + + for(i=0; inotefd >= 0) + write(window[i]->notefd, "hangup", 6); +} + +void +keyboardtap(void*) +{ + Window *cur = nil; + Channel *c; + char *s, *t; + + enum { Akbd, Aopen, Aclose, Awrite, NALT }; + Alt alts[NALT+1] = { + [Akbd] {.c = kbdchan, .v = &s, .op = CHANRCV}, + [Aopen] {.c = opentap, .v = &c, .op = CHANRCV}, + [Aclose]{.c = closetap, .v = &c, .op = CHANRCV}, + [Awrite]{.c = nil, .v = &s, .op = CHANNOP}, + [NALT] {.op = CHANEND}, + }; + + threadsetname("keyboardtap"); + + for(;;){ + switch(alt(alts)){ + case Akbd: + if(*s == 'k' || *s == 'K') + shiftdown = utfrune(s+1, Kshift) != nil; + if(totap == nil) + goto Bypass; + if(input != nil && input != cur){ /* context change */ + char *z = smprint("z%d", input->id); + if(nbsendp(totap, z) != 1){ + free(z); + goto Bypass; + } + } + cur = input; + if(nbsendp(totap, s) != 1) + goto Bypass; + break; + case Aopen: + if(c == fromtap){ + alts[Awrite].c = c; + alts[Awrite].op = CHANRCV; + } + break; + case Aclose: + if(c == fromtap){ + fromtap = nil; + alts[Awrite].c = nil; + alts[Awrite].op = CHANNOP; + } + if(c == totap) + totap = nil; + while(nbrecv(c, &t)) + free(t); + chanfree(c); + break; + case Awrite: + if(input != cur){ + free(s); + break; + } + Bypass: + cur = input; + if(cur == nil){ + free(s); + break; + } + sendp(cur->ck, s); + break; + } + } +} + +int +inborder(Rectangle r, Point xy) +{ + return ptinrect(xy, r) && !ptinrect(xy, insetrect(r, Selborder)); +} + +Rectangle +whichrect(Rectangle r, Point p, int which) +{ + switch(which){ + case 0: /* top left */ + r = Rect(p.x, p.y, r.max.x, r.max.y); + break; + case 2: /* top right */ + r = Rect(r.min.x, p.y, p.x+1, r.max.y); + break; + case 6: /* bottom left */ + r = Rect(p.x, r.min.y, r.max.x, p.y+1); + break; + case 8: /* bottom right */ + r = Rect(r.min.x, r.min.y, p.x+1, p.y+1); + break; + case 1: /* top edge */ + r = Rect(r.min.x, p.y, r.max.x, r.max.y); + break; + case 5: /* right edge */ + r = Rect(r.min.x, r.min.y, p.x+1, r.max.y); + break; + case 7: /* bottom edge */ + r = Rect(r.min.x, r.min.y, r.max.x, p.y+1); + break; + case 3: /* left edge */ + r = Rect(p.x, r.min.y, r.max.x, r.max.y); + break; + } + return canonrect(r); +} + +int +portion(int x, int lo, int hi) +{ + x -= lo; + hi -= lo; + if(x < hi/2){ + if(x < 20) + return 0; + } else { + if(x > hi-20) + return 2; + } + return 1; +} + +int +whichcorner(Rectangle r, Point p) +{ + int i, j; + + i = portion(p.x, r.min.x, r.max.x); + j = portion(p.y, r.min.y, r.max.y); + return 3*j+i; +} + +/* thread to allow fsysproc to synchronize window closing with main proc */ +void +winclosethread(void*) +{ + Window *w; + + threadsetname("winclosethread"); + for(;;){ + w = recvp(winclosechan); + wclose(w); + } +} + +/* + * Button 6 - keyboard toggle - has been pressed. + * Send event to keyboard, wait for button up, send that. + * Note: there is no coordinate translation done here; this + * is just about getting button 6 to the keyboard simulator. + */ +void +keyboardhide(void) +{ + send(wkeyboard->mc.c, mouse); + do + readmouse(mousectl); + while(mouse->buttons & (1<<5)); + send(wkeyboard->mc.c, mouse); +} + +void +mousethread(void*) +{ + int sending, inside, scrolling, moving; + Window *w, *winput; + Image *i; + Point xy; + Mouse tmp; + enum { + MReshape, + MMouse, + NALT + }; + static Alt alts[NALT+1]; + + threadsetname("mousethread"); + sending = FALSE; + scrolling = FALSE; + + alts[MReshape].c = mousectl->resizec; + alts[MReshape].v = nil; + alts[MReshape].op = CHANRCV; + alts[MMouse].c = mousectl->c; + alts[MMouse].v = &mousectl->Mouse; + alts[MMouse].op = CHANRCV; + alts[NALT].op = CHANEND; + + for(;;) + switch(alt(alts)){ + case MReshape: + resized(); + break; + case MMouse: + if(wkeyboard!=nil && (mouse->buttons & (1<<5))){ + keyboardhide(); + break; + } + Again: + moving = FALSE; + winput = input; + /* override everything for the keyboard window */ + if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){ + /* make sure it's on top; this call is free if it is */ + wtopme(wkeyboard); + winput = wkeyboard; + } + if(winput!=nil && !winput->deleted && winput->i!=nil){ + /* convert to logical coordinates */ + xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x); + xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y); + + /* the up and down scroll buttons are not subject to the usual rules */ + if((mouse->buttons&(8|16)) && !winput->mouseopen) + goto Sending; + + inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder)); + if(winput->mouseopen) + scrolling = FALSE; + else if(scrolling) + scrolling = mouse->buttons; + else + scrolling = mouse->buttons && ptinrect(xy, winput->scrollr); + /* topped will be zero or less if window has been bottomed */ + if(sending == FALSE && !scrolling && inborder(winput->screenr, mouse->xy) && winput->topped>0) + moving = TRUE; + else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1))) + sending = TRUE; + }else + sending = FALSE; + if(sending){ + Sending: + wsetcursor(winput, FALSE); + if(mouse->buttons == 0) + sending = FALSE; + tmp = mousectl->Mouse; + tmp.xy = xy; + send(winput->mc.c, &tmp); + continue; + } + if(moving && (mouse->buttons&7)){ + incref(winput); + sweeping = TRUE; + if(mouse->buttons & 3) + i = bandsize(winput); + else + i = drag(winput); + sweeping = FALSE; + if(i != nil){ + wcurrent(winput); + wsendctlmesg(winput, Reshaped, i->r, i); + } + wclose(winput); + continue; + } + w = wpointto(mouse->xy); + if(w!=nil && inborder(w->screenr, mouse->xy)) + riosetcursor(corners[whichcorner(w->screenr, mouse->xy)]); + else + wsetcursor(w, FALSE); + /* we're not sending the event, but if button is down maybe we should */ + if(mouse->buttons){ + /* w->topped will be zero or less if window has been bottomed */ + if(w==nil || (w==winput && w->topped>0)){ + if(mouse->buttons & 1){ + ; + }else if(mouse->buttons & 2){ + if(winput && !winput->deleted && !winput->mouseopen){ + incref(winput); + button2menu(winput); + wclose(winput); + } + }else if(mouse->buttons & 4) + button3menu(); + }else{ + /* if button 1 event in the window, top the window and wait for button up. */ + /* otherwise, top the window and pass the event on */ + if(wtop(mouse->xy) && (mouse->buttons!=1 || inborder(w->screenr, mouse->xy))) + goto Again; + goto Drain; + } + } + break; + + Drain: + do + readmouse(mousectl); + while(mousectl->buttons); + goto Again; /* recalculate mouse position, cursor */ + } +} + +int +wtopcmp(void *a, void *b) +{ + return (*(Window**)a)->topped - (*(Window**)b)->topped; +} + +void +resized(void) +{ + Image *im; + int i, j; + Rectangle r; + Point o, n; + Window *w; + + if(getwindow(display, Refnone) < 0) + error("failed to re-attach window"); + freescrtemps(); + view = screen; + freescreen(wscreen); + wscreen = allocscreen(screen, background, 0); + if(wscreen == nil) + error("can't re-allocate screen"); + draw(view, view->r, background, nil, ZP); + o = subpt(viewr.max, viewr.min); + n = subpt(view->clipr.max, view->clipr.min); + qsort(window, nwindow, sizeof(window[0]), wtopcmp); + for(i=0; ii->r, viewr.min); + r.min.x = (r.min.x*n.x)/o.x; + r.min.y = (r.min.y*n.y)/o.y; + r.max.x = (r.max.x*n.x)/o.x; + r.max.y = (r.max.y*n.y)/o.y; + r = rectaddpt(r, view->clipr.min); + if(!goodrect(r)) + r = rectsubpt(w->i->r, subpt(w->i->r.min, r.min)); + for(j=0; jchan, 0, DNofill); + r = ZR; + } else { + im = allocwindow(wscreen, r, Refbackup, DNofill); + } + if(im!=nil) + wsendctlmesg(w, Reshaped, r, im); + wclose(w); + } + viewr = view->r; + flushimage(display, 1); +} + +int +obscured(Window *w, Rectangle r, int i) +{ + Window *t; + + if(Dx(r) < font->height || Dy(r) < font->height) + return 1; + if(!rectclip(&r, screen->r)) + return 1; + for(; itopped <= w->topped) + continue; + if(Dx(t->screenr) == 0 || Dy(t->screenr) == 0 || rectXrect(r, t->screenr) == 0) + continue; + if(r.min.y < t->screenr.min.y) + if(!obscured(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i)) + return 0; + if(r.min.x < t->screenr.min.x) + if(!obscured(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i)) + return 0; + if(r.max.y > t->screenr.max.y) + if(!obscured(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i)) + return 0; + if(r.max.x > t->screenr.max.x) + if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i)) + return 0; + return 1; + } + return 0; +} + +static char* +shortlabel(char *s) +{ + enum { NBUF=60 }; + static char buf[NBUF*UTFmax]; + int i, k, l; + Rune r; + + l = utflen(s); + if(l < NBUF-2) + return estrdup(s); + k = i = 0; + while(i < NBUF/2){ + k += chartorune(&r, s+k); + i++; + } + strncpy(buf, s, k); + strcpy(buf+k, "..."); + while((l-i) >= NBUF/2-4){ + k += chartorune(&r, s+k); + i++; + } + strcat(buf, s+k); + return estrdup(buf); +} + +void +button3menu(void) +{ + int i, j, n; + + n = nhidden; + for(i=0; iscreenr, 0)){ + hidden[n++] = window[i]; + if(n >= nelem(hidden)) + break; + } + } + if(n >= nelem(menu3str)-Hidden) + n = nelem(menu3str)-Hidden-1; + for(i=0; ilabel); + } + for(i+=Hidden; menu3str[i]; i++){ + free(menu3str[i]); + menu3str[i] = nil; + } + sweeping = TRUE; + switch(i = menuhit(3, mousectl, &menu3, wscreen)){ + case -1: + break; + case New: + new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil); + break; + case Reshape: + resize(); + break; + case Move: + move(); + break; + case Delete: + delete(); + break; + case Hide: + hide(); + break; + case Exit: + confirmexit(); + break; + default: + unhide(i); + break; + } + sweeping = FALSE; +} + +void +button2menu(Window *w) +{ + if(w->scrolling) + menu2str[Scroll] = "noscroll"; + else + menu2str[Scroll] = "scroll"; + switch(menuhit(2, mousectl, &menu2, wscreen)){ + case Cut: + wsnarf(w); + wcut(w); + wscrdraw(w); + break; + + case Snarf: + wsnarf(w); + break; + + case Paste: + getsnarf(); + wpaste(w); + wscrdraw(w); + break; + + case Plumb: + wplumb(w); + break; + + case Look: + wlook(w); + break; + + case Send: + wsend(w); + break; + + case Scroll: + if(w->scrolling ^= 1) + wshow(w, w->nr); + break; + } + flushimage(display, 1); + wsendctlmesg(w, Wakeup, ZR, nil); +} + +Point +onscreen(Point p) +{ + p.x = max(screen->clipr.min.x, p.x); + p.x = min(screen->clipr.max.x-1, p.x); + p.y = max(screen->clipr.min.y, p.y); + p.y = min(screen->clipr.max.y-1, p.y); + return p; +} + +Image* +sweep(void) +{ + Image *i, *oi; + Rectangle r; + Point p0, p; + + i = nil; + menuing = TRUE; + riosetcursor(&crosscursor); + while(mouse->buttons == 0) + readmouse(mousectl); + p0 = onscreen(mouse->xy); + p = p0; + r.min = p; + r.max = p; + oi = nil; + while(mouse->buttons == 4){ + if(!eqpt(mouse->xy, p)){ + p = onscreen(mouse->xy); + r = canonrect(Rpt(p0, p)); + r = whichrect(r, p, whichcorner(r, p)); + if(Dx(r)>5 && Dy(r)>5){ + i = allocwindow(wscreen, r, Refnone, DNofill); + freeimage(oi); + if(i == nil) + goto Rescue; + oi = i; + border(i, r, Selborder, sizecol, ZP); + draw(i, insetrect(r, Selborder), cols[BACK], nil, ZP); + } + } + readmouse(mousectl); + } + if(mouse->buttons != 0) + goto Rescue; + if(i==nil || !goodrect(r)) + goto Rescue; + oi = i; + i = allocwindow(wscreen, oi->r, Refbackup, DNofill); + freeimage(oi); + if(i == nil) + goto Rescue; + riosetcursor(corners[whichcorner(i->r, mouse->xy)]); + goto Return; + + Rescue: + riosetcursor(nil); + freeimage(i); + i = nil; + flushimage(display, 1); + while(mouse->buttons) + readmouse(mousectl); + + Return: + menuing = FALSE; + return i; +} + +void +drawedge(Image **bp, Image *col, Rectangle r) +{ + Image *b = *bp; + if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r)) + originwindow(b, r.min, r.min); + else{ + freeimage(b); + b = allocwindow(wscreen, r, Refbackup, DNofill); + if(b != nil) draw(b, r, col, nil, ZP); + *bp = b; + } +} + +void +drawborder(Rectangle r, Image *col) +{ + static Image *b[4], *lastcol; + + if(col != lastcol){ + freeimage(b[0]), b[0] = nil; + freeimage(b[1]), b[1] = nil; + freeimage(b[2]), b[2] = nil; + freeimage(b[3]), b[3] = nil; + } + if(col != nil){ + r = canonrect(r); + drawedge(&b[0], col, Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y)); + drawedge(&b[1], col, Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth)); + drawedge(&b[2], col, Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y)); + drawedge(&b[3], col, Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y)); + } + lastcol = col; +} + +Image* +drag(Window *w) +{ + Point p, op, d, dm, om; + Rectangle r; + + menuing = TRUE; + riosetcursor(&boxcursor); + om = mouse->xy; + dm = subpt(om, w->screenr.min); + d = subpt(w->screenr.max, w->screenr.min); + op = subpt(om, dm); + drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), sizecol); + while(mouse->buttons==4){ + p = subpt(mouse->xy, dm); + if(!eqpt(p, op)){ + drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), sizecol); + op = p; + } + readmouse(mousectl); + } + r = Rect(op.x, op.y, op.x+d.x, op.y+d.y); + drawborder(r, nil); + p = mouse->xy; + riosetcursor(inborder(r, p) ? corners[whichcorner(r, p)] : nil); + menuing = FALSE; + if(mouse->buttons!=0 || !goodrect(r) || eqrect(r, w->screenr)){ + flushimage(display, 1); + while(mouse->buttons) + readmouse(mousectl); + return nil; + } + return allocwindow(wscreen, r, Refbackup, DNofill); +} + +Image* +bandsize(Window *w) +{ + Rectangle r, or; + Point p, startp; + int which, owhich, but; + + owhich = -1; + or = w->screenr; + but = mouse->buttons; + startp = onscreen(mouse->xy); + drawborder(or, sizecol); + while(mouse->buttons == but) { + p = onscreen(mouse->xy); + which = whichcorner(or, p); + if(which != owhich && which != 4 && (owhich|~which) & 1){ + owhich = which; + riosetcursor(corners[which]); + } + r = whichrect(or, p, owhich); + if(!eqrect(r, or) && goodrect(r)){ + drawborder(r, sizecol); + or = r; + } + readmouse(mousectl); + } + drawborder(or, nil); + if(!goodrect(or)) + riosetcursor(nil); + if(mouse->buttons!=0 || !goodrect(or) || eqrect(or, w->screenr) + || abs(p.x-startp.x)+abs(p.y-startp.y) <= 1){ + flushimage(display, 1); + while(mouse->buttons) + readmouse(mousectl); + return nil; + } + return allocwindow(wscreen, or, Refbackup, DNofill); +} + +Window* +pointto(int wait) +{ + Window *w; + + menuing = TRUE; + riosetcursor(&sightcursor); + while(mouse->buttons == 0) + readmouse(mousectl); + if(mouse->buttons == 4) + w = wpointto(mouse->xy); + else + w = nil; + if(wait){ + while(mouse->buttons){ + if(mouse->buttons!=4 && w !=nil){ /* cancel */ + riosetcursor(nil); + w = nil; + } + readmouse(mousectl); + } + if(w != nil && wpointto(mouse->xy) != w) + w = nil; + } + riosetcursor(nil); + menuing = FALSE; + return w; +} + +void +delete(void) +{ + Window *w; + + w = pointto(TRUE); + if(w!=nil) + wsendctlmesg(w, Deleted, ZR, nil); +} + +void +confirmexit(void) +{ + menuing = TRUE; + riosetcursor(&skull); + while(mouse->buttons == 0) + readmouse(mousectl); + if(mouse->buttons != 4) + goto Nope; + while(mouse->buttons){ + if(mouse->buttons != 4) + goto Nope; + readmouse(mousectl); + } + send(exitchan, nil); +Nope: + riosetcursor(nil); + menuing = FALSE; +} + +void +resize(void) +{ + Window *w; + Image *i; + + w = pointto(TRUE); + if(w == nil) + return; + incref(w); + i = sweep(); + if(i!=nil){ + wcurrent(w); + wsendctlmesg(w, Reshaped, i->r, i); + } + wclose(w); +} + +void +move(void) +{ + Window *w; + Image *i; + + w = pointto(FALSE); + if(w == nil) + return; + incref(w); + i = drag(w); + if(i!=nil){ + wcurrent(w); + wsendctlmesg(w, Reshaped, i->r, i); + } + wclose(w); +} + +int +whide(Window *w) +{ + Image *i; + int j; + + for(j=0; j