this sucks!

This commit is contained in:
Yuvia 2025-08-11 12:31:26 +00:00
commit f8064ab70e
12 changed files with 6402 additions and 0 deletions

343
dat.h Normal file
View file

@ -0,0 +1,343 @@
enum
{
Qdir, /* /dev for this window */
Qscreen,
Qsnarf,
Qwctl,
Qtap,
Qwsys, /* directory of window directories */
Qwsysdir, /* window directory, child of wsys */
Qcons,
Qconsctl,
Qcursor,
Qwdir,
Qwinid,
Qwinname,
Qlabel,
Qkbd,
Qmouse,
Qtext,
Qwindow,
QMAX,
Qglobal = Qcons, /* anything >= must have non nil window */
};
#define STACK 8192
#define MAXSNARF 100*1024
typedef struct Consreadmesg Consreadmesg;
typedef struct Conswritemesg Conswritemesg;
typedef struct Kbdreadmesg Kbdreadmesg;
typedef struct Stringpair Stringpair;
typedef struct Dirtab Dirtab;
typedef struct Fid Fid;
typedef struct Filsys Filsys;
typedef struct Mouseinfo Mouseinfo;
typedef struct Mousereadmesg Mousereadmesg;
typedef struct Mousestate Mousestate;
typedef struct Ref Ref;
typedef struct Timer Timer;
typedef struct Wctlmesg Wctlmesg;
typedef struct Window Window;
typedef struct Xfid Xfid;
typedef struct Tapmesg Tapmesg;
enum
{
Selborder = 4, /* border of selected window */
Unselborder = 1, /* border of unselected window */
Scrollwid = 12, /* width of scroll bar */
Scrollgap = 4, /* gap right of scroll bar */
BIG = 3, /* factor by which window dimension can exceed screen */
TRUE = 1,
FALSE = 0,
};
#define QID(w,q) ((w<<8)|(q))
#define WIN(q) ((((ulong)(q).path)>>8) & 0xFFFFFF)
#define FILE(q) (((ulong)(q).path) & 0xFF)
enum /* control messages */
{
Wakeup,
Reshaped,
Topped,
Repaint,
Refresh,
Movemouse,
Rawon,
Rawoff,
Holdon,
Holdoff,
Truncate,
Deleted,
Exited,
};
struct Wctlmesg
{
int type;
Rectangle r;
void *p;
};
struct Conswritemesg
{
Channel *cw; /* chan(Stringpair) */
};
struct Consreadmesg
{
Channel *c1; /* chan(tuple(char*, int) == Stringpair) */
Channel *c2; /* chan(tuple(char*, int) == Stringpair) */
};
struct Mousereadmesg
{
Channel *cm; /* chan(Mouse) */
};
struct Stringpair /* rune and nrune or byte and nbyte */
{
void *s;
int ns;
};
struct Mousestate
{
Mouse;
ulong counter; /* serial no. of mouse event */
};
struct Mouseinfo
{
Mousestate queue[16];
int ri; /* read index into queue */
int wi; /* write index */
ulong counter; /* serial no. of last mouse event we received */
ulong lastcounter; /* serial no. of last mouse event sent to client */
int lastb; /* last button state we received */
uchar qfull; /* filled the queue; no more recording until client comes back */
};
struct Window
{
Ref;
QLock;
Frame;
Image *i; /* window image, nil when deleted */
Mousectl mc;
Mouseinfo mouse;
Channel *ck; /* chan(char*) */
Channel *cctl; /* chan(Wctlmesg)[4] */
Channel *conswrite; /* chan(Conswritemesg) */
Channel *consread; /* chan(Consreadmesg) */
Channel *mouseread; /* chan(Mousereadmesg) */
Channel *wctlread; /* chan(Consreadmesg) */
Channel *kbdread; /* chan(Consreadmesg) */
Channel *complete; /* chan(Completion*) */
Channel *gone; /* chan(char*) */
uint nr; /* number of runes in window */
uint maxr; /* number of runes allocated in r */
Rune *r;
uint nraw;
Rune *raw;
uint org;
uint q0;
uint q1;
uint qh;
int id;
char name[32];
uint namecount;
Rectangle scrollr;
/*
* Rio once used originwindow, so screenr could be different from i->r.
* Now they're always the same but the code doesn't assume so.
*/
Rectangle screenr; /* screen coordinates of window */
int resized;
int wctlready;
Rectangle lastsr;
int topped;
int notefd;
uchar scrolling;
Cursor cursor;
Cursor *cursorp;
uchar holding;
uchar rawing;
uchar ctlopen;
uchar wctlopen;
uchar deleted;
uchar mouseopen;
uchar kbdopen;
uchar keyup;
uchar winnameread;
char *label;
char *dir;
};
void winctl(void*);
void winshell(void*);
Window* wlookid(int);
Window* wmk(Image*, Mousectl*, Channel*, Channel*, int);
Window* wpointto(Point);
Window* wtop(Point);
void wtopme(Window*);
void wbottomme(Window*);
char* wcontents(Window*, int*);
int wclose(Window*);
uint wbacknl(Window*, uint, uint);
void wcurrent(Window*);
void wuncurrent(Window*);
void wcut(Window*);
void wpaste(Window*);
void wplumb(Window*);
void wlook(Window*);
void wscrdraw(Window*);
void wscroll(Window*, int);
void wsend(Window*);
void wsendctlmesg(Window*, int, Rectangle, void*);
void wsetcursor(Window*, int);
void wsetname(Window*);
void wsetorigin(Window*, uint, int);
void wsetpid(Window*, int, int);
void wshow(Window*, uint);
void wsnarf(Window*);
void wscrsleep(Window*, uint);
struct Dirtab
{
char *name;
uchar type;
uint qid;
uint perm;
};
struct Fid
{
int fid;
int busy;
int open;
int mode;
Qid qid;
Window *w;
Dirtab *dir;
Fid *next;
int nrpart;
uchar rpart[UTFmax];
};
struct Xfid
{
Ref;
Xfid *next;
Xfid *free;
Fcall;
Channel *c; /* chan(void(*)(Xfid*)) */
Fid *f;
uchar *buf;
Filsys *fs;
int flushtag; /* our tag, so flush can find us */
Channel *flushc; /* channel(int) to notify us we're being flushed */
};
Channel* xfidinit(void);
void xfidctl(void*);
void xfidflush(Xfid*);
void xfidattach(Xfid*);
void xfidopen(Xfid*);
void xfidclose(Xfid*);
void xfidread(Xfid*);
void xfidwrite(Xfid*);
enum
{
Nhash = 16,
};
struct Filsys
{
int cfd;
int sfd;
int pid;
char *user;
Channel *cxfidalloc; /* chan(Xfid*) */
Channel *csyncflush; /* chan(int) */
Fid *fids[Nhash];
};
Filsys* filsysinit(Channel*);
int filsysmount(Filsys*, int);
Xfid* filsysrespond(Filsys*, Xfid*, Fcall*, char*);
void filsyscancel(Xfid*);
void deletetimeoutproc(void*);
struct Timer
{
int dt;
int cancel;
Channel *c; /* chan(int) */
Timer *next;
};
Font *font;
Mousectl *mousectl;
Mouse *mouse;
Display *display;
Image *view;
Screen *wscreen;
Cursor boxcursor;
Cursor crosscursor;
Cursor sightcursor;
Cursor whitearrow;
Cursor query;
Cursor *corners[9];
Cursor skull;
Image *background;
Image *cols[NCOL];
Image *titlecol;
Image *lighttitlecol;
Image *dholdcol;
Image *holdcol;
Image *lightholdcol;
Image *paleholdcol;
Image *paletextcol;
Image *sizecol;
int reverse; /* there are no pastel paints in the dungeons and dragons world -- rob pike */
Window **window;
Window *wkeyboard; /* window of simulated keyboard */
int nwindow;
int snarffd;
int gotscreen;
int servekbd;
Channel *opentap; /* open fromtap or totap */
Channel *closetap; /* close fromtap or totap */
Channel *fromtap; /* keyboard output from the tap program */
Channel *totap; /* our keyboard input to tap program */
Window *input;
QLock all; /* BUG */
Filsys *filsys;
Window *hidden[100];
int nhidden;
int nsnarf;
Rune* snarf;
int scrolling;
int maxtab;
Channel* winclosechan;
char *startdir;
int sweeping;
char srvpipe[];
char srvwctl[];
int errorshouldabort;
int menuing; /* menu action is pending; waiting for window to be indicated */
int snarfversion; /* updated each time it is written */
int messagesize; /* negotiated in 9P version setup */
int shiftdown;
int debug;

216
data.c Normal file
View file

@ -0,0 +1,216 @@
#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 skull = {
{-7,-7},
{0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7,
0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8,
0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff,
0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03,
0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0,
0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27,
0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
};
Cursor t = {
{-7, -8},
{0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0,
0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f,
0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80,
0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
};
Cursor tr = {
{-11, -4},
{0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1,
0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88,
0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88,
0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, },
{0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e,
0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70,
0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, }
};
Cursor r = {
{-8, -7},
{0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58,
0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02,
0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58,
0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, },
{0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80,
0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc,
0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80,
0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
};
Cursor br = {
{-11, -11},
{0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05,
0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, },
{0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa,
0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, }
};
Cursor b = {
{-7, -7},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70,
0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, },
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe,
0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
};
Cursor bl = {
{-4, -11},
{0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01,
0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, },
{0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe,
0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, }
};
Cursor l = {
{-7, -7},
{0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20,
0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20,
0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20,
0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, },
{0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0,
0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0,
0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, }
};
Cursor *corners[9] = {
&tl, &t, &tr,
&l, nil, &r,
&bl, &b, &br,
};
void
iconinit(void)
{
background = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x777777FF);
/* greys are multiples of 0x11111100+0xFF, 14* being palest */
cols[BACK] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xFFFFFFFF^reverse);
cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF^reverse);
cols[TEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF^reverse);
cols[HTEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF);
if(!reverse) {
cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
} else {
cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPurpleblue);
titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPurpleblue);
lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x222222FF);
}
dholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
paletextcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF^reverse);
sizecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DRed);
if(reverse == 0)
holdcol = dholdcol;
else
holdcol = paleholdcol;
}

36
fns.h Normal file
View file

@ -0,0 +1,36 @@
int whide(Window*);
int wunhide(Window*);
void freescrtemps(void);
int parsewctl(char**, Rectangle, Rectangle*, int*, int*, int*, int*, char**, char*, char*);
int writewctl(Xfid*, char*);
Window *new(Image*, int, int, int, char*, char*, char**);
void riosetcursor(Cursor*);
int min(int, int);
int max(int, int);
Rune* strrune(Rune*, Rune);
int isalnum(Rune);
int isspace(Rune);
void timerstop(Timer*);
void timercancel(Timer*);
Timer* timerstart(int);
void error(char*);
void killprocs(void);
int shutdown(void*, char*);
void iconinit(void);
void *erealloc(void*, uint);
void *emalloc(uint);
char *estrdup(char*);
void button3menu(void);
void button2menu(Window*);
void cvttorunes(char*, int, Rune*, int*, int*, int*);
/* was (byte*,int) runetobyte(Rune*, int); */
char* runetobyte(Rune*, int, int*);
void putsnarf(void);
void getsnarf(void);
void timerinit(void);
int goodrect(Rectangle);
int inborder(Rectangle, Point);
#define runemalloc(n) malloc((n)*sizeof(Rune))
#define runerealloc(a, n) realloc(a, (n)*sizeof(Rune))
#define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune))

701
fsys.c Normal file
View file

@ -0,0 +1,701 @@
#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";
char Enomem[] = "out of memory";
int messagesize = 8192+IOHDRSZ; /* good start */
Dirtab dirtab[]=
{
{ ".", QTDIR, Qdir, 0500|DMDIR },
{ "screen", QTFILE, Qscreen, 0400 },
{ "snarf", QTFILE, Qsnarf, 0600 },
{ "wctl", QTFILE, Qwctl, 0600 },
{ "kbdtap", QTFILE, Qtap, 0660 },
{ "wsys", QTDIR, Qwsys, 0500|DMDIR },
{ "cons", QTFILE, Qcons, 0600 },
{ "cursor", QTFILE, Qcursor, 0600 },
{ "consctl", QTFILE, Qconsctl, 0200 },
{ "winid", QTFILE, Qwinid, 0400 },
{ "winname", QTFILE, Qwinname, 0400 },
{ "label", QTFILE, Qlabel, 0600 },
{ "kbd", QTFILE, Qkbd, 0600 },
{ "mouse", QTFILE, Qmouse, 0600 },
{ "text", QTFILE, Qtext, 0600 },
{ "wdir", QTFILE, Qwdir, 0600 },
{ "window", QTFILE, Qwindow, 0400 },
{ nil, }
};
static uint getclock(void);
static void filsysproc(void*);
static Fid* newfid(Filsys*, int);
static int dostat(Filsys*, int, Dirtab*, uchar*, int, uint);
int clockfd;
int firstmessage = 1;
char srvpipe[64];
char srvwctl[64];
static Xfid* filsysflush(Filsys*, Xfid*, Fid*);
static Xfid* filsysversion(Filsys*, Xfid*, Fid*);
static Xfid* filsysauth(Filsys*, Xfid*, Fid*);
static Xfid* filsysattach(Filsys*, Xfid*, Fid*);
static Xfid* filsyswalk(Filsys*, Xfid*, Fid*);
static Xfid* filsysopen(Filsys*, Xfid*, Fid*);
static Xfid* filsyscreate(Filsys*, Xfid*, Fid*);
static Xfid* filsysread(Filsys*, Xfid*, Fid*);
static Xfid* filsyswrite(Filsys*, Xfid*, Fid*);
static Xfid* filsysclunk(Filsys*, Xfid*, Fid*);
static Xfid* filsysremove(Filsys*, Xfid*, Fid*);
static Xfid* filsysstat(Filsys*, Xfid*, Fid*);
static Xfid* filsyswstat(Filsys*, Xfid*, Fid*);
Xfid* (*fcall[Tmax])(Filsys*, Xfid*, Fid*) =
{
[Tflush] = filsysflush,
[Tversion] = filsysversion,
[Tauth] = filsysauth,
[Tattach] = filsysattach,
[Twalk] = filsyswalk,
[Topen] = filsysopen,
[Tcreate] = filsyscreate,
[Tread] = filsysread,
[Twrite] = filsyswrite,
[Tclunk] = filsysclunk,
[Tremove]= filsysremove,
[Tstat] = filsysstat,
[Twstat] = filsyswstat,
};
void
post(char *name, char *envname, int srvfd)
{
int fd;
char buf[32];
fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600);
if(fd < 0)
error(name);
snprint(buf, sizeof(buf), "%d", srvfd);
if(write(fd, buf, strlen(buf)) != strlen(buf))
error("srv write");
putenv(envname, name);
}
/*
* Build pipe with OCEXEC set on second fd.
* Can't put it on both because we want to post one in /srv.
*/
int
cexecpipe(int *p0, int *p1)
{
/* pipe the hard way to get close on exec */
if(bind("#|", "/mnt/temp", MREPL) == -1)
return -1;
*p0 = open("/mnt/temp/data", ORDWR);
*p1 = open("/mnt/temp/data1", ORDWR|OCEXEC);
unmount(nil, "/mnt/temp");
if(*p0<0 || *p1<0)
return -1;
return 0;
}
Filsys*
filsysinit(Channel *cxfidalloc)
{
Filsys *fs;
fs = emalloc(sizeof(Filsys));
if(cexecpipe(&fs->cfd, &fs->sfd) < 0)
goto Rescue;
fmtinstall('F', fcallfmt);
clockfd = open("/dev/time", OREAD|OCEXEC);
fs->user = getuser();
fs->csyncflush = chancreate(sizeof(int), 0);
if(fs->csyncflush == nil)
error("chancreate syncflush");
fs->cxfidalloc = cxfidalloc;
proccreate(filsysproc, fs, 10000);
snprint(srvpipe, sizeof(srvpipe), "/srv/rio.%s.%lud", fs->user, (ulong)getpid());
post(srvpipe, "wsys", fs->cfd);
return fs;
Rescue:
free(fs);
return nil;
}
static
void
filsysproc(void *arg)
{
int n;
Xfid *x;
Fid *f;
Fcall t;
uchar *buf;
Filsys *fs;
threadsetname("FILSYSPROC");
fs = arg;
fs->pid = getpid();
x = nil;
for(;;){
buf = malloc(messagesize+UTFmax); /* UTFmax for appending partial rune in xfidwrite */
if(buf == nil)
error(Enomem);
n = read9pmsg(fs->sfd, buf, messagesize);
if(n <= 0){
yield(); /* if threadexitsall'ing, will not return */
fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n);
errorshouldabort = 0;
error("eof or i/o error on server channel");
}
if(x == nil){
send(fs->cxfidalloc, nil);
recv(fs->cxfidalloc, &x);
x->fs = fs;
}
x->buf = buf;
if(convM2S(buf, n, x) != n)
error("convert error in convM2S");
if(debug)
fprint(2, "rio:<-%F\n", &x->Fcall);
if(fcall[x->type] == nil)
x = filsysrespond(fs, x, &t, Ebadfcall);
else{
if(x->type==Tversion || x->type==Tauth)
f = nil;
else
f = newfid(fs, x->fid);
x->f = f;
x = (*fcall[x->type])(fs, x, f);
}
firstmessage = 0;
}
}
/*
* Called only from a different FD group
*/
int
filsysmount(Filsys *fs, int id)
{
char buf[32];
close(fs->sfd); /* close server end so mount won't hang if exiting */
snprint(buf, sizeof buf, "%d", id);
if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) == -1){
fprint(2, "mount failed: %r\n");
return -1;
}
if(bind("/mnt/wsys", "/dev", MBEFORE) == -1){
fprint(2, "bind failed: %r\n");
return -1;
}
return 0;
}
Xfid*
filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err)
{
int n;
if(err){
t->type = Rerror;
t->ename = err;
}else
t->type = x->type+1;
t->fid = x->fid;
t->tag = x->tag;
if(x->buf == nil)
error("no buffer in respond");
n = convS2M(t, x->buf, messagesize);
if(n <= 0)
error("convert error in convS2M");
if(write(fs->sfd, x->buf, n) != n)
error("write error in respond");
if(debug)
fprint(2, "rio:->%F\n", t);
free(x->buf);
x->buf = nil;
x->flushtag = -1;
return x;
}
void
filsyscancel(Xfid *x)
{
if(x->buf){
free(x->buf);
x->buf = nil;
}
}
static
Xfid*
filsysversion(Filsys *fs, Xfid *x, Fid*)
{
Fcall t;
if(!firstmessage)
return filsysrespond(x->fs, x, &t, "version request not first message");
if(x->msize < 256)
return filsysrespond(x->fs, x, &t, "version: message size too small");
messagesize = x->msize;
t.msize = messagesize;
t.version = "9P2000";
if(strncmp(x->version, "9P", 2) != 0)
t.version = "unknown";
return filsysrespond(fs, x, &t, nil);
}
static
Xfid*
filsysauth(Filsys *fs, Xfid *x, Fid*)
{
Fcall t;
return filsysrespond(fs, x, &t, "rio: authentication not required");
}
static
Xfid*
filsysflush(Filsys *fs, Xfid *x, Fid*)
{
sendp(x->c, xfidflush);
/*
* flushes need to be replied in order. xfidflush() will
* awaken us when the flush has been queued.
*/
recv(fs->csyncflush, nil);
return nil;
}
static
Xfid*
filsysattach(Filsys *, Xfid *x, Fid *f)
{
Fcall t;
if(strcmp(x->uname, x->fs->user) != 0)
return filsysrespond(x->fs, x, &t, Eperm);
f->busy = TRUE;
f->open = FALSE;
f->qid.path = Qdir;
f->qid.type = QTDIR;
f->qid.vers = 0;
f->dir = dirtab;
f->nrpart = 0;
sendp(x->c, xfidattach);
return nil;
}
static
int
numeric(char *s)
{
for(; *s!='\0'; s++)
if(*s<'0' || '9'<*s)
return 0;
return 1;
}
static
int
skipdir(char *name)
{
/* don't serve these if it's provided in the environment */
if(snarffd>=0 && strcmp(name, "snarf")==0)
return 1;
if(gotscreen && strcmp(name, "screen")==0)
return 1;
if(!servekbd && strcmp(name, "kbd")==0)
return 1;
return 0;
}
static
Xfid*
filsyswalk(Filsys *fs, Xfid *x, Fid *f)
{
Fcall t;
Fid *nf;
int i, id;
uchar type;
ulong path;
Dirtab *d, *dir;
Window *w;
char *err;
Qid qid;
if(f->open)
return filsysrespond(fs, x, &t, "walk of open file");
nf = nil;
if(x->fid != x->newfid){
/* BUG: check exists */
nf = newfid(fs, x->newfid);
if(nf->busy)
return filsysrespond(fs, x, &t, "clone to busy fid");
nf->busy = TRUE;
nf->open = FALSE;
nf->dir = f->dir;
nf->qid = f->qid;
nf->w = f->w;
if(f->w != nil)
incref(f->w);
nf->nrpart = 0; /* not open, so must be zero */
f = nf; /* walk f */
}
t.nwqid = 0;
err = nil;
/* update f->qid, f->dir only if walk completes */
qid = f->qid;
dir = f->dir;
if(x->nwname > 0){
for(i=0; 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);
if(f->w)
sendp(winclosechan, f->w);
f->w = w;
dir = dirtab;
goto Accept;
}
if(skipdir(x->wname[i]))
break;
id = WIN(f->qid);
d = dirtab;
d++; /* skip '.' */
for(; d->name; d++)
if(strcmp(x->wname[i], d->name) == 0){
if(f->w == nil && d->qid >= Qglobal)
break;
path = d->qid;
type = d->type;
dir = d;
goto Accept;
}
break; /* file not found */
}
if(i==0 && err==nil)
err = Eexist;
}
if(err!=nil || t.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 but Qtext, so just disregard */
if(FILE(f->qid) != Qtext)
x->mode &= ~OTRUNC;
x->mode &= ~OCEXEC;
/* can't execute or remove anything */
if(x->mode==OEXEC || (x->mode&ORCLOSE))
goto Deny;
switch(x->mode & ~OTRUNC){
default:
goto Deny;
case OREAD:
m = 0400;
break;
case OWRITE:
m = 0200;
break;
case ORDWR:
m = 0600;
break;
}
if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
goto Deny;
sendp(x->c, xfidopen);
return nil;
Deny:
return filsysrespond(fs, x, &t, Eperm);
}
static
Xfid*
filsyscreate(Filsys *fs, Xfid *x, Fid*)
{
Fcall t;
return filsysrespond(fs, x, &t, Eperm);
}
static
int
idcmp(void *a, void *b)
{
return *(int*)a - *(int*)b;
}
static
Xfid*
filsysread(Filsys *fs, Xfid *x, Fid *f)
{
Fcall t;
uchar *b;
int i, n, o, e, len, j, k, *ids;
Dirtab *d, dt;
uint clock;
char buf[32];
if((f->qid.type & QTDIR) == 0){
sendp(x->c, xfidread);
return nil;
}
o = x->offset;
e = x->offset+x->count;
clock = getclock();
b = malloc(messagesize-IOHDRSZ); /* avoid memset of emalloc */
if(b == nil)
return filsysrespond(fs, x, &t, Enomem);
n = 0;
switch(FILE(f->qid)){
case Qdir:
case Qwsysdir:
d = dirtab;
d++; /* first entry is '.' */
for(i=0; d->name!=nil && i<e; d++){
if(skipdir(d->name))
continue;
if(f->w == nil && d->qid >= Qglobal)
continue;
len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock);
if(len <= BIT16SZ)
break;
if(i >= o)
n += len;
i += len;
}
break;
case Qwsys:
qlock(&all);
ids = emalloc(nwindow*sizeof(int));
for(j=0; 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);
}

25
mkfile Normal file
View file

@ -0,0 +1,25 @@
</$objtype/mkfile
BIN=$home/bin/$objtype
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\
</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

1352
rio.c Normal file

File diff suppressed because it is too large Load diff

182
scrl.c Normal file
View file

@ -0,0 +1,182 @@
#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
Image*
scrtemps(void)
{
int h;
if(scrtmp == nil){
h = BIG*Dy(screen->r);
scrtmp = allocimage(display, Rect(0, 0, 32, h), screen->chan, 0, DNofill);
}
return scrtmp;
}
void
freescrtemps(void)
{
if(scrtmp){
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;
b = scrtemps();
if(b == nil || w->i == nil)
return;
r = w->scrollr;
r.min.x++;
r1 = r;
draw(b, r, display->black, nil, ZP);
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);
r2 = insetrect(r2, 1);
draw(b, r1, display->black, nil, ZP);
draw(b, insetrect(r1,1), w->cols[BORD], nil, ZP);
draw(b, r2, w->cols[BACK], 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];
if(display->bufp > display->buf)
flushimage(display, 1);
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 y, my, h, first;
s = insetrect(w->scrollr, 1);
h = s.max.y-s.min.y;
oldp0 = ~0;
first = TRUE;
do{
my = w->mc.xy.y;
if(my < s.min.y)
my = s.min.y;
if(my >= s.max.y)
my = s.max.y;
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 || but == 4){
y = max(1, (my-s.min.y)/w->font->height);
p0 = wbacknl(w, w->org, y);
}else{
y = max(my, s.min.y+w->font->height);
p0 = w->org+frcharofpt(w, Pt(s.max.x, y));
}
if(oldp0 != p0)
wsetorigin(w, p0, TRUE);
oldp0 = p0;
/* debounce */
if(first){
if(display->bufp > display->buf)
flushimage(display, 1);
if(but > 3)
return;
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);
}

124
time.c Normal file
View file

@ -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;
}

159
util.c Normal file
View file

@ -0,0 +1,159 @@
#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");
setrealloctag(p, getcallerpc(&p));
return p;
}
void*
emalloc(uint n)
{
void *p;
p = malloc(n);
if(p == nil)
error("malloc failed");
setmalloctag(p, getcallerpc(&n));
memset(p, 0, n);
return p;
}
char*
estrdup(char *s)
{
char *p;
p = malloc(strlen(s)+1);
if(p == nil)
error("strdup failed");
setmalloctag(p, getcallerpc(&s));
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;
}
int
isspace(Rune c)
{
return c == 0 || c == ' ' || c == '\t' ||
c == '\n' || c == '\r' || c == '\v';
}
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;
}

500
wctl.c Normal file
View file

@ -0,0 +1,500 @@
#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(badrect(r) || !eqrect(canonrect(r), r))
return 0;
/* reasonable sizes only please */
if(Dx(r) > BIG*Dx(screen->r))
return 0;
if(Dy(r) > BIG*Dy(screen->r))
return 0;
/*
* the height has to be big enough to fit one line of text.
* that includes the border on each side with an extra pixel
* so that the text is still drawn
*/
if(Dx(r) < 100 || Dy(r) < 2*(Borderwidth+1)+font->height)
return 0;
/* window must be on screen */
if(!rectXrect(screen->r, r))
return 0;
/* must have some screen and border visible so we can move it out of the way */
if(rectinrect(screen->r, insetrect(r, Borderwidth)))
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(*maxp > max){
*minp += max-*maxp;
*maxp = max;
}
if(*minp < min){
*maxp += min-*minp;
if(*maxp > max)
*maxp = max;
*minp = min;
}
}
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, n, nt, param, xy, sign;
char *f[2], *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;
if((nt = gettokens(*cdp, f, nelem(f), " \t\r\n\v\f")) < 1)
return -1;
n = strlen(*cdp);
if((*cdp)[0] == '\'' && (*cdp)[n-1] == '\'')
((*cdp)++)[n-1] = '\0'; /* drop quotes */
s += n+(nt-1);
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, DNofill);
else
i = allocwindow(wscreen, rect, Refbackup, DNofill);
if(i == nil){
strcpy(err, Ewalloc);
return -1;
}
new(i, hideit, scrollit, pid, dir, "/bin/rc", argv);
free(argv); /* when new() returns, argv and args have been copied */
return 1;
}
int
wctlcmd(Window *w, Rectangle r, int cmd, char *err)
{
Image *i;
switch(cmd){
case Move:
r = Rect(r.min.x, r.min.y, r.min.x+Dx(w->screenr), r.min.y+Dy(w->screenr));
r = rectonscreen(r);
/* fall through */
case Resize:
if(!goodrect(r)){
strcpy(err, Ebadwr);
return -1;
}
if(Dx(w->screenr) > 0){
if(eqrect(r, w->screenr))
return 1;
if(w != input){
strcpy(err, "window not current");
return -1;
}
i = allocwindow(wscreen, r, Refbackup, DNofill);
} else { /* hidden */
if(eqrect(r, w->i->r))
return 1;
wuncurrent(w);
i = allocimage(display, r, w->i->chan, 0, DNofill);
r = ZR;
}
if(i == nil){
strcpy(err, Ewalloc);
return -1;
}
wsendctlmesg(w, Reshaped, 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:
if(Dx(w->screenr)<=0){
strcpy(err, "window is hidden");
return -1;
}
wcurrent(w);
wtopme(w);
wsendctlmesg(w, Topped, ZR, nil);
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:
switch(wunhide(w)){
case -1:
strcpy(err, "window not hidden");
return -1;
case 0:
strcpy(err, "hide failed");
return -1;
default:
break;
}
return 1;
case Delete:
wsendctlmesg(w, Deleted, ZR, nil);
return 1;
}
strcpy(err, "invalid wctl message");
return -1;
}
int
writewctl(Xfid *x, char *err)
{
int cnt, cmd, id, hideit, scrollit, pid;
char *arg, *dir;
Rectangle r;
Window *w;
w = x->f->w;
cnt = x->count;
x->data[cnt] = '\0';
id = 0;
if(w == nil)
r = ZR;
else
r = rectsubpt(w->screenr, screen->r.min);
cmd = parsewctl(&arg, r, &r, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
if(cmd < 0)
return -1;
if(id != 0){
w = wlookid(id);
if(w == 0){
strcpy(err, "no such window id");
return -1;
}
}
if(w == nil && cmd != New){
strcpy(err, "command needs to be run within a window");
return -1;
}
switch(cmd){
case New:
return wctlnew(r, arg, pid, hideit, scrollit, dir, err);
case Set:
if(pid > 0)
wsetpid(w, pid, 0);
return 1;
}
incref(w);
id = wctlcmd(w, r, cmd, err);
wclose(w);
return id;
}

1848
wind.c Normal file

File diff suppressed because it is too large Load diff

916
xfid.c Normal file
View file

@ -0,0 +1,916 @@
#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"
char Einuse[] = "file in use";
char Edeleted[] = "window deleted";
char Etooshort[] = "buffer too small";
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";
extern char Eperm[];
extern char Enomem[];
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*);
x = arg;
threadsetname("xfid.%p", x);
for(;;){
f = recvp(x->c);
if(f){
x->flushtag = x->tag;
(*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){
incref(xf); /* to hold data structures up at tail of synchronization */
if(xf->ref == 1)
error("ref 1 in flush");
xf->flushtag = -1;
break;
}
/* take over flushtag so follow up flushes wait for us */
x->flushtag = x->oldtag;
/*
* wakeup filsysflush() in the filsysproc so the next
* flush can come in.
*/
sendul(x->fs->csyncflush, 0);
if(xf){
enum { Done, Flush, End };
Alt alts[End+1];
void *f;
int z;
z = 0;
f = nil;
alts[Done].c = xf->c;
alts[Done].v = &f;
alts[Done].op = CHANSND;
alts[Flush].c = xf->flushc;
alts[Flush].v = &z;
alts[Flush].op = CHANSND;
alts[End].op = CHANEND;
while(alt(alts) != Done)
;
}
if(nbrecv(x->flushc, nil)){
filsyscancel(x);
return;
}
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;
dir = nil;
newlymade = FALSE;
hideit = 0;
scrollit = scrolling;
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, DNofill);
else
i = allocwindow(wscreen, r, Refbackup, DNofill);
if(i){
if(pid == 0)
pid = -1; /* make sure we don't pop a shell! - UGH */
w = new(i, hideit, scrollit, pid, dir, nil, nil);
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 if(strncmp(x->aname, "none", 4) == 0){
x->f->w = nil;
goto Done;
}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);
Done:
qunlock(&all);
filsysrespond(x->fs, x, &t, nil);
}
void
xfidopen(Xfid *x)
{
Fcall t;
Window *w;
w = x->f->w;
if(w != nil && w->deleted){
filsysrespond(x->fs, x, &t, Edeleted);
return;
}
switch(FILE(x->f->qid)){
case Qtext:
if(x->mode&OTRUNC)
wsendctlmesg(w, Truncate, ZR, nil);
break;
case Qconsctl:
if(w->ctlopen){
filsysrespond(x->fs, x, &t, Einuse);
return;
}
w->ctlopen = TRUE;
break;
case Qkbd:
if(w->kbdopen){
filsysrespond(x->fs, x, &t, Einuse);
return;
}
w->kbdopen = TRUE;
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)
ntsnarf = 0;
break;
case Qwctl:
if(w != nil && (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;
case Qtap:
if((x->mode == OWRITE || x->mode == ORDWR) && fromtap != nil){
filsysrespond(x->fs, x, &t, Einuse);
return;
}
if(x->mode == OREAD || x->mode == ORDWR){
if(totap != nil){
filsysrespond(x->fs, x, &t, Einuse);
return;
}
totap = chancreate(sizeof(char*), 32);
sendp(opentap, totap);
}
if(x->mode == OWRITE || x->mode == ORDWR){
fromtap = chancreate(sizeof(char*), 32);
sendp(opentap, fromtap);
}
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 Qkbd:
w->kbdopen = FALSE;
break;
case Qmouse:
w->resized = FALSE;
w->mouseopen = FALSE;
w->winnameread = 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);
ntsnarf = 0;
}
break;
case Qwctl:
if(w != nil && (x->f->mode==OREAD || x->f->mode==ORDWR))
w->wctlopen = FALSE;
break;
case Qtap:
if(fromtap != nil && (x->f->mode == OWRITE || x->f->mode == ORDWR))
sendp(closetap, fromtap);
if(totap != nil && (x->f->mode == OREAD || x->f->mode == ORDWR))
sendp(closetap, totap);
break;
}
if(w)
wclose(w);
filsysrespond(x->fs, x, &t, nil);
}
void
xfidwrite(Xfid *x)
{
Fcall fc;
int cnt, qid, nb, nr;
char err[ERRMAX], *p, *e;
Point pt;
Window *w;
Rune *r;
Conswritemesg cwm;
Stringpair pair;
enum { CWdata, CWgone, CWflush, NCW };
Alt alts[NCW+1];
w = x->f->w;
if(w != nil && w->deleted){
filsysrespond(x->fs, x, &fc, Edeleted);
return;
}
qid = FILE(x->f->qid);
cnt = x->count;
x->data[cnt] = 0;
switch(qid){
case Qcons:
case Qtext:
alts[CWdata].c = w->conswrite;
alts[CWdata].v = &cwm;
alts[CWdata].op = CHANRCV;
alts[CWgone].c = w->gone;
alts[CWgone].v = nil;
alts[CWgone].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 CWgone:
filsysrespond(x->fs, x, &fc, Edeleted);
return;
case CWflush:
filsyscancel(x);
return;
}
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;
}
r = runemalloc(cnt);
if(r == nil){
pair.ns = 0;
send(cwm.cw, &pair);
filsysrespond(x->fs, x, &fc, Enomem);
return;
}
x->f->nrpart = 0;
cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil);
/* approach end of buffer */
while(fullrune(x->data+nb, cnt-nb)){
nb += chartorune(&r[nr], x->data+nb);
if(r[nr])
nr++;
}
if(nb < cnt){
memmove(x->f->rpart, x->data+nb, cnt-nb);
x->f->nrpart = cnt-nb;
}
pair.s = r;
pair.ns = nr;
send(cwm.cw, &pair);
fc.count = x->count;
filsysrespond(x->fs, x, &fc, nil);
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 == 0)
wsendctlmesg(w, Holdoff, ZR, nil);
break;
}
if(strncmp(x->data, "rawon", 5)==0){
if(w->holding){
w->holding = 0;
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, TRUE);
break;
case Qlabel:
p = realloc(w->label, cnt+1);
if(p == nil){
filsysrespond(x->fs, x, &fc, Enomem);
return;
}
w->label = p;
w->label[cnt] = 0;
memmove(w->label, x->data, cnt);
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:
if(cnt == 0)
break;
/* always append only */
if(ntsnarf > MAXSNARF){ /* avoid thrashing when people cut huge text */
filsysrespond(x->fs, x, &fc, Elong);
return;
}
p = realloc(tsnarf, ntsnarf+cnt+1); /* room for NUL */
if(p == nil){
filsysrespond(x->fs, x, &fc, Enomem);
return;
}
tsnarf = p;
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 */
if(x->data[0] == '/'){
p = smprint("%.*s", cnt, x->data);
}else{
p = smprint("%s/%.*s", w->dir, cnt, x->data);
}
if(p == nil){
filsysrespond(x->fs, x, &fc, Enomem);
return;
}
free(w->dir);
w->dir = cleanname(p);
break;
case Qwctl:
if(writewctl(x, err) < 0){
filsysrespond(x->fs, x, &fc, err);
return;
}
break;
case Qtap:
if(cnt < 2){
filsysrespond(x->fs, x, &fc, "malformed key");
return;
}
e = x->data + cnt;
for(p = x->data; p < e; p += strlen(p)+1){
switch(*p){
case '\0':
fc.count = p - x->data;
filsysrespond(x->fs, x, &fc, "null message type");
return;
case 'z': /* ignore context change */
break;
default:
chanprint(fromtap, "%s", p);
break;
}
}
break;
default:
fprint(2, "unknown qid %d in write\n", qid);
filsysrespond(x->fs, x, &fc, "unknown qid in write");
return;
}
fc.count = cnt;
filsysrespond(x->fs, x, &fc, nil);
}
int
readwindow(Image *i, char *t, Rectangle r, int offset, int n)
{
int ww, oo, y, m;
uchar *tt;
ww = bytesperline(r, i->depth);
r.min.y += offset/ww;
if(r.min.y >= r.max.y)
return 0;
y = r.min.y + (n + ww-1)/ww;
if(y < r.max.y)
r.max.y = y;
m = ww * Dy(r);
oo = offset % ww;
if(oo == 0 && n >= m)
return unloadimage(i, r, (uchar*)t, n);
if((tt = malloc(m)) == nil)
return -1;
m = unloadimage(i, r, tt, m) - oo;
if(m > 0){
if(n < m) m = n;
memmove(t, tt + oo, m);
}
free(tt);
return m;
}
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;
Stringpair pair;
enum { Adata, Agone, Aflush, Aend };
Alt alts[Aend+1];
w = x->f->w;
if(w != nil && w->deleted){
filsysrespond(x->fs, x, &fc, Edeleted);
return;
}
qid = FILE(x->f->qid);
off = x->offset;
cnt = x->count;
switch(qid){
case Qwctl:
if(w == nil){
if(off >= 6*12){
filsysrespond(x->fs, x, &fc, Etooshort);
return;
}
n = sprint(buf, "%11d %11d %11d %11d nowindow nowindow ",
screen->r.min.x, screen->r.min.y, screen->r.max.x, screen->r.max.y);
t = estrdup(buf);
goto Text;
}
if(cnt < 4*12){
filsysrespond(x->fs, x, &fc, Etooshort);
return;
}
alts[Adata].c = w->wctlread;
goto Consmesg;
case Qkbd:
alts[Adata].c = w->kbdread;
goto Consmesg;
case Qcons:
alts[Adata].c = w->consread;
Consmesg:
alts[Adata].v = &crm;
alts[Adata].op = CHANRCV;
alts[Agone].c = w->gone;
alts[Agone].v = nil;
alts[Agone].op = CHANRCV;
alts[Aflush].c = x->flushc;
alts[Aflush].v = nil;
alts[Aflush].op = CHANRCV;
alts[Aend].op = CHANEND;
switch(alt(alts)){
case Adata:
break;
case Agone:
filsysrespond(x->fs, x, &fc, Edeleted);
return;
case Aflush:
filsyscancel(x);
return;
}
c1 = crm.c1;
c2 = crm.c2;
t = emalloc(cnt+UTFmax+1); /* room to unpack partial rune plus */
pair.s = t;
pair.ns = cnt;
send(c1, &pair);
recv(c2, &pair);
fc.data = pair.s;
fc.count = min(cnt, pair.ns);
filsysrespond(x->fs, x, &fc, nil);
free(t);
break;
case Qtap:
assert(totap != nil);
alts[Adata].c = totap;
alts[Adata].v = &t;
alts[Adata].op = CHANRCV;
if(w != nil){
alts[Agone].c = w->gone;
alts[Agone].v = nil;
alts[Agone].op = CHANRCV;
} else
alts[Agone].op = CHANNOP;
alts[Aflush].c = x->flushc;
alts[Aflush].v = nil;
alts[Aflush].op = CHANRCV;
alts[Aend].op = CHANEND;
switch(alt(alts)){
case Adata:
break;
case Agone:
filsysrespond(x->fs, x, &fc, Edeleted);
return;
case Aflush:
filsyscancel(x);
return;
}
fc.data = t;
/* kbdproc ensures we're only dealing with one message */
fc.count = strlen(t)+1;
filsysrespond(x->fs, x, &fc, nil);
free(t);
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:
alts[Adata].c = w->mouseread;
alts[Adata].v = &mrm;
alts[Adata].op = CHANRCV;
alts[Agone].c = w->gone;
alts[Agone].v = nil;
alts[Agone].op = CHANRCV;
alts[Aflush].c = x->flushc;
alts[Aflush].v = nil;
alts[Aflush].op = CHANRCV;
alts[Aend].op = CHANEND;
switch(alt(alts)){
case Adata:
break;
case Agone:
filsysrespond(x->fs, x, &fc, Edeleted);
return;
case Aflush:
filsyscancel(x);
return;
}
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);
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);
w->winnameread = TRUE;
goto Text;
case Qwindow:
i = w->i;
if(i == nil){
filsysrespond(x->fs, x, &fc, Enowindow);
return;
}
r = i->r;
goto caseImage;
case Qscreen:
i = screen;
r = screen->r;
caseImage:
if(off < 5*12){
n = sprint(buf, "%11s %11d %11d %11d %11d ",
chantostr(cbuf, i->chan),
r.min.x, r.min.y, r.max.x, r.max.y);
t = estrdup(buf);
goto Text;
}
off -= 5*12;
n = -1;
t = malloc(cnt);
if(t){
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;
default:
fprint(2, "unknown qid %d in read\n", qid);
snprint(buf, sizeof(buf), "unknown qid in read");
filsysrespond(x->fs, x, &fc, buf);
break;
}
}