diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/rio |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/rio')
-rwxr-xr-x | sys/src/cmd/rio/dat.h | 342 | ||||
-rwxr-xr-x | sys/src/cmd/rio/data.c | 180 | ||||
-rwxr-xr-x | sys/src/cmd/rio/fns.h | 35 | ||||
-rwxr-xr-x | sys/src/cmd/rio/fsys.c | 700 | ||||
-rwxr-xr-x | sys/src/cmd/rio/mkfile | 30 | ||||
-rwxr-xr-x | sys/src/cmd/rio/rio.c | 1191 | ||||
-rwxr-xr-x | sys/src/cmd/rio/scrl.c | 183 | ||||
-rwxr-xr-x | sys/src/cmd/rio/time.c | 124 | ||||
-rwxr-xr-x | sys/src/cmd/rio/util.c | 149 | ||||
-rwxr-xr-x | sys/src/cmd/rio/wctl.c | 515 | ||||
-rwxr-xr-x | sys/src/cmd/rio/wind.c | 1699 | ||||
-rwxr-xr-x | sys/src/cmd/rio/xfid.c | 846 |
12 files changed, 5994 insertions, 0 deletions
diff --git a/sys/src/cmd/rio/dat.h b/sys/src/cmd/rio/dat.h new file mode 100755 index 000000000..8deb25199 --- /dev/null +++ b/sys/src/cmd/rio/dat.h @@ -0,0 +1,342 @@ +enum +{ + Qdir, /* /dev for this window */ + Qcons, + Qconsctl, + Qcursor, + Qwdir, + Qwinid, + Qwinname, + Qkbdin, + Qlabel, + Qmouse, + Qnew, + Qscreen, + Qsnarf, + Qtext, + Qwctl, + Qwindow, + Qwsys, /* directory of window directories */ + Qwsysdir, /* window directory, child of wsys */ + + QMAX, +}; + +enum +{ + Kscrolloneup = KF|0x20, + Kscrollonedown = KF|0x21, +}; + +#define STACK 8192 + +typedef struct Consreadmesg Consreadmesg; +typedef struct Conswritemesg Conswritemesg; +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; + +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, + Moved, + Refresh, + Movemouse, + Rawon, + Rawoff, + Holdon, + Holdoff, + Deleted, + Exited, +}; + +struct Wctlmesg +{ + int type; + Rectangle r; + Image *image; +}; + +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; + Mousectl mc; + Mouseinfo mouse; + Channel *ck; /* chan(Rune[10]) */ + Channel *cctl; /* chan(Wctlmesg)[20] */ + Channel *conswrite; /* chan(Conswritemesg) */ + Channel *consread; /* chan(Consreadmesg) */ + Channel *mouseread; /* chan(Mousereadmesg) */ + Channel *wctlread; /* chan(Consreadmesg) */ + 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; + char *label; + int pid; + char *dir; +}; + +int winborder(Window*, Point); +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 wbswidth(Window*, Rune); +int wclickmatch(Window*, int, int, int, uint*); +int wclose(Window*); +int wctlmesg(Window*, int, Rectangle, Image*); +int wctlmesg(Window*, int, Rectangle, Image*); +uint wbacknl(Window*, uint, uint); +uint winsert(Window*, Rune*, int, uint); +void waddraw(Window*, Rune*, int); +void wborder(Window*, int); +void wclosewin(Window*); +void wcurrent(Window*); +void wcut(Window*); +void wdelete(Window*, uint, uint); +void wdoubleclick(Window*, uint*, uint*); +void wfill(Window*); +void wframescroll(Window*, int); +void wkeyctl(Window*, Rune); +void wmousectl(Window*); +void wmovemouse(Window*, Point); +void wpaste(Window*); +void wplumb(Window*); +void wrefresh(Window*, Rectangle); +void wrepaint(Window*); +void wresize(Window*, Image*, int); +void wscrdraw(Window*); +void wscroll(Window*, int); +void wselect(Window*); +void wsendctlmesg(Window*, int, Rectangle, Image*); +void wsetcursor(Window*, int); +void wsetname(Window*); +void wsetorigin(Window*, uint, int); +void wsetpid(Window*, int, int); +void wsetselect(Window*, uint, uint); +void wshow(Window*, uint); +void wsnarf(Window*); +void wscrsleep(Window*, uint); +void wsetcols(Window*); + +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; + QLock active; + int flushing; /* another Xfid is trying to flush us */ + 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*) */ + Fid *fids[Nhash]; +}; + +Filsys* filsysinit(Channel*); +int filsysmount(Filsys*, int); +Xfid* filsysrespond(Filsys*, Xfid*, Fcall*, char*); +void filsyscancel(Xfid*); + +void wctlproc(void*); +void wctlthread(void*); + +void deletetimeoutproc(void*); + +struct Timer +{ + int dt; + int cancel; + Channel *c; /* chan(int) */ + Timer *next; +}; + +Font *font; +Mousectl *mousectl; +Mouse *mouse; +Keyboardctl *keyboardctl; +Display *display; +Image *view; +Screen *wscreen; +Cursor boxcursor; +Cursor crosscursor; +Cursor sightcursor; +Cursor whitearrow; +Cursor query; +Cursor *corners[9]; +Image *background; +Image *lightgrey; +Image *red; +Window **window; +Window *wkeyboard; /* window of simulated keyboard */ +int nwindow; +int snarffd; +Window *input; +QLock all; /* BUG */ +Filsys *filsys; +Window *hidden[100]; +int nhidden; +int nsnarf; +Rune* snarf; +int scrolling; +int maxtab; +Channel* winclosechan; +Channel* deletechan; +char *startdir; +int sweeping; +int wctlfd; +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 */ diff --git a/sys/src/cmd/rio/data.c b/sys/src/cmd/rio/data.c new file mode 100755 index 000000000..86e589b3e --- /dev/null +++ b/sys/src/cmd/rio/data.c @@ -0,0 +1,180 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#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 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), RGB24, 1, 0x777777FF); + red = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0xDD0000FF); +} diff --git a/sys/src/cmd/rio/fns.h b/sys/src/cmd/rio/fns.h new file mode 100755 index 000000000..d6e152e35 --- /dev/null +++ b/sys/src/cmd/rio/fns.h @@ -0,0 +1,35 @@ +void keyboardsend(char*, int); +int whide(Window*); +int wunhide(int); +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); +int min(int, int); +int max(int, int); +Rune* strrune(Rune*, Rune); +int isalnum(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); + +#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/sys/src/cmd/rio/fsys.c b/sys/src/cmd/rio/fsys.c new file mode 100755 index 000000000..f8cfa3e14 --- /dev/null +++ b/sys/src/cmd/rio/fsys.c @@ -0,0 +1,700 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#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"; + +int messagesize = 8192+IOHDRSZ; /* good start */ + +enum{ + DEBUG = 0 +}; + +Dirtab dirtab[]= +{ + { ".", QTDIR, Qdir, 0500|DMDIR }, + { "cons", QTFILE, Qcons, 0600 }, + { "cursor", QTFILE, Qcursor, 0600 }, + { "consctl", QTFILE, Qconsctl, 0200 }, + { "winid", QTFILE, Qwinid, 0400 }, + { "winname", QTFILE, Qwinname, 0400 }, + { "kbdin", QTFILE, Qkbdin, 0200 }, + { "label", QTFILE, Qlabel, 0600 }, + { "mouse", QTFILE, Qmouse, 0600 }, + { "screen", QTFILE, Qscreen, 0400 }, + { "snarf", QTFILE, Qsnarf, 0600 }, + { "text", QTFILE, Qtext, 0400 }, + { "wdir", QTFILE, Qwdir, 0600 }, + { "wctl", QTFILE, Qwctl, 0600 }, + { "window", QTFILE, Qwindow, 0400 }, + { "wsys", QTDIR, Qwsys, 0500|DMDIR }, + { 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* filsysnop(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); + sprint(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) < 0) + 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) +{ + int n, fd, pid, p0; + Filsys *fs; + Channel *c; + char buf[128]; + + fs = emalloc(sizeof(Filsys)); + if(cexecpipe(&fs->cfd, &fs->sfd) < 0) + goto Rescue; + fmtinstall('F', fcallfmt); + clockfd = open("/dev/time", OREAD|OCEXEC); + fd = open("/dev/user", OREAD); + strcpy(buf, "Jean-Paul_Belmondo"); + if(fd >= 0){ + n = read(fd, buf, sizeof buf-1); + if(n > 0) + buf[n] = 0; + close(fd); + } + fs->user = estrdup(buf); + fs->cxfidalloc = cxfidalloc; + pid = getpid(); + + /* + * Create and post wctl pipe + */ + if(cexecpipe(&p0, &wctlfd) < 0) + goto Rescue; + sprint(srvwctl, "/srv/riowctl.%s.%d", fs->user, pid); + post(srvwctl, "wctl", p0); + close(p0); + + /* + * Start server processes + */ + c = chancreate(sizeof(char*), 0); + if(c == nil) + error("wctl channel"); + proccreate(wctlproc, c, 4096); + threadcreate(wctlthread, c, 4096); + proccreate(filsysproc, fs, 10000); + + /* + * Post srv pipe + */ + sprint(srvpipe, "/srv/rio.%s.%d", fs->user, pid); + 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 = emalloc(messagesize+UTFmax); /* UTFmax for appending partial rune in xfidwrite */ + 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 */ + sprint(buf, "%d", id); + if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) < 0){ + fprint(2, "mount failed: %r\n"); + return -1; + } + if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){ + 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) + x->buf = malloc(messagesize); + 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; + 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; + if(strncmp(x->version, "9P2000", 6) != 0) + return filsysrespond(x->fs, x, &t, "unrecognized 9P version"); + t.version = "9P2000"; + 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*, Xfid *x, Fid*) +{ + sendp(x->c, xfidflush); + 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 +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; + 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; i<x->nwname; 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); + sendp(winclosechan, f->w); + f->w = w; + dir = dirtab; + goto Accept; + } + + if(snarffd>=0 && strcmp(x->wname[i], "snarf")==0) + break; /* don't serve /dev/snarf if it's provided in the environment */ + id = WIN(f->qid); + d = dirtab; + d++; /* skip '.' */ + for(; d->name; d++) + if(strcmp(x->wname[i], d->name) == 0){ + 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.nwqid<x->nwname){ + 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, so just disregard */ + x->mode &= ~(OTRUNC|OCEXEC); + /* can't execute or remove anything */ + if(x->mode==OEXEC || (x->mode&ORCLOSE)) + goto Deny; + switch(x->mode){ + 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[16]; + + 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, "out of memory"); + n = 0; + switch(FILE(f->qid)){ + case Qdir: + case Qwsysdir: + d = dirtab; + d++; /* first entry is '.' */ + for(i=0; d->name!=nil && i<e; i+=len){ + len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock); + if(len <= BIT16SZ) + break; + if(i >= o) + n += len; + d++; + } + break; + case Qwsys: + qlock(&all); + ids = emalloc(nwindow*sizeof(int)); + for(j=0; j<nwindow; j++) + ids[j] = window[j]->id; + qunlock(&all); + qsort(ids, nwindow, sizeof ids[0], idcmp); + dt.name = buf; + for(i=0, j=0; j<nwindow && i<e; i+=len){ + k = ids[j]; + sprint(dt.name, "%d", k); + dt.qid = QID(k, Qdir); + dt.type = QTDIR; + dt.perm = DMDIR|0700; + len = dostat(fs, k, &dt, b+n, x->count-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/sys/src/cmd/rio/mkfile b/sys/src/cmd/rio/mkfile new file mode 100755 index 000000000..5ffe21177 --- /dev/null +++ b/sys/src/cmd/rio/mkfile @@ -0,0 +1,30 @@ +</$objtype/mkfile +BIN=/$objtype/bin + +TARG=rio +OFILES=\ + rio.$O\ + data.$O\ + fsys.$O\ + scrl.$O\ + time.$O\ + util.$O\ + wctl.$O\ + wind.$O\ + xfid.$O\ + +HFILES=dat.h\ + fns.h\ + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + +</sys/src/cmd/mkone + +$O.out: /$objtype/lib/libdraw.a /$objtype/lib/libframe.a \ + /$objtype/lib/libthread.a /$objtype/lib/libplumb.a /$objtype/lib/libc.a +syms:V: + $CC -a $CFLAGS rio.c > syms + $CC -aa $CFLAGS *.c >>syms diff --git a/sys/src/cmd/rio/rio.c b/sys/src/cmd/rio/rio.c new file mode 100755 index 000000000..e699b0d69 --- /dev/null +++ b/sys/src/cmd/rio/rio.c @@ -0,0 +1,1191 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include <plumb.h> +#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); +Image *sweep(void); +Image *bandsize(Window*); +Image* drag(Window*, Rectangle*); +void refresh(Rectangle); +void resized(void); +Channel *exitchan; /* chan(int) */ +Channel *winclosechan; /* chan(Window*); */ +Rectangle viewr; +int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */ + +void mousethread(void*); +void keyboardthread(void*); +void winclosethread(void*); +void deletethread(void*); +void initcmd(void*); + +char *fontname; +int mainpid; + +enum +{ + New, + Reshape, + Move, + Delete, + Hide, + Exit, +}; + +enum +{ + Cut, + Paste, + Snarf, + Plumb, + Send, + Scroll, +}; + +char *menu2str[] = { + [Cut] "cut", + [Paste] "paste", + [Snarf] "snarf", + [Plumb] "plumb", + [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 [-f font] [-i initcmd] [-k kbdcmd] [-s]\n"); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *initstr, *kbdin, *s; + static void *arg[1]; + char buf[256]; + Image *i; + Rectangle r; + + if(strstr(argv[0], ".out") == nil){ + menu3str[Exit] = nil; + Hidden--; + } + initstr = nil; + kbdin = nil; + maxtab = 0; + ARGBEGIN{ + case 'f': + fontname = ARGF(); + if(fontname == nil) + usage(); + break; + case 'i': + initstr = ARGF(); + if(initstr == nil) + usage(); + break; + case 'k': + if(kbdin != nil) + usage(); + kbdin = ARGF(); + if(kbdin == nil) + usage(); + break; + case 's': + scrolling = TRUE; + break; + }ARGEND + + mainpid = getpid(); + if(getwd(buf, sizeof buf) == nil) + startdir = estrdup("."); + else + startdir = estrdup(buf); + if(fontname == nil) + fontname = getenv("font"); + if(fontname == nil) + fontname = "/lib/font/bit/lucm/unicode.9.font"; + s = getenv("tabstop"); + if(s != nil) + maxtab = strtol(s, nil, 0); + if(maxtab == 0) + maxtab = 4; + free(s); + /* 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); + + if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){ + fprint(2, "rio: can't open display: %r\n"); + exits("display open"); + } + iconinit(); + view = screen; + viewr = view->r; + mousectl = initmouse(nil, screen); + if(mousectl == nil) + error("can't find mouse"); + mouse = mousectl; + keyboardctl = initkeyboard(nil); + if(keyboardctl == nil) + error("can't find keyboard"); + wscreen = allocscreen(screen, background, 0); + if(wscreen == nil) + error("can't allocate screen"); + draw(view, viewr, background, nil, ZP); + flushimage(display, 1); + + exitchan = chancreate(sizeof(int), 0); + winclosechan = chancreate(sizeof(Window*), 0); + deletechan = chancreate(sizeof(char*), 0); + + timerinit(); + threadcreate(keyboardthread, nil, STACK); + threadcreate(mousethread, nil, STACK); + threadcreate(winclosethread, nil, STACK); + threadcreate(deletethread, 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.max.x = r.min.x+300; + r.max.y = r.min.y+80; + i = allocwindow(wscreen, r, Refbackup, DWhite); + 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(); + 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); + if(fd < 0) + return; + /* snarf buffer could be huge, so fprint will truncate; do it in blocks */ + for(i=0; i<nsnarf; i+=n){ + n = nsnarf-i; + if(n >= 256) + n = 256; + if(fprint(fd, "%.*S", n, snarf+i) < 0) + break; + } + close(fd); +} + +void +getsnarf(void) +{ + int i, n, nb, nulls; + char *sn, buf[1024]; + + if(snarffd < 0) + return; + sn = nil; + i = 0; + seek(snarffd, 0, 0); + while((n = read(snarffd, buf, sizeof buf)) > 0){ + sn = erealloc(sn, i+n+1); + memmove(sn+i, buf, n); + i += n; + sn[i] = 0; + } + if(i > 0){ + snarf = runerealloc(snarf, i+1); + cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls); + free(sn); + } +} + +void +initcmd(void *arg) +{ + char *cmd; + + cmd = arg; + rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG); + 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(); + exits(msg); + return 0; +} + +void +killprocs(void) +{ + int i; + + for(i=0; i<nwindow; i++) + postnote(PNGROUP, window[i]->pid, "hangup"); +} + +void +keyboardthread(void*) +{ + Rune buf[2][20], *rp; + int n, i; + + threadsetname("keyboardthread"); + n = 0; + for(;;){ + rp = buf[n]; + n = 1-n; + recv(keyboardctl->c, rp); + for(i=1; i<nelem(buf[0])-1; i++) + if(nbrecv(keyboardctl->c, rp+i) <= 0) + break; + rp[i] = L'\0'; + if(input != nil) + sendp(input->ck, rp); + } +} + +/* + * Used by /dev/kbdin + */ +void +keyboardsend(char *s, int cnt) +{ + Rune *r; + int i, nb, nr; + + r = runemalloc(cnt); + /* BUGlet: partial runes will be converted to error runes */ + cvttorunes(s, cnt, r, &nb, &nr, nil); + for(i=0; i<nr; i++) + send(keyboardctl->c, &r[i]); + free(r); +} + +int +portion(int x, int lo, int hi) +{ + x -= lo; + hi -= lo; + if(x < 20) + return 0; + if(x > hi-20) + return 2; + return 1; +} + +int +whichcorner(Window *w, Point p) +{ + int i, j; + + i = portion(p.x, w->screenr.min.x, w->screenr.max.x); + j = portion(p.y, w->screenr.min.y, w->screenr.max.y); + return 3*j+i; +} + +void +cornercursor(Window *w, Point p, int force) +{ + if(w!=nil && winborder(w, p)) + riosetcursor(corners[whichcorner(w, p)], force); + else + wsetcursor(w, force); +} + +/* thread to allow fsysproc to synchronize window closing with main proc */ +void +winclosethread(void*) +{ + Window *w; + + threadsetname("winclosethread"); + for(;;){ + w = recvp(winclosechan); + wclose(w); + } +} + +/* thread to make Deleted windows that the client still holds disappear offscreen after an interval */ +void +deletethread(void*) +{ + char *s; + Image *i; + + threadsetname("deletethread"); + for(;;){ + s = recvp(deletechan); + i = namedimage(display, s); + if(i != nil){ + /* move it off-screen to hide it, since client is slow in letting it go */ + originwindow(i, i->r.min, view->r.max); + } + freeimage(i); + free(s); + } +} + +void +deletetimeoutproc(void *v) +{ + char *s; + + s = v; + sleep(750); /* remove window from screen after 3/4 of a second */ + sendp(deletechan, s); +} + +/* + * 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, band; + Window *oin, *w, *winput; + Image *i; + Rectangle r; + Point xy; + Mouse tmp; + enum { + MReshape, + MMouse, + NALT + }; + static Alt alts[NALT+1]; + + threadsetname("mousethread"); + sending = FALSE; + scrolling = FALSE; + moving = 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: + 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->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 && winborder(winput, mouse->xy) && winput->topped>0){ + moving = TRUE; + }else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1))) + sending = TRUE; + }else + sending = FALSE; + if(sending){ + Sending: + if(mouse->buttons == 0){ + cornercursor(winput, mouse->xy, 0); + sending = FALSE; + }else + wsetcursor(winput, 0); + tmp = mousectl->Mouse; + tmp.xy = xy; + send(winput->mc.c, &tmp); + continue; + } + w = wpointto(mouse->xy); + /* change cursor if over anyone's border */ + if(w != nil) + cornercursor(w, mouse->xy, 0); + else + riosetcursor(nil, 0); + if(moving && (mouse->buttons&7)){ + oin = winput; + band = mouse->buttons & 3; + sweeping = 1; + if(band) + i = bandsize(winput); + else + i = drag(winput, &r); + sweeping = 0; + if(i != nil){ + if(winput == oin){ + if(band) + wsendctlmesg(winput, Reshaped, i->r, i); + else + wsendctlmesg(winput, Moved, r, i); + cornercursor(winput, mouse->xy, 1); + }else + freeimage(i); + } + } + if(w != nil) + cornercursor(w, mouse->xy, 0); + /* 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->mouseopen) + button2menu(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 || winborder(w, mouse->xy))) + goto Again; + goto Drain; + } + } + moving = FALSE; + break; + + Drain: + do + readmouse(mousectl); + while(mousectl->buttons); + moving = FALSE; + goto Again; /* recalculate mouse position, cursor */ + } +} + +void +resized(void) +{ + Image *im; + int i, j, ishidden; + 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); + for(i=0; i<nwindow; i++){ + w = window[i]; + if(w->deleted) + continue; + r = rectsubpt(w->i->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, screen->clipr.min); + ishidden = 0; + for(j=0; j<nhidden; j++) + if(w == hidden[j]){ + ishidden = 1; + break; + } + if(ishidden){ + im = allocimage(display, r, screen->chan, 0, DWhite); + r = ZR; + }else + im = allocwindow(wscreen, r, Refbackup, DWhite); + if(im) + wsendctlmesg(w, Reshaped, r, im); + } + viewr = screen->r; + flushimage(display, 1); +} + +void +button3menu(void) +{ + int i; + + for(i=0; i<nhidden; i++) + menu3str[i+Hidden] = hidden[i]->label; + menu3str[i+Hidden] = nil; + + sweeping = 1; + 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: + if(Hidden > Exit){ + send(exitchan, nil); + break; + } + /* else fall through */ + default: + unhide(i); + break; + } + sweeping = 0; +} + +void +button2menu(Window *w) +{ + if(w->deleted) + return; + incref(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 Send: + getsnarf(); + wsnarf(w); + if(nsnarf == 0) + break; + if(w->rawing){ + waddraw(w, snarf, nsnarf); + if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') + waddraw(w, L"\n", 1); + }else{ + winsert(w, snarf, nsnarf, w->nr); + if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') + winsert(w, L"\n", 1, w->nr); + } + wsetselect(w, w->nr, w->nr); + wshow(w, w->nr); + break; + + case Scroll: + if(w->scrolling ^= 1) + wshow(w, w->nr); + break; + } + wclose(w); + wsendctlmesg(w, Wakeup, ZR, nil); + flushimage(display, 1); +} + +Point +onscreen(Point p) +{ + p.x = max(screen->clipr.min.x, p.x); + p.x = min(screen->clipr.max.x, p.x); + p.y = max(screen->clipr.min.y, p.y); + p.y = min(screen->clipr.max.y, p.y); + return p; +} + +Image* +sweep(void) +{ + Image *i, *oi; + Rectangle r; + Point p0, p; + + i = nil; + menuing = TRUE; + riosetcursor(&crosscursor, 1); + while(mouse->buttons == 0) + readmouse(mousectl); + p0 = onscreen(mouse->xy); + p = p0; + r.min = p; + r.max = p; + oi = nil; + while(mouse->buttons == 4){ + readmouse(mousectl); + if(mouse->buttons != 4 && mouse->buttons != 0) + break; + if(!eqpt(mouse->xy, p)){ + p = onscreen(mouse->xy); + r = canonrect(Rpt(p0, p)); + if(Dx(r)>5 && Dy(r)>5){ + i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */ + freeimage(oi); + if(i == nil) + goto Rescue; + oi = i; + border(i, r, Selborder, red, ZP); + flushimage(display, 1); + } + } + } + if(mouse->buttons != 0) + goto Rescue; + if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height) + goto Rescue; + oi = i; + i = allocwindow(wscreen, oi->r, Refbackup, DWhite); + freeimage(oi); + if(i == nil) + goto Rescue; + border(i, r, Selborder, red, ZP); + cornercursor(input, mouse->xy, 1); + goto Return; + + Rescue: + freeimage(i); + i = nil; + cornercursor(input, mouse->xy, 1); + while(mouse->buttons) + readmouse(mousectl); + + Return: + moveto(mousectl, mouse->xy); /* force cursor update; ugly */ + menuing = FALSE; + return i; +} + +void +drawedge(Image **bp, 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); + *bp = allocwindow(wscreen, r, Refbackup, DRed); + } +} + +void +drawborder(Rectangle r, int show) +{ + static Image *b[4]; + int i; + if(show == 0){ + for(i = 0; i < 4; i++){ + freeimage(b[i]); + b[i] = nil; + } + }else{ + r = canonrect(r); + drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y)); + drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth)); + drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y)); + drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y)); + } +} + +Image* +drag(Window *w, Rectangle *rp) +{ + Image *i, *ni; + Point p, op, d, dm, om; + Rectangle r; + + i = w->i; + menuing = TRUE; + om = mouse->xy; + riosetcursor(&boxcursor, 1); + dm = subpt(mouse->xy, w->screenr.min); + d = subpt(i->r.max, i->r.min); + op = subpt(mouse->xy, dm); + drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1); + flushimage(display, 1); + 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), 1); + flushimage(display, 1); + op = p; + } + readmouse(mousectl); + } + r = Rect(op.x, op.y, op.x+d.x, op.y+d.y); + drawborder(r, 0); + cornercursor(w, mouse->xy, 1); + moveto(mousectl, mouse->xy); /* force cursor update; ugly */ + menuing = FALSE; + flushimage(display, 1); + if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){ + moveto(mousectl, om); + while(mouse->buttons) + readmouse(mousectl); + *rp = Rect(0, 0, 0, 0); + return nil; + } + draw(ni, ni->r, i, nil, i->r.min); + *rp = r; + return ni; +} + +Point +cornerpt(Rectangle r, Point p, int which) +{ + switch(which){ + case 0: /* top left */ + p = Pt(r.min.x, r.min.y); + break; + case 2: /* top right */ + p = Pt(r.max.x,r.min.y); + break; + case 6: /* bottom left */ + p = Pt(r.min.x, r.max.y); + break; + case 8: /* bottom right */ + p = Pt(r.max.x, r.max.y); + break; + case 1: /* top edge */ + p = Pt(p.x,r.min.y); + break; + case 5: /* right edge */ + p = Pt(r.max.x, p.y); + break; + case 7: /* bottom edge */ + p = Pt(p.x, r.max.y); + break; + case 3: /* left edge */ + p = Pt(r.min.x, p.y); + break; + } + return p; +} + +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, r.max.y); + break; + case 6: /* bottom left */ + r = Rect(p.x, r.min.y, r.max.x, p.y); + break; + case 8: /* bottom right */ + r = Rect(r.min.x, r.min.y, p.x, p.y); + 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, r.max.y); + break; + case 7: /* bottom edge */ + r = Rect(r.min.x, r.min.y, r.max.x, p.y); + break; + case 3: /* left edge */ + r = Rect(p.x, r.min.y, r.max.x, r.max.y); + break; + } + return canonrect(r); +} + +Image* +bandsize(Window *w) +{ + Image *i; + Rectangle r, or; + Point p, startp; + int which, but; + + p = mouse->xy; + but = mouse->buttons; + which = whichcorner(w, p); + p = cornerpt(w->screenr, p, which); + wmovemouse(w, p); + readmouse(mousectl); + r = whichrect(w->screenr, p, which); + drawborder(r, 1); + or = r; + startp = p; + + while(mouse->buttons == but){ + p = onscreen(mouse->xy); + r = whichrect(w->screenr, p, which); + if(!eqrect(r, or) && goodrect(r)){ + drawborder(r, 1); + flushimage(display, 1); + or = r; + } + readmouse(mousectl); + } + p = mouse->xy; + drawborder(or, 0); + flushimage(display, 1); + wsetcursor(w, 1); + if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){ + while(mouse->buttons) + readmouse(mousectl); + return nil; + } + if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1) + return nil; + i = allocwindow(wscreen, or, Refbackup, DWhite); + if(i == nil) + return nil; + border(i, r, Selborder, red, ZP); + return i; +} + +Window* +pointto(int wait) +{ + Window *w; + + menuing = TRUE; + riosetcursor(&sightcursor, 1); + 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 */ + cornercursor(input, mouse->xy, 0); + w = nil; + } + readmouse(mousectl); + } + if(w != nil && wpointto(mouse->xy) != w) + w = nil; + } + cornercursor(input, mouse->xy, 0); + moveto(mousectl, mouse->xy); /* force cursor update; ugly */ + menuing = FALSE; + return w; +} + +void +delete(void) +{ + Window *w; + + w = pointto(TRUE); + if(w) + wsendctlmesg(w, Deleted, ZR, nil); +} + +void +resize(void) +{ + Window *w; + Image *i; + + w = pointto(TRUE); + if(w == nil) + return; + i = sweep(); + if(i) + wsendctlmesg(w, Reshaped, i->r, i); +} + +void +move(void) +{ + Window *w; + Image *i; + Rectangle r; + + w = pointto(FALSE); + if(w == nil) + return; + i = drag(w, &r); + if(i) + wsendctlmesg(w, Moved, r, i); + cornercursor(input, mouse->xy, 1); +} + +int +whide(Window *w) +{ + Image *i; + int j; + + for(j=0; j<nhidden; j++) + if(hidden[j] == w) /* already hidden */ + return -1; + i = allocimage(display, w->screenr, w->i->chan, 0, DWhite); + if(i){ + hidden[nhidden++] = w; + wsendctlmesg(w, Reshaped, ZR, i); + return 1; + } + return 0; +} + +int +wunhide(int h) +{ + Image *i; + Window *w; + + w = hidden[h]; + i = allocwindow(wscreen, w->i->r, Refbackup, DWhite); + if(i){ + --nhidden; + memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*)); + wsendctlmesg(w, Reshaped, w->i->r, i); + return 1; + } + return 0; +} + +void +hide(void) +{ + Window *w; + + w = pointto(TRUE); + if(w == nil) + return; + whide(w); +} + +void +unhide(int h) +{ + Window *w; + + h -= Hidden; + w = hidden[h]; + if(w == nil) + return; + wunhide(h); +} + +Window* +new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv) +{ + Window *w; + Mousectl *mc; + Channel *cm, *ck, *cctl, *cpid; + void **arg; + + if(i == nil) + return nil; + cm = chancreate(sizeof(Mouse), 0); + ck = chancreate(sizeof(Rune*), 0); + cctl = chancreate(sizeof(Wctlmesg), 4); + cpid = chancreate(sizeof(int), 0); + if(cm==nil || ck==nil || cctl==nil) + error("new: channel alloc failed"); + mc = emalloc(sizeof(Mousectl)); + *mc = *mousectl; + mc->image = i; + mc->c = cm; + w = wmk(i, mc, ck, cctl, scrollit); + free(mc); /* wmk copies *mc */ + window = erealloc(window, ++nwindow*sizeof(Window*)); + window[nwindow-1] = w; + if(hideit){ + hidden[nhidden++] = w; + w->screenr = ZR; + } + threadcreate(winctl, w, 8192); + if(!hideit) + wcurrent(w); + flushimage(display, 1); + if(pid == 0){ + arg = emalloc(5*sizeof(void*)); + arg[0] = w; + arg[1] = cpid; + arg[2] = cmd; + if(argv == nil) + arg[3] = rcargv; + else + arg[3] = argv; + arg[4] = dir; + proccreate(winshell, arg, 8192); + pid = recvul(cpid); + free(arg); + } + if(pid == 0){ + /* window creation failed */ + wsendctlmesg(w, Deleted, ZR, nil); + chanfree(cpid); + return nil; + } + wsetpid(w, pid, 1); + wsetname(w); + if(dir) + w->dir = estrdup(dir); + chanfree(cpid); + return w; +} diff --git a/sys/src/cmd/rio/scrl.c b/sys/src/cmd/rio/scrl.c new file mode 100755 index 000000000..47480ac90 --- /dev/null +++ b/sys/src/cmd/rio/scrl.c @@ -0,0 +1,183 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include "dat.h" +#include "fns.h" + +static Image *scrtmp; + +static +void +scrtemps(void) +{ + int h; + + if(scrtmp) + return; + h = BIG*Dy(screen->r); + scrtmp = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, DWhite); + if(scrtmp == nil) + error("scrtemps"); +} + +void +freescrtemps(void) +{ + freeimage(scrtmp); + scrtmp = nil; +} + +static +Rectangle +scrpos(Rectangle r, uint p0, uint p1, uint tot) +{ + Rectangle q; + int h; + + q = r; + h = q.max.y-q.min.y; + if(tot == 0) + return q; + if(tot > 1024*1024){ + tot>>=10; + p0>>=10; + p1>>=10; + } + if(p0 > 0) + q.min.y += h*p0/tot; + if(p1 < tot) + q.max.y -= h*(tot-p1)/tot; + if(q.max.y < q.min.y+2){ + if(q.min.y+2 <= r.max.y) + q.max.y = q.min.y+2; + else + q.min.y = q.max.y-2; + } + return q; +} + +void +wscrdraw(Window *w) +{ + Rectangle r, r1, r2; + Image *b; + + scrtemps(); + if(w->i == nil) + error("scrdraw"); + r = w->scrollr; + b = scrtmp; + r1 = r; + r1.min.x = 0; + r1.max.x = Dx(r); + r2 = scrpos(r1, w->org, w->org+w->nchars, w->nr); + if(!eqrect(r2, w->lastsr)){ + w->lastsr = r2; + /* move r1, r2 to (0,0) to avoid clipping */ + r2 = rectsubpt(r2, r1.min); + r1 = rectsubpt(r1, r1.min); + draw(b, r1, w->cols[BORD], nil, ZP); + draw(b, r2, w->cols[BACK], nil, ZP); + r2.min.x = r2.max.x-1; + draw(b, r2, w->cols[BORD], nil, ZP); + draw(w->i, r, b, nil, Pt(0, r1.min.y)); + } +} + +void +wscrsleep(Window *w, uint dt) +{ + Timer *timer; + int y, b; + static Alt alts[3]; + + timer = timerstart(dt); + y = w->mc.xy.y; + b = w->mc.buttons; + alts[0].c = timer->c; + alts[0].v = nil; + alts[0].op = CHANRCV; + alts[1].c = w->mc.c; + alts[1].v = &w->mc.Mouse; + alts[1].op = CHANRCV; + alts[2].op = CHANEND; + for(;;) + switch(alt(alts)){ + case 0: + timerstop(timer); + return; + case 1: + if(abs(w->mc.xy.y-y)>2 || w->mc.buttons!=b){ + timercancel(timer); + return; + } + break; + } +} + +void +wscroll(Window *w, int but) +{ + uint p0, oldp0; + Rectangle s; + int x, y, my, h, first; + + s = insetrect(w->scrollr, 1); + h = s.max.y-s.min.y; + x = (s.min.x+s.max.x)/2; + oldp0 = ~0; + first = TRUE; + do{ + flushimage(display, 1); + if(w->mc.xy.x<s.min.x || s.max.x<=w->mc.xy.x){ + readmouse(&w->mc); + }else{ + my = w->mc.xy.y; + if(my < s.min.y) + my = s.min.y; + if(my >= s.max.y) + my = s.max.y; + if(!eqpt(w->mc.xy, Pt(x, my))){ + wmovemouse(w, Pt(x, my)); + readmouse(&w->mc); /* absorb event generated by moveto() */ + } + if(but == 2){ + y = my; + if(y > s.max.y-2) + y = s.max.y-2; + if(w->nr > 1024*1024) + p0 = ((w->nr>>10)*(y-s.min.y)/h)<<10; + else + p0 = w->nr*(y-s.min.y)/h; + if(oldp0 != p0) + wsetorigin(w, p0, FALSE); + oldp0 = p0; + readmouse(&w->mc); + continue; + } + if(but == 1) + p0 = wbacknl(w, w->org, (my-s.min.y)/w->font->height); + else + p0 = w->org+frcharofpt(w, Pt(s.max.x, my)); + if(oldp0 != p0) + wsetorigin(w, p0, TRUE); + oldp0 = p0; + /* debounce */ + if(first){ + flushimage(display, 1); + sleep(200); + nbrecv(w->mc.c, &w->mc.Mouse); + first = FALSE; + } + wscrsleep(w, 100); + } + }while(w->mc.buttons & (1<<(but-1))); + while(w->mc.buttons) + readmouse(&w->mc); +} diff --git a/sys/src/cmd/rio/time.c b/sys/src/cmd/rio/time.c new file mode 100755 index 000000000..7046ccbba --- /dev/null +++ b/sys/src/cmd/rio/time.c @@ -0,0 +1,124 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include "dat.h" +#include "fns.h" + +static Channel* ctimer; /* chan(Timer*)[100] */ +static Timer *timer; + +static +uint +msec(void) +{ + return nsec()/1000000; +} + +void +timerstop(Timer *t) +{ + t->next = timer; + timer = t; +} + +void +timercancel(Timer *t) +{ + t->cancel = TRUE; +} + +static +void +timerproc(void*) +{ + int i, nt, na, dt, del; + Timer **t, *x; + uint old, new; + + rfork(RFFDG); + threadsetname("TIMERPROC"); + t = nil; + na = 0; + nt = 0; + old = msec(); + for(;;){ + sleep(1); /* will sleep minimum incr */ + new = msec(); + dt = new-old; + old = new; + if(dt < 0) /* timer wrapped; go around, losing a tick */ + continue; + for(i=0; i<nt; i++){ + x = t[i]; + x->dt -= dt; + del = 0; + if(x->cancel){ + timerstop(x); + del = 1; + }else if(x->dt <= 0){ + /* + * avoid possible deadlock if client is + * now sending on ctimer + */ + if(nbsendul(x->c, 0) > 0) + del = 1; + } + if(del){ + memmove(&t[i], &t[i+1], (nt-i-1)*sizeof t[0]); + --nt; + --i; + } + } + if(nt == 0){ + x = recvp(ctimer); + gotit: + if(nt == na){ + na += 10; + t = realloc(t, na*sizeof(Timer*)); + if(t == nil) + abort(); + } + t[nt++] = x; + old = msec(); + } + if(nbrecv(ctimer, &x) > 0) + goto gotit; + } +} + +void +timerinit(void) +{ + ctimer = chancreate(sizeof(Timer*), 100); + proccreate(timerproc, nil, STACK); +} + +/* + * timeralloc() and timerfree() don't lock, so can only be + * called from the main proc. + */ + +Timer* +timerstart(int dt) +{ + Timer *t; + + t = timer; + if(t) + timer = timer->next; + else{ + t = emalloc(sizeof(Timer)); + t->c = chancreate(sizeof(int), 0); + } + t->next = nil; + t->dt = dt; + t->cancel = FALSE; + sendp(ctimer, t); + return t; +} diff --git a/sys/src/cmd/rio/util.c b/sys/src/cmd/rio/util.c new file mode 100755 index 000000000..8a35d1d75 --- /dev/null +++ b/sys/src/cmd/rio/util.c @@ -0,0 +1,149 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include "dat.h" +#include "fns.h" + +void +cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls) +{ + uchar *q; + Rune *s; + int j, w; + + /* + * Always guaranteed that n bytes may be interpreted + * without worrying about partial runes. This may mean + * reading up to UTFmax-1 more bytes than n; the caller + * knows this. If n is a firm limit, the caller should + * set p[n] = 0. + */ + q = (uchar*)p; + s = r; + for(j=0; j<n; j+=w){ + if(*q < Runeself){ + w = 1; + *s = *q++; + }else{ + w = chartorune(s, (char*)q); + q += w; + } + if(*s) + s++; + else if(nulls) + *nulls = TRUE; + } + *nb = (char*)q-p; + *nr = s-r; +} + +void +error(char *s) +{ + fprint(2, "rio: %s: %r\n", s); + if(errorshouldabort) + abort(); + threadexitsall("error"); +} + +void* +erealloc(void *p, uint n) +{ + p = realloc(p, n); + if(p == nil) + error("realloc failed"); + return p; +} + +void* +emalloc(uint n) +{ + void *p; + + p = malloc(n); + if(p == nil) + error("malloc failed"); + memset(p, 0, n); + return p; +} + +char* +estrdup(char *s) +{ + char *p; + + p = malloc(strlen(s)+1); + if(p == nil) + error("strdup failed"); + strcpy(p, s); + return p; +} + +int +isalnum(Rune c) +{ + /* + * Hard to get absolutely right. Use what we know about ASCII + * and assume anything above the Latin control characters is + * potentially an alphanumeric. + */ + if(c <= ' ') + return FALSE; + if(0x7F<=c && c<=0xA0) + return FALSE; + if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) + return FALSE; + return TRUE; +} + +Rune* +strrune(Rune *s, Rune c) +{ + Rune c1; + + if(c == 0) { + while(*s++) + ; + return s-1; + } + + while(c1 = *s++) + if(c1 == c) + return s-1; + return nil; +} + +int +min(int a, int b) +{ + if(a < b) + return a; + return b; +} + +int +max(int a, int b) +{ + if(a > b) + return a; + return b; +} + +char* +runetobyte(Rune *r, int n, int *ip) +{ + char *s; + int m; + + s = emalloc(n*UTFmax+1); + m = snprint(s, n*UTFmax+1, "%.*S", n, r); + *ip = m; + return s; +} + diff --git a/sys/src/cmd/rio/wctl.c b/sys/src/cmd/rio/wctl.c new file mode 100755 index 000000000..07b882c9f --- /dev/null +++ b/sys/src/cmd/rio/wctl.c @@ -0,0 +1,515 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include <plumb.h> +#include "dat.h" +#include "fns.h" +#include <ctype.h> + +char Ebadwr[] = "bad rectangle in wctl request"; +char Ewalloc[] = "window allocation failed in wctl request"; + +/* >= Top are disallowed if mouse button is pressed */ +enum +{ + New, + Resize, + Move, + Scroll, + Noscroll, + Set, + Top, + Bottom, + Current, + Hide, + Unhide, + Delete, +}; + +static char *cmds[] = { + [New] = "new", + [Resize] = "resize", + [Move] = "move", + [Scroll] = "scroll", + [Noscroll] = "noscroll", + [Set] = "set", + [Top] = "top", + [Bottom] = "bottom", + [Current] = "current", + [Hide] = "hide", + [Unhide] = "unhide", + [Delete] = "delete", + nil +}; + +enum +{ + Cd, + Deltax, + Deltay, + Hidden, + Id, + Maxx, + Maxy, + Minx, + Miny, + PID, + R, + Scrolling, + Noscrolling, +}; + +static char *params[] = { + [Cd] = "-cd", + [Deltax] = "-dx", + [Deltay] = "-dy", + [Hidden] = "-hide", + [Id] = "-id", + [Maxx] = "-maxx", + [Maxy] = "-maxy", + [Minx] = "-minx", + [Miny] = "-miny", + [PID] = "-pid", + [R] = "-r", + [Scrolling] = "-scroll", + [Noscrolling] = "-noscroll", + nil +}; + +/* + * Check that newly created window will be of manageable size + */ +int +goodrect(Rectangle r) +{ + if(!eqrect(canonrect(r), r)) + return 0; + if(Dx(r)<100 || Dy(r)<3*font->height) + return 0; + /* must have some screen and border visible so we can move it out of the way */ + if(Dx(r) >= Dx(screen->r) && Dy(r) >= Dy(screen->r)) + return 0; + /* reasonable sizes only please */ + if(Dx(r) > BIG*Dx(screen->r)) + return 0; + if(Dy(r) > BIG*Dx(screen->r)) + return 0; + return 1; +} + +static +int +word(char **sp, char *tab[]) +{ + char *s, *t; + int i; + + s = *sp; + while(isspace(*s)) + s++; + t = s; + while(*s!='\0' && !isspace(*s)) + s++; + for(i=0; tab[i]!=nil; i++) + if(strncmp(tab[i], t, strlen(tab[i])) == 0){ + *sp = s; + return i; + } + return -1; +} + +int +set(int sign, int neg, int abs, int pos) +{ + if(sign < 0) + return neg; + if(sign > 0) + return pos; + return abs; +} + +Rectangle +newrect(void) +{ + static int i = 0; + int minx, miny, dx, dy; + + dx = min(600, Dx(screen->r) - 2*Borderwidth); + dy = min(400, Dy(screen->r) - 2*Borderwidth); + minx = 32 + 16*i; + miny = 32 + 16*i; + i++; + i %= 10; + + return Rect(minx, miny, minx+dx, miny+dy); +} + +void +shift(int *minp, int *maxp, int min, int max) +{ + if(*minp < min){ + *maxp += min-*minp; + *minp = min; + } + if(*maxp > max){ + *minp += max-*maxp; + *maxp = max; + } +} + +Rectangle +rectonscreen(Rectangle r) +{ + shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x); + shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y); + return r; +} + +/* permit square brackets, in the manner of %R */ +int +riostrtol(char *s, char **t) +{ + int n; + + while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='[')) + s++; + if(*s == '[') + s++; + n = strtol(s, t, 10); + if(*t != s) + while((*t)[0] == ']') + (*t)++; + return n; +} + + +int +parsewctl(char **argp, Rectangle r, Rectangle *rp, int *pidp, int *idp, int *hiddenp, int *scrollingp, char **cdp, char *s, char *err) +{ + int cmd, param, xy, sign; + char *t; + + *pidp = 0; + *hiddenp = 0; + *scrollingp = scrolling; + *cdp = nil; + cmd = word(&s, cmds); + if(cmd < 0){ + strcpy(err, "unrecognized wctl command"); + return -1; + } + if(cmd == New) + r = newrect(); + + strcpy(err, "missing or bad wctl parameter"); + while((param = word(&s, params)) >= 0){ + switch(param){ /* special cases */ + case Hidden: + *hiddenp = 1; + continue; + case Scrolling: + *scrollingp = 1; + continue; + case Noscrolling: + *scrollingp = 0; + continue; + case R: + r.min.x = riostrtol(s, &t); + if(t == s) + return -1; + s = t; + r.min.y = riostrtol(s, &t); + if(t == s) + return -1; + s = t; + r.max.x = riostrtol(s, &t); + if(t == s) + return -1; + s = t; + r.max.y = riostrtol(s, &t); + if(t == s) + return -1; + s = t; + continue; + } + while(isspace(*s)) + s++; + if(param == Cd){ + *cdp = s; + while(*s && !isspace(*s)) + s++; + if(*s != '\0') + *s++ = '\0'; + continue; + } + sign = 0; + if(*s == '-'){ + sign = -1; + s++; + }else if(*s == '+'){ + sign = +1; + s++; + } + if(!isdigit(*s)) + return -1; + xy = riostrtol(s, &s); + switch(param){ + case -1: + strcpy(err, "unrecognized wctl parameter"); + return -1; + case Minx: + r.min.x = set(sign, r.min.x-xy, xy, r.min.x+xy); + break; + case Miny: + r.min.y = set(sign, r.min.y-xy, xy, r.min.y+xy); + break; + case Maxx: + r.max.x = set(sign, r.max.x-xy, xy, r.max.x+xy); + break; + case Maxy: + r.max.y = set(sign, r.max.y-xy, xy, r.max.y+xy); + break; + case Deltax: + r.max.x = set(sign, r.max.x-xy, r.min.x+xy, r.max.x+xy); + break; + case Deltay: + r.max.y = set(sign, r.max.y-xy, r.min.y+xy, r.max.y+xy); + break; + case Id: + if(idp != nil) + *idp = xy; + break; + case PID: + if(pidp != nil) + *pidp = xy; + break; + } + } + + *rp = rectonscreen(rectaddpt(r, screen->r.min)); + + while(isspace(*s)) + s++; + if(cmd!=New && *s!='\0'){ + strcpy(err, "extraneous text in wctl message"); + return -1; + } + + if(argp) + *argp = s; + + return cmd; +} + +int +wctlnew(Rectangle rect, char *arg, int pid, int hideit, int scrollit, char *dir, char *err) +{ + char **argv; + Image *i; + + if(!goodrect(rect)){ + strcpy(err, Ebadwr); + return -1; + } + argv = emalloc(4*sizeof(char*)); + argv[0] = "rc"; + argv[1] = "-c"; + while(isspace(*arg)) + arg++; + if(*arg == '\0'){ + argv[1] = "-i"; + argv[2] = nil; + }else{ + argv[2] = arg; + argv[3] = nil; + } + if(hideit) + i = allocimage(display, rect, screen->chan, 0, DWhite); + else + i = allocwindow(wscreen, rect, Refbackup, DWhite); + if(i == nil){ + strcpy(err, Ewalloc); + return -1; + } + border(i, rect, Selborder, red, ZP); + + new(i, hideit, scrollit, pid, dir, "/bin/rc", argv); + + free(argv); /* when new() returns, argv and args have been copied */ + return 1; +} + +int +writewctl(Xfid *x, char *err) +{ + int cnt, cmd, j, id, hideit, scrollit, pid; + Image *i; + char *arg, *dir; + Rectangle rect; + Window *w; + + w = x->f->w; + cnt = x->count; + x->data[cnt] = '\0'; + id = 0; + + rect = rectsubpt(w->screenr, screen->r.min); + cmd = parsewctl(&arg, rect, &rect, &pid, &id, &hideit, &scrollit, &dir, x->data, err); + if(cmd < 0) + return -1; + + if(mouse->buttons!=0 && cmd>=Top){ + strcpy(err, "action disallowed when mouse active"); + return -1; + } + + if(id != 0){ + for(j=0; j<nwindow; j++) + if(window[j]->id == id) + break; + if(j == nwindow){ + strcpy(err, "no such window id"); + return -1; + } + w = window[j]; + if(w->deleted || w->i==nil){ + strcpy(err, "window deleted"); + return -1; + } + } + + switch(cmd){ + case New: + return wctlnew(rect, arg, pid, hideit, scrollit, dir, err); + case Set: + if(pid > 0) + wsetpid(w, pid, 0); + return 1; + case Move: + rect = Rect(rect.min.x, rect.min.y, rect.min.x+Dx(w->screenr), rect.min.y+Dy(w->screenr)); + rect = rectonscreen(rect); + /* fall through */ + case Resize: + if(!goodrect(rect)){ + strcpy(err, Ebadwr); + return -1; + } + if(eqrect(rect, w->screenr)) + return 1; + i = allocwindow(wscreen, rect, Refbackup, DWhite); + if(i == nil){ + strcpy(err, Ewalloc); + return -1; + } + border(i, rect, Selborder, red, ZP); + wsendctlmesg(w, Reshaped, i->r, i); + return 1; + case Scroll: + w->scrolling = 1; + wshow(w, w->nr); + wsendctlmesg(w, Wakeup, ZR, nil); + return 1; + case Noscroll: + w->scrolling = 0; + wsendctlmesg(w, Wakeup, ZR, nil); + return 1; + case Top: + wtopme(w); + return 1; + case Bottom: + wbottomme(w); + return 1; + case Current: + wcurrent(w); + return 1; + case Hide: + switch(whide(w)){ + case -1: + strcpy(err, "window already hidden"); + return -1; + case 0: + strcpy(err, "hide failed"); + return -1; + default: + break; + } + return 1; + case Unhide: + for(j=0; j<nhidden; j++) + if(hidden[j] == w) + break; + if(j == nhidden){ + strcpy(err, "window not hidden"); + return -1; + } + if(wunhide(j) == 0){ + strcpy(err, "hide failed"); + return -1; + } + return 1; + case Delete: + wsendctlmesg(w, Deleted, ZR, nil); + return 1; + } + strcpy(err, "invalid wctl message"); + return -1; +} + +void +wctlthread(void *v) +{ + char *buf, *arg, *dir; + int cmd, id, pid, hideit, scrollit; + Rectangle rect; + char err[ERRMAX]; + Channel *c; + + c = v; + + threadsetname("WCTLTHREAD"); + + for(;;){ + buf = recvp(c); + cmd = parsewctl(&arg, ZR, &rect, &pid, &id, &hideit, &scrollit, &dir, buf, err); + + switch(cmd){ + case New: + wctlnew(rect, arg, pid, hideit, scrollit, dir, err); + } + free(buf); + } +} + +void +wctlproc(void *v) +{ + char *buf; + int n, eofs; + Channel *c; + + threadsetname("WCTLPROC"); + c = v; + + eofs = 0; + for(;;){ + buf = emalloc(messagesize); + n = read(wctlfd, buf, messagesize-1); /* room for \0 */ + if(n < 0) + break; + if(n == 0){ + if(++eofs > 20) + break; + continue; + } + eofs = 0; + + buf[n] = '\0'; + sendp(c, buf); + } +} diff --git a/sys/src/cmd/rio/wind.c b/sys/src/cmd/rio/wind.c new file mode 100755 index 000000000..f3dc1a777 --- /dev/null +++ b/sys/src/cmd/rio/wind.c @@ -0,0 +1,1699 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include <plumb.h> +#include <complete.h> +#include "dat.h" +#include "fns.h" + +#define MOVEIT if(0) + +enum +{ + HiWater = 640000, /* max size of history */ + LoWater = 400000, /* min size of history after max'ed */ + MinWater = 20000, /* room to leave available when reallocating */ +}; + +static int topped; +static int id; + +static Image *cols[NCOL]; +static Image *grey; +static Image *darkgrey; +static Cursor *lastcursor; +static Image *titlecol; +static Image *lighttitlecol; +static Image *holdcol; +static Image *lightholdcol; +static Image *paleholdcol; + +Window* +wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling) +{ + Window *w; + Rectangle r; + + if(cols[0] == nil){ + /* greys are multiples of 0x11111100+0xFF, 14* being palest */ + grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF); + darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF); + cols[BACK] = display->white; + cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF); + cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF); + cols[TEXT] = display->black; + cols[HTEXT] = display->black; + titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen); + lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen); + holdcol = 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); + } + w = emalloc(sizeof(Window)); + w->screenr = i->r; + r = insetrect(i->r, Selborder+1); + w->i = i; + w->mc = *mc; + w->ck = ck; + w->cctl = cctl; + w->cursorp = nil; + w->conswrite = chancreate(sizeof(Conswritemesg), 0); + w->consread = chancreate(sizeof(Consreadmesg), 0); + w->mouseread = chancreate(sizeof(Mousereadmesg), 0); + w->wctlread = chancreate(sizeof(Consreadmesg), 0); + w->scrollr = r; + w->scrollr.max.x = r.min.x+Scrollwid; + w->lastsr = ZR; + r.min.x += Scrollwid+Scrollgap; + frinit(w, r, font, i, cols); + w->maxtab = maxtab*stringwidth(font, "0"); + w->topped = ++topped; + w->id = ++id; + w->notefd = -1; + w->scrolling = scrolling; + w->dir = estrdup(startdir); + w->label = estrdup("<unnamed>"); + r = insetrect(w->i->r, Selborder); + draw(w->i, r, cols[BACK], nil, w->entire.min); + wborder(w, Selborder); + wscrdraw(w); + incref(w); /* ref will be removed after mounting; avoids delete before ready to be deleted */ + return w; +} + +void +wsetname(Window *w) +{ + int i, n; + char err[ERRMAX]; + + n = sprint(w->name, "window.%d.%d", w->id, w->namecount++); + for(i='A'; i<='Z'; i++){ + if(nameimage(w->i, w->name, 1) > 0) + return; + errstr(err, sizeof err); + if(strcmp(err, "image name in use") != 0) + break; + w->name[n] = i; + w->name[n+1] = 0; + } + w->name[0] = 0; + fprint(2, "rio: setname failed: %s\n", err); +} + +void +wresize(Window *w, Image *i, int move) +{ + Rectangle r, or; + + or = w->i->r; + if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r))) + draw(i, i->r, w->i, nil, w->i->r.min); + freeimage(w->i); + w->i = i; + wsetname(w); + w->mc.image = i; + r = insetrect(i->r, Selborder+1); + w->scrollr = r; + w->scrollr.max.x = r.min.x+Scrollwid; + w->lastsr = ZR; + r.min.x += Scrollwid+Scrollgap; + if(move) + frsetrects(w, r, w->i); + else{ + frclear(w, FALSE); + frinit(w, r, w->font, w->i, cols); + wsetcols(w); + w->maxtab = maxtab*stringwidth(w->font, "0"); + r = insetrect(w->i->r, Selborder); + draw(w->i, r, cols[BACK], nil, w->entire.min); + wfill(w); + wsetselect(w, w->q0, w->q1); + wscrdraw(w); + } + wborder(w, Selborder); + w->topped = ++topped; + w->resized = TRUE; + w->mouse.counter++; +} + +void +wrefresh(Window *w, Rectangle) +{ + /* BUG: rectangle is ignored */ + if(w == input) + wborder(w, Selborder); + else + wborder(w, Unselborder); + if(w->mouseopen) + return; + draw(w->i, insetrect(w->i->r, Borderwidth), w->cols[BACK], nil, w->i->r.min); + w->ticked = 0; + if(w->p0 > 0) + frdrawsel(w, frptofchar(w, 0), 0, w->p0, 0); + if(w->p1 < w->nchars) + frdrawsel(w, frptofchar(w, w->p1), w->p1, w->nchars, 0); + frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 1); + w->lastsr = ZR; + wscrdraw(w); +} + +int +wclose(Window *w) +{ + int i; + + i = decref(w); + if(i > 0) + return 0; + if(i < 0) + error("negative ref count"); + if(!w->deleted) + wclosewin(w); + wsendctlmesg(w, Exited, ZR, nil); + return 1; +} + + +void +winctl(void *arg) +{ + Rune *rp, *bp, *tp, *up, *kbdr; + uint qh; + int nr, nb, c, wid, i, npart, initial, lastb; + char *s, *t, part[3]; + Window *w; + Mousestate *mp, m; + enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT }; + Alt alts[NWALT+1]; + Mousereadmesg mrm; + Conswritemesg cwm; + Consreadmesg crm; + Consreadmesg cwrm; + Stringpair pair; + Wctlmesg wcm; + char buf[4*12+1]; + + w = arg; + snprint(buf, sizeof buf, "winctl-id%d", w->id); + threadsetname(buf); + + mrm.cm = chancreate(sizeof(Mouse), 0); + cwm.cw = chancreate(sizeof(Stringpair), 0); + crm.c1 = chancreate(sizeof(Stringpair), 0); + crm.c2 = chancreate(sizeof(Stringpair), 0); + cwrm.c1 = chancreate(sizeof(Stringpair), 0); + cwrm.c2 = chancreate(sizeof(Stringpair), 0); + + + alts[WKey].c = w->ck; + alts[WKey].v = &kbdr; + alts[WKey].op = CHANRCV; + alts[WMouse].c = w->mc.c; + alts[WMouse].v = &w->mc.Mouse; + alts[WMouse].op = CHANRCV; + alts[WMouseread].c = w->mouseread; + alts[WMouseread].v = &mrm; + alts[WMouseread].op = CHANSND; + alts[WCtl].c = w->cctl; + alts[WCtl].v = &wcm; + alts[WCtl].op = CHANRCV; + alts[WCwrite].c = w->conswrite; + alts[WCwrite].v = &cwm; + alts[WCwrite].op = CHANSND; + alts[WCread].c = w->consread; + alts[WCread].v = &crm; + alts[WCread].op = CHANSND; + alts[WWread].c = w->wctlread; + alts[WWread].v = &cwrm; + alts[WWread].op = CHANSND; + alts[NWALT].op = CHANEND; + + npart = 0; + lastb = -1; + for(;;){ + if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter) + alts[WMouseread].op = CHANSND; + else + alts[WMouseread].op = CHANNOP; + if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->nchars) + alts[WCwrite].op = CHANNOP; + else + alts[WCwrite].op = CHANSND; + if(w->deleted || !w->wctlready) + alts[WWread].op = CHANNOP; + else + alts[WWread].op = CHANSND; + /* this code depends on NL and EOT fitting in a single byte */ + /* kind of expensive for each loop; worth precomputing? */ + if(w->holding) + alts[WCread].op = CHANNOP; + else if(npart || (w->rawing && w->nraw>0)) + alts[WCread].op = CHANSND; + else{ + alts[WCread].op = CHANNOP; + for(i=w->qh; i<w->nr; i++){ + c = w->r[i]; + if(c=='\n' || c=='\004'){ + alts[WCread].op = CHANSND; + break; + } + } + } + switch(alt(alts)){ + case WKey: + for(i=0; kbdr[i]!=L'\0'; i++) + wkeyctl(w, kbdr[i]); +// wkeyctl(w, r); +/// while(nbrecv(w->ck, &r)) +// wkeyctl(w, r); + break; + case WMouse: + if(w->mouseopen) { + w->mouse.counter++; + + /* queue click events */ + if(!w->mouse.qfull && lastb != w->mc.buttons) { /* add to ring */ + mp = &w->mouse.queue[w->mouse.wi]; + if(++w->mouse.wi == nelem(w->mouse.queue)) + w->mouse.wi = 0; + if(w->mouse.wi == w->mouse.ri) + w->mouse.qfull = TRUE; + mp->Mouse = w->mc; + mp->counter = w->mouse.counter; + lastb = w->mc.buttons; + } + } else + wmousectl(w); + break; + case WMouseread: + /* send a queued event or, if the queue is empty, the current state */ + /* if the queue has filled, we discard all the events it contained. */ + /* the intent is to discard frantic clicking by the user during long latencies. */ + w->mouse.qfull = FALSE; + if(w->mouse.wi != w->mouse.ri) { + m = w->mouse.queue[w->mouse.ri]; + if(++w->mouse.ri == nelem(w->mouse.queue)) + w->mouse.ri = 0; + } else + m = (Mousestate){w->mc.Mouse, w->mouse.counter}; + + w->mouse.lastcounter = m.counter; + send(mrm.cm, &m.Mouse); + continue; + case WCtl: + if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){ + chanfree(crm.c1); + chanfree(crm.c2); + chanfree(mrm.cm); + chanfree(cwm.cw); + chanfree(cwrm.c1); + chanfree(cwrm.c2); + threadexits(nil); + } + continue; + case WCwrite: + recv(cwm.cw, &pair); + rp = pair.s; + nr = pair.ns; + bp = rp; + for(i=0; i<nr; i++) + if(*bp++ == '\b'){ + --bp; + initial = 0; + tp = runemalloc(nr); + runemove(tp, rp, i); + up = tp+i; + for(; i<nr; i++){ + *up = *bp++; + if(*up == '\b') + if(up == tp) + initial++; + else + --up; + else + up++; + } + if(initial){ + if(initial > w->qh) + initial = w->qh; + qh = w->qh-initial; + wdelete(w, qh, qh+initial); + w->qh = qh; + } + free(rp); + rp = tp; + nr = up-tp; + rp[nr] = 0; + break; + } + w->qh = winsert(w, rp, nr, w->qh)+nr; + if(w->scrolling || w->mouseopen) + wshow(w, w->qh); + wsetselect(w, w->q0, w->q1); + wscrdraw(w); + free(rp); + break; + case WCread: + recv(crm.c1, &pair); + t = pair.s; + nb = pair.ns; + i = npart; + npart = 0; + if(i) + memmove(t, part, i); + while(i<nb && (w->qh<w->nr || w->nraw>0)){ + if(w->qh == w->nr){ + wid = runetochar(t+i, &w->raw[0]); + w->nraw--; + runemove(w->raw, w->raw+1, w->nraw); + }else + wid = runetochar(t+i, &w->r[w->qh++]); + c = t[i]; /* knows break characters fit in a byte */ + i += wid; + if(!w->rawing && (c == '\n' || c=='\004')){ + if(c == '\004') + i--; + break; + } + } + if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004') + w->qh++; + if(i > nb){ + npart = i-nb; + memmove(part, t+nb, npart); + i = nb; + } + pair.s = t; + pair.ns = i; + send(crm.c2, &pair); + continue; + case WWread: + w->wctlready = 0; + recv(cwrm.c1, &pair); + if(w->deleted || w->i==nil) + pair.ns = sprint(pair.s, ""); + else{ + s = "visible"; + for(i=0; i<nhidden; i++) + if(hidden[i] == w){ + s = "hidden"; + break; + } + t = "notcurrent"; + if(w == input) + t = "current"; + pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ", + w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s); + } + send(cwrm.c2, &pair); + continue; + } + if(!w->deleted) + flushimage(display, 1); + } +} + +void +waddraw(Window *w, Rune *r, int nr) +{ + w->raw = runerealloc(w->raw, w->nraw+nr); + runemove(w->raw+w->nraw, r, nr); + w->nraw += nr; +} + +/* + * Need to do this in a separate proc because if process we're interrupting + * is dying and trying to print tombstone, kernel is blocked holding p->debug lock. + */ +void +interruptproc(void *v) +{ + int *notefd; + + notefd = v; + write(*notefd, "interrupt", 9); + free(notefd); +} + +int +windfilewidth(Window *w, uint q0, int oneelement) +{ + uint q; + Rune r; + + q = q0; + while(q > 0){ + r = w->r[q-1]; + if(r<=' ') + break; + if(oneelement && r=='/') + break; + --q; + } + return q0-q; +} + +void +showcandidates(Window *w, Completion *c) +{ + int i; + Fmt f; + Rune *rp; + uint nr, qline, q0; + char *s; + + runefmtstrinit(&f); + if (c->nmatch == 0) + s = "[no matches in "; + else + s = "["; + if(c->nfile > 32) + fmtprint(&f, "%s%d files]\n", s, c->nfile); + else{ + fmtprint(&f, "%s", s); + for(i=0; i<c->nfile; i++){ + if(i > 0) + fmtprint(&f, " "); + fmtprint(&f, "%s", c->filename[i]); + } + fmtprint(&f, "]\n"); + } + /* place text at beginning of line before host point */ + qline = w->qh; + while(qline>0 && w->r[qline-1] != '\n') + qline--; + + rp = runefmtstrflush(&f); + nr = runestrlen(rp); + + q0 = w->q0; + q0 += winsert(w, rp, runestrlen(rp), qline) - qline; + free(rp); + wsetselect(w, q0+nr, q0+nr); +} + +Rune* +namecomplete(Window *w) +{ + int nstr, npath; + Rune *rp, *path, *str; + Completion *c; + char *s, *dir, *root; + + /* control-f: filename completion; works back to white space or / */ + if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */ + return nil; + nstr = windfilewidth(w, w->q0, TRUE); + str = runemalloc(nstr); + runemove(str, w->r+(w->q0-nstr), nstr); + npath = windfilewidth(w, w->q0-nstr, FALSE); + path = runemalloc(npath); + runemove(path, w->r+(w->q0-nstr-npath), npath); + rp = nil; + + /* is path rooted? if not, we need to make it relative to window path */ + if(npath>0 && path[0]=='/'){ + dir = malloc(UTFmax*npath+1); + sprint(dir, "%.*S", npath, path); + }else{ + if(strcmp(w->dir, "") == 0) + root = "."; + else + root = w->dir; + dir = malloc(strlen(root)+1+UTFmax*npath+1); + sprint(dir, "%s/%.*S", root, npath, path); + } + dir = cleanname(dir); + + s = smprint("%.*S", nstr, str); + c = complete(dir, s); + free(s); + if(c == nil) + goto Return; + + if(!c->advance) + showcandidates(w, c); + + if(c->advance) + rp = runesmprint("%s", c->string); + + Return: + freecompletion(c); + free(dir); + free(path); + free(str); + return rp; +} + +void +wkeyctl(Window *w, Rune r) +{ + uint q0 ,q1; + int n, nb, nr; + Rune *rp; + int *notefd; + + if(r == 0) + return; + if(w->deleted) + return; + /* navigation keys work only when mouse is not open */ + if(!w->mouseopen) + switch(r){ + case Kdown: + n = w->maxlines/3; + goto case_Down; + case Kscrollonedown: + n = mousescrollsize(w->maxlines); + if(n <= 0) + n = 1; + goto case_Down; + case Kpgdown: + n = 2*w->maxlines/3; + case_Down: + q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+n*w->font->height)); + wsetorigin(w, q0, TRUE); + return; + case Kup: + n = w->maxlines/3; + goto case_Up; + case Kscrolloneup: + n = mousescrollsize(w->maxlines); + if(n <= 0) + n = 1; + goto case_Up; + case Kpgup: + n = 2*w->maxlines/3; + case_Up: + q0 = wbacknl(w, w->org, n); + wsetorigin(w, q0, TRUE); + return; + case Kleft: + if(w->q0 > 0){ + q0 = w->q0-1; + wsetselect(w, q0, q0); + wshow(w, q0); + } + return; + case Kright: + if(w->q1 < w->nr){ + q1 = w->q1+1; + wsetselect(w, q1, q1); + wshow(w, q1); + } + return; + case Khome: + wshow(w, 0); + return; + case Kend: + wshow(w, w->nr); + return; + case 0x01: /* ^A: beginning of line */ + if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n') + return; + nb = wbswidth(w, 0x15 /* ^U */); + wsetselect(w, w->q0-nb, w->q0-nb); + wshow(w, w->q0); + return; + case 0x05: /* ^E: end of line */ + q0 = w->q0; + while(q0 < w->nr && w->r[q0]!='\n') + q0++; + wsetselect(w, q0, q0); + wshow(w, w->q0); + return; + } + if(w->rawing && (w->q0==w->nr || w->mouseopen)){ + waddraw(w, &r, 1); + return; + } + if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */ + if(w->holding) + --w->holding; + else + w->holding++; + wrepaint(w); + if(r == 0x1B) + return; + } + if(r != 0x7F){ + wsnarf(w); + wcut(w); + } + switch(r){ + case 0x7F: /* send interrupt */ + w->qh = w->nr; + wshow(w, w->qh); + notefd = emalloc(sizeof(int)); + *notefd = w->notefd; + proccreate(interruptproc, notefd, 4096); + return; + case 0x06: /* ^F: file name completion */ + case Kins: /* Insert: file name completion */ + rp = namecomplete(w); + if(rp == nil) + return; + nr = runestrlen(rp); + q0 = w->q0; + q0 = winsert(w, rp, nr, q0); + wshow(w, q0+nr); + free(rp); + return; + case 0x08: /* ^H: erase character */ + case 0x15: /* ^U: erase line */ + case 0x17: /* ^W: erase word */ + if(w->q0==0 || w->q0==w->qh) + return; + nb = wbswidth(w, r); + q1 = w->q0; + q0 = q1-nb; + if(q0 < w->org){ + q0 = w->org; + nb = q1-q0; + } + if(nb > 0){ + wdelete(w, q0, q0+nb); + wsetselect(w, q0, q0); + } + return; + } + /* otherwise ordinary character; just insert */ + q0 = w->q0; + q0 = winsert(w, &r, 1, q0); + wshow(w, q0+1); +} + +void +wsetcols(Window *w) +{ + if(w->holding) + if(w == input) + w->cols[TEXT] = w->cols[HTEXT] = holdcol; + else + w->cols[TEXT] = w->cols[HTEXT] = lightholdcol; + else + if(w == input) + w->cols[TEXT] = w->cols[HTEXT] = display->black; + else + w->cols[TEXT] = w->cols[HTEXT] = darkgrey; +} + +void +wrepaint(Window *w) +{ + wsetcols(w); + if(!w->mouseopen) + frredraw(w); + if(w == input){ + wborder(w, Selborder); + wsetcursor(w, 0); + }else + wborder(w, Unselborder); +} + +int +wbswidth(Window *w, Rune c) +{ + uint q, eq, stop; + Rune r; + int skipping; + + /* there is known to be at least one character to erase */ + if(c == 0x08) /* ^H: erase character */ + return 1; + q = w->q0; + stop = 0; + if(q > w->qh) + stop = w->qh; + skipping = TRUE; + while(q > stop){ + r = w->r[q-1]; + if(r == '\n'){ /* eat at most one more character */ + if(q == w->q0) /* eat the newline */ + --q; + break; + } + if(c == 0x17){ + eq = isalnum(r); + if(eq && skipping) /* found one; stop skipping */ + skipping = FALSE; + else if(!eq && !skipping) + break; + } + --q; + } + return w->q0-q; +} + +void +wsnarf(Window *w) +{ + if(w->q1 == w->q0) + return; + nsnarf = w->q1-w->q0; + snarf = runerealloc(snarf, nsnarf); + snarfversion++; /* maybe modified by parent */ + runemove(snarf, w->r+w->q0, nsnarf); + putsnarf(); +} + +void +wcut(Window *w) +{ + if(w->q1 == w->q0) + return; + wdelete(w, w->q0, w->q1); + wsetselect(w, w->q0, w->q0); +} + +void +wpaste(Window *w) +{ + uint q0; + + if(nsnarf == 0) + return; + wcut(w); + q0 = w->q0; + if(w->rawing && q0==w->nr){ + waddraw(w, snarf, nsnarf); + wsetselect(w, q0, q0); + }else{ + q0 = winsert(w, snarf, nsnarf, w->q0); + wsetselect(w, q0, q0+nsnarf); + } +} + +void +wplumb(Window *w) +{ + Plumbmsg *m; + static int fd = -2; + char buf[32]; + uint p0, p1; + Cursor *c; + + if(fd == -2) + fd = plumbopen("send", OWRITE|OCEXEC); + if(fd < 0) + return; + m = emalloc(sizeof(Plumbmsg)); + m->src = estrdup("rio"); + m->dst = nil; + m->wdir = estrdup(w->dir); + m->type = estrdup("text"); + p0 = w->q0; + p1 = w->q1; + if(w->q1 > w->q0) + m->attr = nil; + else{ + while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n') + p0--; + while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n') + p1++; + sprint(buf, "click=%d", w->q0-p0); + m->attr = plumbunpackattr(buf); + } + if(p1-p0 > messagesize-1024){ + plumbfree(m); + return; /* too large for 9P */ + } + m->data = runetobyte(w->r+p0, p1-p0, &m->ndata); + if(plumbsend(fd, m) < 0){ + c = lastcursor; + riosetcursor(&query, 1); + sleep(300); + riosetcursor(c, 1); + } + plumbfree(m); +} + +int +winborder(Window *w, Point xy) +{ + return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder)); +} + +void +wmousectl(Window *w) +{ + int but; + + if(w->mc.buttons == 1) + but = 1; + else if(w->mc.buttons == 2) + but = 2; + else if(w->mc.buttons == 4) + but = 3; + else{ + if(w->mc.buttons == 8) + wkeyctl(w, Kscrolloneup); + if(w->mc.buttons == 16) + wkeyctl(w, Kscrollonedown); + return; + } + + incref(w); /* hold up window while we track */ + if(w->deleted) + goto Return; + if(ptinrect(w->mc.xy, w->scrollr)){ + if(but) + wscroll(w, but); + goto Return; + } + if(but == 1) + wselect(w); + /* else all is handled by main process */ + Return: + wclose(w); +} + +void +wdelete(Window *w, uint q0, uint q1) +{ + uint n, p0, p1; + + n = q1-q0; + if(n == 0) + return; + runemove(w->r+q0, w->r+q1, w->nr-q1); + w->nr -= n; + if(q0 < w->q0) + w->q0 -= min(n, w->q0-q0); + if(q0 < w->q1) + w->q1 -= min(n, w->q1-q0); + if(q1 < w->qh) + w->qh -= n; + else if(q0 < w->qh) + w->qh = q0; + if(q1 <= w->org) + w->org -= n; + else if(q0 < w->org+w->nchars){ + p1 = q1 - w->org; + if(p1 > w->nchars) + p1 = w->nchars; + if(q0 < w->org){ + w->org = q0; + p0 = 0; + }else + p0 = q0 - w->org; + frdelete(w, p0, p1); + wfill(w); + } +} + + +static Window *clickwin; +static uint clickmsec; +static Window *selectwin; +static uint selectq; + +/* + * called from frame library + */ +void +framescroll(Frame *f, int dl) +{ + if(f != &selectwin->Frame) + error("frameselect not right frame"); + wframescroll(selectwin, dl); +} + +void +wframescroll(Window *w, int dl) +{ + uint q0; + + if(dl == 0){ + wscrsleep(w, 100); + return; + } + if(dl < 0){ + q0 = wbacknl(w, w->org, -dl); + if(selectq > w->org+w->p0) + wsetselect(w, w->org+w->p0, selectq); + else + wsetselect(w, selectq, w->org+w->p0); + }else{ + if(w->org+w->nchars == w->nr) + return; + q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+dl*w->font->height)); + if(selectq >= w->org+w->p1) + wsetselect(w, w->org+w->p1, selectq); + else + wsetselect(w, selectq, w->org+w->p1); + } + wsetorigin(w, q0, TRUE); +} + +void +wselect(Window *w) +{ + uint q0, q1; + int b, x, y, first; + + first = 1; + selectwin = w; + /* + * Double-click immediately if it might make sense. + */ + b = w->mc.buttons; + q0 = w->q0; + q1 = w->q1; + selectq = w->org+frcharofpt(w, w->mc.xy); + if(clickwin==w && w->mc.msec-clickmsec<500) + if(q0==q1 && selectq==w->q0){ + wdoubleclick(w, &q0, &q1); + wsetselect(w, q0, q1); + flushimage(display, 1); + x = w->mc.xy.x; + y = w->mc.xy.y; + /* stay here until something interesting happens */ + do + readmouse(&w->mc); + while(w->mc.buttons==b && abs(w->mc.xy.x-x)<3 && abs(w->mc.xy.y-y)<3); + w->mc.xy.x = x; /* in case we're calling frselect */ + w->mc.xy.y = y; + q0 = w->q0; /* may have changed */ + q1 = w->q1; + selectq = q0; + } + if(w->mc.buttons == b){ + w->scroll = framescroll; + frselect(w, &w->mc); + /* horrible botch: while asleep, may have lost selection altogether */ + if(selectq > w->nr) + selectq = w->org + w->p0; + w->Frame.scroll = nil; + if(selectq < w->org) + q0 = selectq; + else + q0 = w->org + w->p0; + if(selectq > w->org+w->nchars) + q1 = selectq; + else + q1 = w->org+w->p1; + } + if(q0 == q1){ + if(q0==w->q0 && clickwin==w && w->mc.msec-clickmsec<500){ + wdoubleclick(w, &q0, &q1); + clickwin = nil; + }else{ + clickwin = w; + clickmsec = w->mc.msec; + } + }else + clickwin = nil; + wsetselect(w, q0, q1); + flushimage(display, 1); + while(w->mc.buttons){ + w->mc.msec = 0; + b = w->mc.buttons; + if(b & 6){ + if(b & 2){ + wsnarf(w); + wcut(w); + }else{ + if(first){ + first = 0; + getsnarf(); + } + wpaste(w); + } + } + wscrdraw(w); + flushimage(display, 1); + while(w->mc.buttons == b) + readmouse(&w->mc); + clickwin = nil; + } +} + +void +wsendctlmesg(Window *w, int type, Rectangle r, Image *image) +{ + Wctlmesg wcm; + + wcm.type = type; + wcm.r = r; + wcm.image = image; + send(w->cctl, &wcm); +} + +int +wctlmesg(Window *w, int m, Rectangle r, Image *i) +{ + char buf[64]; + + switch(m){ + default: + error("unknown control message"); + break; + case Wakeup: + break; + case Moved: + case Reshaped: + if(w->deleted){ + freeimage(i); + break; + } + w->screenr = r; + strcpy(buf, w->name); + wresize(w, i, m==Moved); + w->wctlready = 1; + proccreate(deletetimeoutproc, estrdup(buf), 4096); + if(Dx(r) > 0){ + if(w != input) + wcurrent(w); + }else if(w == input) + wcurrent(nil); + flushimage(display, 1); + break; + case Refresh: + if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r)) + break; + if(!w->mouseopen) + wrefresh(w, r); + flushimage(display, 1); + break; + case Movemouse: + if(sweeping || !ptinrect(r.min, w->i->r)) + break; + wmovemouse(w, r.min); + case Rawon: + break; + case Rawoff: + if(w->deleted) + break; + while(w->nraw > 0){ + wkeyctl(w, w->raw[0]); + --w->nraw; + runemove(w->raw, w->raw+1, w->nraw); + } + break; + case Holdon: + case Holdoff: + if(w->deleted) + break; + wrepaint(w); + flushimage(display, 1); + break; + case Deleted: + if(w->deleted) + break; + write(w->notefd, "hangup", 6); + proccreate(deletetimeoutproc, estrdup(w->name), 4096); + wclosewin(w); + break; + case Exited: + frclear(w, TRUE); + close(w->notefd); + chanfree(w->mc.c); + chanfree(w->ck); + chanfree(w->cctl); + chanfree(w->conswrite); + chanfree(w->consread); + chanfree(w->mouseread); + chanfree(w->wctlread); + free(w->raw); + free(w->r); + free(w->dir); + free(w->label); + free(w); + break; + } + return m; +} + +/* + * Convert back to physical coordinates + */ +void +wmovemouse(Window *w, Point p) +{ + p.x += w->screenr.min.x-w->i->r.min.x; + p.y += w->screenr.min.y-w->i->r.min.y; + moveto(mousectl, p); +} + +void +wborder(Window *w, int type) +{ + Image *col; + + if(w->i == nil) + return; + if(w->holding){ + if(type == Selborder) + col = holdcol; + else + col = paleholdcol; + }else{ + if(type == Selborder) + col = titlecol; + else + col = lighttitlecol; + } + + border(w->i, w->i->r, Selborder, col, ZP); +} + +Window* +wpointto(Point pt) +{ + int i; + Window *v, *w; + + w = nil; + for(i=0; i<nwindow; i++){ + v = window[i]; + if(ptinrect(pt, v->screenr)) + if(!v->deleted) + if(w==nil || v->topped>w->topped) + w = v; + } + return w; +} + +void +wcurrent(Window *w) +{ + Window *oi; + + if(wkeyboard!=nil && w==wkeyboard) + return; + oi = input; + input = w; + if(oi!=w && oi!=nil) + wrepaint(oi); + if(w !=nil){ + wrepaint(w); + wsetcursor(w, 0); + } + if(w != oi){ + if(oi){ + oi->wctlready = 1; + wsendctlmesg(oi, Wakeup, ZR, nil); + } + if(w){ + w->wctlready = 1; + wsendctlmesg(w, Wakeup, ZR, nil); + } + } +} + +void +wsetcursor(Window *w, int force) +{ + Cursor *p; + + if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0) + p = nil; + else if(wpointto(mouse->xy) == w){ + p = w->cursorp; + if(p==nil && w->holding) + p = &whitearrow; + }else + p = nil; + if(!menuing) + riosetcursor(p, force && !menuing); +} + +void +riosetcursor(Cursor *p, int force) +{ + if(!force && p==lastcursor) + return; + setcursor(mousectl, p); + lastcursor = p; +} + +Window* +wtop(Point pt) +{ + Window *w; + + w = wpointto(pt); + if(w){ + if(w->topped == topped) + return nil; + topwindow(w->i); + wcurrent(w); + flushimage(display, 1); + w->topped = ++topped; + } + return w; +} + +void +wtopme(Window *w) +{ + if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){ + topwindow(w->i); + flushimage(display, 1); + w->topped = ++ topped; + } +} + +void +wbottomme(Window *w) +{ + if(w!=nil && w->i!=nil && !w->deleted){ + bottomwindow(w->i); + flushimage(display, 1); + w->topped = - ++topped; + } +} + +Window* +wlookid(int id) +{ + int i; + + for(i=0; i<nwindow; i++) + if(window[i]->id == id) + return window[i]; + return nil; +} + +void +wclosewin(Window *w) +{ + Rectangle r; + int i; + + w->deleted = TRUE; + if(w == input){ + input = nil; + wsetcursor(w, 0); + } + if(w == wkeyboard) + wkeyboard = nil; + for(i=0; i<nhidden; i++) + if(hidden[i] == w){ + --nhidden; + memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0])); + hidden[nhidden] = nil; + break; + } + for(i=0; i<nwindow; i++) + if(window[i] == w){ + --nwindow; + memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*)); + w->deleted = TRUE; + r = w->i->r; + /* move it off-screen to hide it, in case client is slow in letting it go */ + MOVEIT originwindow(w->i, r.min, view->r.max); + freeimage(w->i); + w->i = nil; + return; + } + error("unknown window in closewin"); +} + +void +wsetpid(Window *w, int pid, int dolabel) +{ + char buf[128]; + int fd; + + w->pid = pid; + if(dolabel){ + sprint(buf, "rc %d", pid); + free(w->label); + w->label = estrdup(buf); + } + sprint(buf, "/proc/%d/notepg", pid); + fd = open(buf, OWRITE|OCEXEC); + if(w->notefd > 0) + close(w->notefd); + w->notefd = fd; +} + +void +winshell(void *args) +{ + Window *w; + Channel *pidc; + void **arg; + char *cmd, *dir; + char **argv; + + arg = args; + w = arg[0]; + pidc = arg[1]; + cmd = arg[2]; + argv = arg[3]; + dir = arg[4]; + rfork(RFNAMEG|RFFDG|RFENVG); + if(filsysmount(filsys, w->id) < 0){ + fprint(2, "mount failed: %r\n"); + sendul(pidc, 0); + threadexits("mount failed"); + } + close(0); + if(open("/dev/cons", OREAD) < 0){ + fprint(2, "can't open /dev/cons: %r\n"); + sendul(pidc, 0); + threadexits("/dev/cons"); + } + close(1); + if(open("/dev/cons", OWRITE) < 0){ + fprint(2, "can't open /dev/cons: %r\n"); + sendul(pidc, 0); + threadexits("open"); /* BUG? was terminate() */ + } + if(wclose(w) == 0){ /* remove extra ref hanging from creation */ + notify(nil); + dup(1, 2); + if(dir) + chdir(dir); + procexec(pidc, cmd, argv); + _exits("exec failed"); + } +} + +static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 }; +static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 }; +static Rune left2[] = { L'\n', 0 }; +static Rune left3[] = { L'\'', L'"', L'`', 0 }; + +Rune *left[] = { + left1, + left2, + left3, + nil +}; +Rune *right[] = { + right1, + left2, + left3, + nil +}; + +void +wdoubleclick(Window *w, uint *q0, uint *q1) +{ + int c, i; + Rune *r, *l, *p; + uint q; + + for(i=0; left[i]!=nil; i++){ + q = *q0; + l = left[i]; + r = right[i]; + /* try matching character to left, looking right */ + if(q == 0) + c = '\n'; + else + c = w->r[q-1]; + p = strrune(l, c); + if(p != nil){ + if(wclickmatch(w, c, r[p-l], 1, &q)) + *q1 = q-(c!='\n'); + return; + } + /* try matching character to right, looking left */ + if(q == w->nr) + c = '\n'; + else + c = w->r[q]; + p = strrune(r, c); + if(p != nil){ + if(wclickmatch(w, c, l[p-r], -1, &q)){ + *q1 = *q0+(*q0<w->nr && c=='\n'); + *q0 = q; + if(c!='\n' || q!=0 || w->r[0]=='\n') + (*q0)++; + } + return; + } + } + /* try filling out word to right */ + while(*q1<w->nr && isalnum(w->r[*q1])) + (*q1)++; + /* try filling out word to left */ + while(*q0>0 && isalnum(w->r[*q0-1])) + (*q0)--; +} + +int +wclickmatch(Window *w, int cl, int cr, int dir, uint *q) +{ + Rune c; + int nest; + + nest = 1; + for(;;){ + if(dir > 0){ + if(*q == w->nr) + break; + c = w->r[*q]; + (*q)++; + }else{ + if(*q == 0) + break; + (*q)--; + c = w->r[*q]; + } + if(c == cr){ + if(--nest==0) + return 1; + }else if(c == cl) + nest++; + } + return cl=='\n' && nest==1; +} + + +uint +wbacknl(Window *w, uint p, uint n) +{ + int i, j; + + /* look for start of this line if n==0 */ + if(n==0 && p>0 && w->r[p-1]!='\n') + n = 1; + i = n; + while(i-->0 && p>0){ + --p; /* it's at a newline now; back over it */ + if(p == 0) + break; + /* at 128 chars, call it a line anyway */ + for(j=128; --j>0 && p>0; p--) + if(w->r[p-1]=='\n') + break; + } + return p; +} + +void +wshow(Window *w, uint q0) +{ + int qe; + int nl; + uint q; + + qe = w->org+w->nchars; + if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr))) + wscrdraw(w); + else{ + nl = 4*w->maxlines/5; + q = wbacknl(w, q0, nl); + /* avoid going backwards if trying to go forwards - long lines! */ + if(!(q0>w->org && q<w->org)) + wsetorigin(w, q, TRUE); + while(q0 > w->org+w->nchars) + wsetorigin(w, w->org+1, FALSE); + } +} + +void +wsetorigin(Window *w, uint org, int exact) +{ + int i, a, fixup; + Rune *r; + uint n; + + if(org>0 && !exact){ + /* org is an estimate of the char posn; find a newline */ + /* don't try harder than 256 chars */ + for(i=0; i<256 && org<w->nr; i++){ + if(w->r[org] == '\n'){ + org++; + break; + } + org++; + } + } + a = org-w->org; + fixup = 0; + if(a>=0 && a<w->nchars){ + frdelete(w, 0, a); + fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */ + }else if(a<0 && -a<w->nchars){ + n = w->org - org; + r = runemalloc(n); + runemove(r, w->r+org, n); + frinsert(w, r, r+n, 0); + free(r); + }else + frdelete(w, 0, w->nchars); + w->org = org; + wfill(w); + wscrdraw(w); + wsetselect(w, w->q0, w->q1); + if(fixup && w->p1 > w->p0) + frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1); +} + +void +wsetselect(Window *w, uint q0, uint q1) +{ + int p0, p1; + + /* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */ + w->q0 = q0; + w->q1 = q1; + /* compute desired p0,p1 from q0,q1 */ + p0 = q0-w->org; + p1 = q1-w->org; + if(p0 < 0) + p0 = 0; + if(p1 < 0) + p1 = 0; + if(p0 > w->nchars) + p0 = w->nchars; + if(p1 > w->nchars) + p1 = w->nchars; + if(p0==w->p0 && p1==w->p1) + return; + /* screen disagrees with desired selection */ + if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){ + /* no overlap or too easy to bother trying */ + frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 0); + frdrawsel(w, frptofchar(w, p0), p0, p1, 1); + goto Return; + } + /* overlap; avoid unnecessary painting */ + if(p0 < w->p0){ + /* extend selection backwards */ + frdrawsel(w, frptofchar(w, p0), p0, w->p0, 1); + }else if(p0 > w->p0){ + /* trim first part of selection */ + frdrawsel(w, frptofchar(w, w->p0), w->p0, p0, 0); + } + if(p1 > w->p1){ + /* extend selection forwards */ + frdrawsel(w, frptofchar(w, w->p1), w->p1, p1, 1); + }else if(p1 < w->p1){ + /* trim last part of selection */ + frdrawsel(w, frptofchar(w, p1), p1, w->p1, 0); + } + + Return: + w->p0 = p0; + w->p1 = p1; +} + +uint +winsert(Window *w, Rune *r, int n, uint q0) +{ + uint m; + + if(n == 0) + return q0; + if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){ + m = min(HiWater-LoWater, min(w->org, w->qh)); + w->org -= m; + w->qh -= m; + if(w->q0 > m) + w->q0 -= m; + else + w->q0 = 0; + if(w->q1 > m) + w->q1 -= m; + else + w->q1 = 0; + w->nr -= m; + runemove(w->r, w->r+m, w->nr); + q0 -= m; + } + if(w->nr+n > w->maxr){ + /* + * Minimize realloc breakage: + * Allocate at least MinWater + * Double allocation size each time + * But don't go much above HiWater + */ + m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater; + if(m > HiWater) + m = max(HiWater+MinWater, w->nr+n); + if(m > w->maxr){ + w->r = runerealloc(w->r, m); + w->maxr = m; + } + } + runemove(w->r+q0+n, w->r+q0, w->nr-q0); + runemove(w->r+q0, r, n); + w->nr += n; + /* if output touches, advance selection, not qh; works best for keyboard and output */ + if(q0 <= w->q1) + w->q1 += n; + if(q0 <= w->q0) + w->q0 += n; + if(q0 < w->qh) + w->qh += n; + if(q0 < w->org) + w->org += n; + else if(q0 <= w->org+w->nchars) + frinsert(w, r, r+n, q0-w->org); + return q0; +} + +void +wfill(Window *w) +{ + Rune *rp; + int i, n, m, nl; + + if(w->lastlinefull) + return; + rp = malloc(messagesize); + do{ + n = w->nr-(w->org+w->nchars); + if(n == 0) + break; + if(n > 2000) /* educated guess at reasonable amount */ + n = 2000; + runemove(rp, w->r+(w->org+w->nchars), n); + /* + * it's expensive to frinsert more than we need, so + * count newlines. + */ + nl = w->maxlines-w->nlines; + m = 0; + for(i=0; i<n; ){ + if(rp[i++] == '\n'){ + m++; + if(m >= nl) + break; + } + } + frinsert(w, rp, rp+i, w->nchars); + }while(w->lastlinefull == FALSE); + free(rp); +} + +char* +wcontents(Window *w, int *ip) +{ + return runetobyte(w->r, w->nr, ip); +} diff --git a/sys/src/cmd/rio/xfid.c b/sys/src/cmd/rio/xfid.c new file mode 100755 index 000000000..c67f783fb --- /dev/null +++ b/sys/src/cmd/rio/xfid.c @@ -0,0 +1,846 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include <plumb.h> +#include "dat.h" +#include "fns.h" + +#define MAXSNARF 100*1024 + +char Einuse[] = "file in use"; +char Edeleted[] = "window deleted"; +char Ebadreq[] = "bad graphics request"; +char Etooshort[] = "buffer too small"; +char Ebadtile[] = "unknown tile"; +char Eshort[] = "short i/o request"; +char Elong[] = "snarf buffer too long"; +char Eunkid[] = "unknown id in attach"; +char Ebadrect[] = "bad rectangle in attach"; +char Ewindow[] = "cannot make window"; +char Enowindow[] = "window has no image"; +char Ebadmouse[] = "bad format on /dev/mouse"; +char Ebadwrect[] = "rectangle outside screen"; +char Ebadoffset[] = "window read not on scan line boundary"; +extern char Eperm[]; + +static Xfid *xfidfree; +static Xfid *xfid; +static Channel *cxfidalloc; /* chan(Xfid*) */ +static Channel *cxfidfree; /* chan(Xfid*) */ + +static char *tsnarf; +static int ntsnarf; + +void +xfidallocthread(void*) +{ + Xfid *x; + enum { Alloc, Free, N }; + static Alt alts[N+1]; + + alts[Alloc].c = cxfidalloc; + alts[Alloc].v = nil; + alts[Alloc].op = CHANRCV; + alts[Free].c = cxfidfree; + alts[Free].v = &x; + alts[Free].op = CHANRCV; + alts[N].op = CHANEND; + for(;;){ + switch(alt(alts)){ + case Alloc: + x = xfidfree; + if(x) + xfidfree = x->free; + else{ + x = emalloc(sizeof(Xfid)); + x->c = chancreate(sizeof(void(*)(Xfid*)), 0); + x->flushc = chancreate(sizeof(int), 0); /* notification only; no data */ + x->flushtag = -1; + x->next = xfid; + xfid = x; + threadcreate(xfidctl, x, 16384); + } + if(x->ref != 0){ + fprint(2, "%p incref %ld\n", x, x->ref); + error("incref"); + } + if(x->flushtag != -1) + error("flushtag in allocate"); + incref(x); + sendp(cxfidalloc, x); + break; + case Free: + if(x->ref != 0){ + fprint(2, "%p decref %ld\n", x, x->ref); + error("decref"); + } + if(x->flushtag != -1) + error("flushtag in free"); + x->free = xfidfree; + xfidfree = x; + break; + } + } +} + +Channel* +xfidinit(void) +{ + cxfidalloc = chancreate(sizeof(Xfid*), 0); + cxfidfree = chancreate(sizeof(Xfid*), 0); + threadcreate(xfidallocthread, nil, STACK); + return cxfidalloc; +} + +void +xfidctl(void *arg) +{ + Xfid *x; + void (*f)(Xfid*); + char buf[64]; + + x = arg; + snprint(buf, sizeof buf, "xfid.%p", x); + threadsetname(buf); + for(;;){ + f = recvp(x->c); + (*f)(x); + if(decref(x) == 0) + sendp(cxfidfree, x); + } +} + +void +xfidflush(Xfid *x) +{ + Fcall t; + Xfid *xf; + + for(xf=xfid; xf; xf=xf->next) + if(xf->flushtag == x->oldtag){ + xf->flushtag = -1; + xf->flushing = TRUE; + incref(xf); /* to hold data structures up at tail of synchronization */ + if(xf->ref == 1) + error("ref 1 in flush"); + if(canqlock(&xf->active)){ + qunlock(&xf->active); + sendul(xf->flushc, 0); + }else{ + qlock(&xf->active); /* wait for him to finish */ + qunlock(&xf->active); + } + xf->flushing = FALSE; + if(decref(xf) == 0) + sendp(cxfidfree, xf); + break; + } + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidattach(Xfid *x) +{ + Fcall t; + int id, hideit, scrollit; + Window *w; + char *err, *n, *dir, errbuf[ERRMAX]; + int pid, newlymade; + Rectangle r; + Image *i; + + t.qid = x->f->qid; + qlock(&all); + w = nil; + err = Eunkid; + newlymade = FALSE; + hideit = 0; + + if(x->aname[0] == 'N'){ /* N 100,100, 200, 200 - old syntax */ + n = x->aname+1; + pid = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.min.x = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.min.y = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.max.x = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.max.y = strtoul(n, &n, 0); + Allocate: + if(!goodrect(r)) + err = Ebadrect; + else{ + if(hideit) + i = allocimage(display, r, screen->chan, 0, DWhite); + else + i = allocwindow(wscreen, r, Refbackup, DWhite); + if(i){ + border(i, r, Selborder, display->black, ZP); + if(pid == 0) + pid = -1; /* make sure we don't pop a shell! - UGH */ + w = new(i, hideit, scrolling, pid, nil, nil, nil); + flushimage(display, 1); + newlymade = TRUE; + }else + err = Ewindow; + } + }else if(strncmp(x->aname, "new", 3) == 0){ /* new -dx -dy - new syntax, as in wctl */ + pid = 0; + if(parsewctl(nil, ZR, &r, &pid, nil, &hideit, &scrollit, &dir, x->aname, errbuf) < 0) + err = errbuf; + else + goto Allocate; + }else{ + id = atoi(x->aname); + w = wlookid(id); + } + x->f->w = w; + if(w == nil){ + qunlock(&all); + x->f->busy = FALSE; + filsysrespond(x->fs, x, &t, err); + return; + } + if(!newlymade) /* counteract dec() in winshell() */ + incref(w); + qunlock(&all); + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidopen(Xfid *x) +{ + Fcall t; + Window *w; + + w = x->f->w; + if(w->deleted){ + filsysrespond(x->fs, x, &t, Edeleted); + return; + } + switch(FILE(x->f->qid)){ + case Qconsctl: + if(w->ctlopen){ + filsysrespond(x->fs, x, &t, Einuse); + return; + } + w->ctlopen = TRUE; + break; + case Qkbdin: + if(w != wkeyboard){ + filsysrespond(x->fs, x, &t, Eperm); + return; + } + break; + case Qmouse: + if(w->mouseopen){ + filsysrespond(x->fs, x, &t, Einuse); + return; + } + /* + * Reshaped: there's a race if the appl. opens the + * window, is resized, and then opens the mouse, + * but that's rare. The alternative is to generate + * a resized event every time a new program starts + * up in a window that has been resized since the + * dawn of time. We choose the lesser evil. + */ + w->resized = FALSE; + w->mouseopen = TRUE; + break; + case Qsnarf: + if(x->mode==ORDWR || x->mode==OWRITE){ + if(tsnarf) + free(tsnarf); /* collision, but OK */ + ntsnarf = 0; + tsnarf = malloc(1); + } + break; + case Qwctl: + if(x->mode==OREAD || x->mode==ORDWR){ + /* + * It would be much nicer to implement fan-out for wctl reads, + * so multiple people can see the resizings, but rio just isn't + * structured for that. It's structured for /dev/cons, which gives + * alternate data to alternate readers. So to keep things sane for + * wctl, we compromise and give an error if two people try to + * open it. Apologies. + */ + if(w->wctlopen){ + filsysrespond(x->fs, x, &t, Einuse); + return; + } + w->wctlopen = TRUE; + w->wctlready = 1; + wsendctlmesg(w, Wakeup, ZR, nil); + } + break; + } + t.qid = x->f->qid; + t.iounit = messagesize-IOHDRSZ; + x->f->open = TRUE; + x->f->mode = x->mode; + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidclose(Xfid *x) +{ + Fcall t; + Window *w; + int nb, nulls; + + w = x->f->w; + switch(FILE(x->f->qid)){ + case Qconsctl: + if(w->rawing){ + w->rawing = FALSE; + wsendctlmesg(w, Rawoff, ZR, nil); + } + if(w->holding){ + w->holding = FALSE; + wsendctlmesg(w, Holdoff, ZR, nil); + } + w->ctlopen = FALSE; + break; + case Qcursor: + w->cursorp = nil; + wsetcursor(w, FALSE); + break; + case Qmouse: + w->resized = FALSE; + w->mouseopen = FALSE; + if(w->i != nil) + wsendctlmesg(w, Refresh, w->i->r, nil); + break; + /* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */ + case Qsnarf: + if(x->f->mode==ORDWR || x->f->mode==OWRITE){ + snarf = runerealloc(snarf, ntsnarf+1); + cvttorunes(tsnarf, ntsnarf, snarf, &nb, &nsnarf, &nulls); + free(tsnarf); + tsnarf = nil; + ntsnarf = 0; + } + break; + case Qwctl: + if(x->f->mode==OREAD || x->f->mode==ORDWR) + w->wctlopen = FALSE; + break; + } + wclose(w); + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidwrite(Xfid *x) +{ + Fcall fc; + int c, cnt, qid, nb, off, nr; + char buf[256], *p; + Point pt; + Window *w; + Rune *r; + Conswritemesg cwm; + Stringpair pair; + enum { CWdata, CWflush, NCW }; + Alt alts[NCW+1]; + + w = x->f->w; + if(w->deleted){ + filsysrespond(x->fs, x, &fc, Edeleted); + return; + } + qid = FILE(x->f->qid); + cnt = x->count; + off = x->offset; + x->data[cnt] = 0; + switch(qid){ + case Qcons: + nr = x->f->nrpart; + if(nr > 0){ + memmove(x->data+nr, x->data, cnt); /* there's room: see malloc in filsysproc */ + memmove(x->data, x->f->rpart, nr); + cnt += nr; + x->f->nrpart = 0; + } + r = runemalloc(cnt); + cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil); + /* approach end of buffer */ + while(fullrune(x->data+nb, cnt-nb)){ + c = nb; + nb += chartorune(&r[nr], x->data+c); + if(r[nr]) + nr++; + } + if(nb < cnt){ + memmove(x->f->rpart, x->data+nb, cnt-nb); + x->f->nrpart = cnt-nb; + } + x->flushtag = x->tag; + + alts[CWdata].c = w->conswrite; + alts[CWdata].v = &cwm; + alts[CWdata].op = CHANRCV; + alts[CWflush].c = x->flushc; + alts[CWflush].v = nil; + alts[CWflush].op = CHANRCV; + alts[NCW].op = CHANEND; + + switch(alt(alts)){ + case CWdata: + break; + case CWflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + pair.s = runemalloc(1); + pair.ns = 0; + send(cwm.cw, &pair); /* wake up window with empty data */ + filsyscancel(x); + return; + } + qlock(&x->active); + pair.s = r; + pair.ns = nr; + send(cwm.cw, &pair); + fc.count = x->count; + filsysrespond(x->fs, x, &fc, nil); + qunlock(&x->active); + return; + + case Qconsctl: + if(strncmp(x->data, "holdon", 6)==0){ + if(w->holding++ == 0) + wsendctlmesg(w, Holdon, ZR, nil); + break; + } + if(strncmp(x->data, "holdoff", 7)==0 && w->holding){ + if(--w->holding == FALSE) + wsendctlmesg(w, Holdoff, ZR, nil); + break; + } + if(strncmp(x->data, "rawon", 5)==0){ + if(w->holding){ + w->holding = FALSE; + wsendctlmesg(w, Holdoff, ZR, nil); + } + if(w->rawing++ == 0) + wsendctlmesg(w, Rawon, ZR, nil); + break; + } + if(strncmp(x->data, "rawoff", 6)==0 && w->rawing){ + if(--w->rawing == 0) + wsendctlmesg(w, Rawoff, ZR, nil); + break; + } + filsysrespond(x->fs, x, &fc, "unknown control message"); + return; + + case Qcursor: + if(cnt < 2*4+2*2*16) + w->cursorp = nil; + else{ + w->cursor.offset.x = BGLONG(x->data+0*4); + w->cursor.offset.y = BGLONG(x->data+1*4); + memmove(w->cursor.clr, x->data+2*4, 2*2*16); + w->cursorp = &w->cursor; + } + wsetcursor(w, !sweeping); + break; + + case Qlabel: + if(off != 0){ + filsysrespond(x->fs, x, &fc, "non-zero offset writing label"); + return; + } + free(w->label); + w->label = emalloc(cnt+1); + memmove(w->label, x->data, cnt); + w->label[cnt] = 0; + break; + + case Qmouse: + if(w!=input || Dx(w->screenr)<=0) + break; + if(x->data[0] != 'm'){ + filsysrespond(x->fs, x, &fc, Ebadmouse); + return; + } + p = nil; + pt.x = strtoul(x->data+1, &p, 0); + if(p == nil){ + filsysrespond(x->fs, x, &fc, Eshort); + return; + } + pt.y = strtoul(p, nil, 0); + if(w==input && wpointto(mouse->xy)==w) + wsendctlmesg(w, Movemouse, Rpt(pt, pt), nil); + break; + + case Qsnarf: + /* always append only */ + if(ntsnarf > MAXSNARF){ /* avoid thrashing when people cut huge text */ + filsysrespond(x->fs, x, &fc, Elong); + return; + } + tsnarf = erealloc(tsnarf, ntsnarf+cnt+1); /* room for NUL */ + memmove(tsnarf+ntsnarf, x->data, cnt); + ntsnarf += cnt; + snarfversion++; + break; + + case Qwdir: + if(cnt == 0) + break; + if(x->data[cnt-1] == '\n'){ + if(cnt == 1) + break; + x->data[cnt-1] = '\0'; + } + /* assume data comes in a single write */ + /* + * Problem: programs like dossrv, ftp produce illegal UTF; + * we must cope by converting it first. + */ + snprint(buf, sizeof buf, "%.*s", cnt, x->data); + if(buf[0] == '/'){ + free(w->dir); + w->dir = estrdup(buf); + }else{ + p = emalloc(strlen(w->dir) + 1 + strlen(buf) + 1); + sprint(p, "%s/%s", w->dir, buf); + free(w->dir); + w->dir = cleanname(p); + } + break; + + case Qkbdin: + keyboardsend(x->data, cnt); + break; + + case Qwctl: + if(writewctl(x, buf) < 0){ + filsysrespond(x->fs, x, &fc, buf); + return; + } + flushimage(display, 1); + break; + + default: + fprint(2, buf, "unknown qid %d in write\n", qid); + sprint(buf, "unknown qid in write"); + filsysrespond(x->fs, x, &fc, buf); + return; + } + fc.count = cnt; + filsysrespond(x->fs, x, &fc, nil); +} + +int +readwindow(Image *i, char *t, Rectangle r, int offset, int n) +{ + int ww, y; + + offset -= 5*12; + ww = bytesperline(r, screen->depth); + r.min.y += offset/ww; + if(r.min.y >= r.max.y) + return 0; + y = r.min.y + n/ww; + if(y < r.max.y) + r.max.y = y; + if(r.max.y <= r.min.y) + return 0; + return unloadimage(i, r, (uchar*)t, n); +} + +void +xfidread(Xfid *x) +{ + Fcall fc; + int n, off, cnt, c; + uint qid; + char buf[128], *t; + char cbuf[30]; + Window *w; + Mouse ms; + Rectangle r; + Image *i; + Channel *c1, *c2; /* chan (tuple(char*, int)) */ + Consreadmesg crm; + Mousereadmesg mrm; + Consreadmesg cwrm; + Stringpair pair; + enum { CRdata, CRflush, NCR }; + enum { MRdata, MRflush, NMR }; + enum { WCRdata, WCRflush, NWCR }; + Alt alts[NCR+1]; + + w = x->f->w; + if(w->deleted){ + filsysrespond(x->fs, x, &fc, Edeleted); + return; + } + qid = FILE(x->f->qid); + off = x->offset; + cnt = x->count; + switch(qid){ + case Qcons: + x->flushtag = x->tag; + + alts[CRdata].c = w->consread; + alts[CRdata].v = &crm; + alts[CRdata].op = CHANRCV; + alts[CRflush].c = x->flushc; + alts[CRflush].v = nil; + alts[CRflush].op = CHANRCV; + alts[NMR].op = CHANEND; + + switch(alt(alts)){ + case CRdata: + break; + case CRflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + c1 = crm.c1; + c2 = crm.c2; + t = malloc(cnt+UTFmax+1); /* room to unpack partial rune plus */ + pair.s = t; + pair.ns = cnt; + send(c1, &pair); + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + recv(c2, nil); /* wake up window and toss data */ + free(t); + filsyscancel(x); + return; + } + qlock(&x->active); + recv(c2, &pair); + fc.data = pair.s; + fc.count = pair.ns; + filsysrespond(x->fs, x, &fc, nil); + free(t); + qunlock(&x->active); + break; + + case Qlabel: + n = strlen(w->label); + if(off > n) + off = n; + if(off+cnt > n) + cnt = n-off; + fc.data = w->label+off; + fc.count = cnt; + filsysrespond(x->fs, x, &fc, nil); + break; + + case Qmouse: + x->flushtag = x->tag; + + alts[MRdata].c = w->mouseread; + alts[MRdata].v = &mrm; + alts[MRdata].op = CHANRCV; + alts[MRflush].c = x->flushc; + alts[MRflush].v = nil; + alts[MRflush].op = CHANRCV; + alts[NMR].op = CHANEND; + + switch(alt(alts)){ + case MRdata: + break; + case MRflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + recv(mrm.cm, nil); /* wake up window and toss data */ + filsyscancel(x); + return; + } + qlock(&x->active); + recv(mrm.cm, &ms); + c = 'm'; + if(w->resized) + c = 'r'; + n = sprint(buf, "%c%11d %11d %11d %11ld ", c, ms.xy.x, ms.xy.y, ms.buttons, ms.msec); + w->resized = 0; + fc.data = buf; + fc.count = min(n, cnt); + filsysrespond(x->fs, x, &fc, nil); + qunlock(&x->active); + break; + + case Qcursor: + filsysrespond(x->fs, x, &fc, "cursor read not implemented"); + break; + + /* The algorithm for snarf and text is expensive but easy and rarely used */ + case Qsnarf: + getsnarf(); + if(nsnarf) + t = runetobyte(snarf, nsnarf, &n); + else { + t = nil; + n = 0; + } + goto Text; + + case Qtext: + t = wcontents(w, &n); + goto Text; + + Text: + if(off > n){ + off = n; + cnt = 0; + } + if(off+cnt > n) + cnt = n-off; + fc.data = t+off; + fc.count = cnt; + filsysrespond(x->fs, x, &fc, nil); + free(t); + break; + + case Qwdir: + t = estrdup(w->dir); + n = strlen(t); + goto Text; + + case Qwinid: + n = sprint(buf, "%11d ", w->id); + t = estrdup(buf); + goto Text; + + + case Qwinname: + n = strlen(w->name); + if(n == 0){ + filsysrespond(x->fs, x, &fc, "window has no name"); + break; + } + t = estrdup(w->name); + goto Text; + + case Qwindow: + i = w->i; + if(i == nil || Dx(w->screenr)<=0){ + filsysrespond(x->fs, x, &fc, Enowindow); + return; + } + r = w->screenr; + goto caseImage; + + case Qscreen: + i = display->image; + if(i == nil){ + filsysrespond(x->fs, x, &fc, "no top-level screen"); + break; + } + r = i->r; + /* fall through */ + + caseImage: + if(off < 5*12){ + n = sprint(buf, "%11s %11d %11d %11d %11d ", + chantostr(cbuf, screen->chan), + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y); + t = estrdup(buf); + goto Text; + } + t = malloc(cnt); + fc.data = t; + n = readwindow(i, t, r, off, cnt); /* careful; fc.count is unsigned */ + if(n < 0){ + buf[0] = 0; + errstr(buf, sizeof buf); + filsysrespond(x->fs, x, &fc, buf); + }else{ + fc.count = n; + filsysrespond(x->fs, x, &fc, nil); + } + free(t); + return; + + case Qwctl: /* read returns rectangle, hangs if not resized */ + if(cnt < 4*12){ + filsysrespond(x->fs, x, &fc, Etooshort); + break; + } + x->flushtag = x->tag; + + alts[WCRdata].c = w->wctlread; + alts[WCRdata].v = &cwrm; + alts[WCRdata].op = CHANRCV; + alts[WCRflush].c = x->flushc; + alts[WCRflush].v = nil; + alts[WCRflush].op = CHANRCV; + alts[NMR].op = CHANEND; + + switch(alt(alts)){ + case WCRdata: + break; + case WCRflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + c1 = cwrm.c1; + c2 = cwrm.c2; + t = malloc(cnt+1); /* be sure to have room for NUL */ + pair.s = t; + pair.ns = cnt+1; + send(c1, &pair); + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + recv(c2, nil); /* wake up window and toss data */ + free(t); + filsyscancel(x); + return; + } + qlock(&x->active); + recv(c2, &pair); + fc.data = pair.s; + if(pair.ns > cnt) + pair.ns = cnt; + fc.count = pair.ns; + filsysrespond(x->fs, x, &fc, nil); + free(t); + qunlock(&x->active); + break; + + default: + fprint(2, "unknown qid %d in read\n", qid); + sprint(buf, "unknown qid in read"); + filsysrespond(x->fs, x, &fc, buf); + break; + } +} |