]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rio/xfid.c
rio: cleanup and error handling
[plan9front.git] / sys / src / cmd / rio / xfid.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13
14 char Einuse[] =         "file in use";
15 char Edeleted[] =       "window deleted";
16 char Etooshort[] =      "buffer too small";
17 char Eshort[] =         "short i/o request";
18 char Elong[] =          "snarf buffer too long";
19 char Eunkid[] =         "unknown id in attach";
20 char Ebadrect[] =       "bad rectangle in attach";
21 char Ewindow[] =        "cannot make window";
22 char Enowindow[] =      "window has no image";
23 char Ebadmouse[] =      "bad format on /dev/mouse";
24
25 extern char Eperm[];
26 extern char Enomem[];
27
28 static  Xfid    *xfidfree;
29 static  Xfid    *xfid;
30 static  Channel *cxfidalloc;    /* chan(Xfid*) */
31 static  Channel *cxfidfree;     /* chan(Xfid*) */
32
33 static  char    *tsnarf;
34 static  int     ntsnarf;
35
36 void
37 xfidallocthread(void*)
38 {
39         Xfid *x;
40         enum { Alloc, Free, N };
41         static Alt alts[N+1];
42
43         alts[Alloc].c = cxfidalloc;
44         alts[Alloc].v = nil;
45         alts[Alloc].op = CHANRCV;
46         alts[Free].c = cxfidfree;
47         alts[Free].v = &x;
48         alts[Free].op = CHANRCV;
49         alts[N].op = CHANEND;
50         for(;;){
51                 switch(alt(alts)){
52                 case Alloc:
53                         x = xfidfree;
54                         if(x)
55                                 xfidfree = x->free;
56                         else{
57                                 x = emalloc(sizeof(Xfid));
58                                 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
59                                 x->flushc = chancreate(sizeof(int), 0); /* notification only; no data */
60                                 x->flushtag = -1;
61                                 x->next = xfid;
62                                 xfid = x;
63                                 threadcreate(xfidctl, x, 16384);
64                         }
65                         if(x->ref != 0){
66                                 fprint(2, "%p incref %ld\n", x, x->ref);
67                                 error("incref");
68                         }
69                         if(x->flushtag != -1)
70                                 error("flushtag in allocate");
71                         incref(x);
72                         sendp(cxfidalloc, x);
73                         break;
74                 case Free:
75                         if(x->ref != 0){
76                                 fprint(2, "%p decref %ld\n", x, x->ref);
77                                 error("decref");
78                         }
79                         if(x->flushtag != -1)
80                                 error("flushtag in free");
81                         x->free = xfidfree;
82                         xfidfree = x;
83                         break;
84                 }
85         }
86 }
87
88 Channel*
89 xfidinit(void)
90 {
91         cxfidalloc = chancreate(sizeof(Xfid*), 0);
92         cxfidfree = chancreate(sizeof(Xfid*), 0);
93         threadcreate(xfidallocthread, nil, STACK);
94         return cxfidalloc;
95 }
96
97 void
98 xfidctl(void *arg)
99 {
100         Xfid *x;
101         void (*f)(Xfid*);
102         char buf[64];
103
104         x = arg;
105         snprint(buf, sizeof buf, "xfid.%p", x);
106         threadsetname(buf);
107         for(;;){
108                 f = recvp(x->c);
109                 if(f){
110                         x->flushtag = x->tag;
111                         (*f)(x);
112                 }
113                 if(decref(x) == 0)
114                         sendp(cxfidfree, x);
115         }
116 }
117
118 void
119 xfidflush(Xfid *x)
120 {
121         Fcall t;
122         Xfid *xf;
123
124         for(xf=xfid; xf; xf=xf->next)
125                 if(xf->flushtag == x->oldtag){
126                         incref(xf);     /* to hold data structures up at tail of synchronization */
127                         if(xf->ref == 1)
128                                 error("ref 1 in flush");
129                         xf->flushtag = -1;
130                         break;
131                 }
132
133         /* take over flushtag so follow up flushes wait for us */
134         x->flushtag = x->oldtag;
135
136         /*
137          * wakeup filsysflush() in the filsysproc so the next
138          * flush can come in.
139          */
140         sendul(x->fs->csyncflush, 0);
141
142         if(xf){
143                 enum { Done, Flush, End };
144                 Alt alts[End+1];
145                 void *f;
146                 int z;
147
148                 z = 0;
149                 f = nil;
150
151                 alts[Done].c = xf->c;
152                 alts[Done].v = &f;
153                 alts[Done].op = CHANSND;
154                 alts[Flush].c = xf->flushc;
155                 alts[Flush].v = &z;
156                 alts[Flush].op = CHANSND;
157                 alts[End].op = CHANEND;
158
159                 while(alt(alts) != Done)
160                         ;
161         }
162         if(nbrecv(x->flushc, nil)){
163                 filsyscancel(x);
164                 return;
165         }
166         filsysrespond(x->fs, x, &t, nil);
167 }
168
169 void
170 xfidattach(Xfid *x)
171 {
172         Fcall t;
173         int id, hideit, scrollit;
174         Window *w;
175         char *err, *n, *dir, errbuf[ERRMAX];
176         int pid, newlymade;
177         Rectangle r;
178         Image *i;
179
180         t.qid = x->f->qid;
181         qlock(&all);
182         w = nil;
183         err = Eunkid;
184         newlymade = FALSE;
185         hideit = 0;
186         scrollit = scrolling;
187
188         if(x->aname[0] == 'N'){ /* N 100,100, 200, 200 - old syntax */
189                 n = x->aname+1;
190                 pid = strtoul(n, &n, 0);
191                 if(*n == ',')
192                         n++;
193                 r.min.x = strtoul(n, &n, 0);
194                 if(*n == ',')
195                         n++;
196                 r.min.y = strtoul(n, &n, 0);
197                 if(*n == ',')
198                         n++;
199                 r.max.x = strtoul(n, &n, 0);
200                 if(*n == ',')
201                         n++;
202                 r.max.y = strtoul(n, &n, 0);
203   Allocate:
204                 if(!goodrect(r))
205                         err = Ebadrect;
206                 else{
207                         if(hideit)
208                                 i = allocimage(display, r, screen->chan, 0, DNofill);
209                         else
210                                 i = allocwindow(wscreen, r, Refbackup, DNofill);
211                         if(i){
212                                 if(pid == 0)
213                                         pid = -1;       /* make sure we don't pop a shell! - UGH */
214                                 w = new(i, hideit, scrollit, pid, nil, nil, nil);
215                                 newlymade = TRUE;
216                         }else
217                                 err = Ewindow;
218                 }
219         }else if(strncmp(x->aname, "new", 3) == 0){     /* new -dx -dy - new syntax, as in wctl */
220                 pid = 0;
221                 if(parsewctl(nil, ZR, &r, &pid, nil, &hideit, &scrollit, &dir, x->aname, errbuf) < 0)
222                         err = errbuf;
223                 else
224                         goto Allocate;
225         }else{
226                 id = atoi(x->aname);
227                 w = wlookid(id);
228         }
229         x->f->w = w;
230         if(w == nil){
231                 qunlock(&all);
232                 x->f->busy = FALSE;
233                 filsysrespond(x->fs, x, &t, err);
234                 return;
235         }
236         if(!newlymade)  /* counteract dec() in winshell() */
237                 incref(w);
238         qunlock(&all);
239         filsysrespond(x->fs, x, &t, nil);
240 }
241
242 void
243 xfidopen(Xfid *x)
244 {
245         Fcall t;
246         Window *w;
247
248         w = x->f->w;
249         if(w->deleted){
250                 filsysrespond(x->fs, x, &t, Edeleted);
251                 return;
252         }
253         switch(FILE(x->f->qid)){
254         case Qconsctl:
255                 if(w->ctlopen){
256                         filsysrespond(x->fs, x, &t, Einuse);
257                         return;
258                 }
259                 w->ctlopen = TRUE;
260                 break;
261         case Qkbdin:
262                 if(w != wkeyboard){
263                         filsysrespond(x->fs, x, &t, Eperm);
264                         return;
265                 }
266                 break;
267         case Qkbd:
268                 if(w->kbdopen){
269                         filsysrespond(x->fs, x, &t, Einuse);
270                         return;
271                 }
272                 w->kbdopen = TRUE;
273                 break;
274         case Qmouse:
275                 if(w->mouseopen){
276                         filsysrespond(x->fs, x, &t, Einuse);
277                         return;
278                 }
279                 /*
280                  * Reshaped: there's a race if the appl. opens the
281                  * window, is resized, and then opens the mouse,
282                  * but that's rare.  The alternative is to generate
283                  * a resized event every time a new program starts
284                  * up in a window that has been resized since the
285                  * dawn of time.  We choose the lesser evil.
286                  */
287                 w->resized = FALSE;
288                 w->mouseopen = TRUE;
289                 break;
290         case Qsnarf:
291                 if(x->mode==ORDWR || x->mode==OWRITE)
292                         ntsnarf = 0;
293                 break;
294         case Qwctl:
295                 if(x->mode==OREAD || x->mode==ORDWR){
296                         /*
297                          * It would be much nicer to implement fan-out for wctl reads,
298                          * so multiple people can see the resizings, but rio just isn't
299                          * structured for that.  It's structured for /dev/cons, which gives
300                          * alternate data to alternate readers.  So to keep things sane for
301                          * wctl, we compromise and give an error if two people try to
302                          * open it.  Apologies.
303                          */
304                         if(w->wctlopen){
305                                 filsysrespond(x->fs, x, &t, Einuse);
306                                 return;
307                         }
308                         w->wctlopen = TRUE;
309                         w->wctlready = 1;
310                         wsendctlmesg(w, Wakeup, ZR, nil);
311                 }
312                 break;
313         }
314         t.qid = x->f->qid;
315         t.iounit = messagesize-IOHDRSZ;
316         x->f->open = TRUE;
317         x->f->mode = x->mode;
318         filsysrespond(x->fs, x, &t, nil);
319 }
320
321 void
322 xfidclose(Xfid *x)
323 {
324         Fcall t;
325         Window *w;
326         int nb, nulls;
327
328         w = x->f->w;
329         switch(FILE(x->f->qid)){
330         case Qconsctl:
331                 if(w->rawing){
332                         w->rawing = FALSE;
333                         wsendctlmesg(w, Rawoff, ZR, nil);
334                 }
335                 if(w->holding){
336                         w->holding = FALSE;
337                         wsendctlmesg(w, Holdoff, ZR, nil);
338                 }
339                 w->ctlopen = FALSE;
340                 break;
341         case Qcursor:
342                 w->cursorp = nil;
343                 wsetcursor(w, FALSE);
344                 break;
345         case Qkbd:
346                 w->kbdopen = FALSE;
347                 break;
348         case Qmouse:
349                 w->resized = FALSE;
350                 w->mouseopen = FALSE;
351                 if(w->i != nil)
352                         wsendctlmesg(w, Refresh, w->i->r, nil);
353                 break;
354         /* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */
355         case Qsnarf:
356                 if(x->f->mode==ORDWR || x->f->mode==OWRITE){
357                         snarf = runerealloc(snarf, ntsnarf+1);
358                         cvttorunes(tsnarf, ntsnarf, snarf, &nb, &nsnarf, &nulls);
359                         ntsnarf = 0;
360                 }
361                 break;
362         case Qwctl:
363                 if(x->f->mode==OREAD || x->f->mode==ORDWR)
364                         w->wctlopen = FALSE;
365                 break;
366         }
367         wclose(w);
368         filsysrespond(x->fs, x, &t, nil);
369 }
370
371 void
372 xfidwrite(Xfid *x)
373 {
374         Fcall fc;
375         int cnt, qid, nb, off, nr;
376         char err[ERRMAX], *p;
377         Point pt;
378         Window *w;
379         Rune *r;
380         Conswritemesg cwm;
381         Stringpair pair;
382         enum { CWdata, CWgone, CWflush, NCW };
383         Alt alts[NCW+1];
384
385         w = x->f->w;
386         if(w->deleted){
387                 filsysrespond(x->fs, x, &fc, Edeleted);
388                 return;
389         }
390         qid = FILE(x->f->qid);
391         cnt = x->count;
392         off = x->offset;
393         x->data[cnt] = 0;
394         switch(qid){
395         case Qcons:
396                 alts[CWdata].c = w->conswrite;
397                 alts[CWdata].v = &cwm;
398                 alts[CWdata].op = CHANRCV;
399                 alts[CWgone].c = w->gone;
400                 alts[CWgone].v = nil;
401                 alts[CWgone].op = CHANRCV;
402                 alts[CWflush].c = x->flushc;
403                 alts[CWflush].v = nil;
404                 alts[CWflush].op = CHANRCV;
405                 alts[NCW].op = CHANEND;
406         
407                 switch(alt(alts)){
408                 case CWdata:
409                         break;
410                 case CWgone:
411                         filsysrespond(x->fs, x, &fc, Edeleted);
412                         return;
413                 case CWflush:
414                         filsyscancel(x);
415                         return;
416                 }
417
418                 nr = x->f->nrpart;
419                 if(nr > 0){
420                         memmove(x->data+nr, x->data, cnt);      /* there's room: see malloc in filsysproc */
421                         memmove(x->data, x->f->rpart, nr);
422                         cnt += nr;
423                 }
424                 r = runemalloc(cnt);
425                 if(r == nil){
426                         pair.ns = 0;
427                         send(cwm.cw, &pair);
428                         filsysrespond(x->fs, x, &fc, Enomem);
429                         return;
430                 }
431                 x->f->nrpart = 0;
432                 cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil);
433                 /* approach end of buffer */
434                 while(fullrune(x->data+nb, cnt-nb)){
435                         nb += chartorune(&r[nr], x->data+nb);
436                         if(r[nr])
437                                 nr++;
438                 }
439                 if(nb < cnt){
440                         memmove(x->f->rpart, x->data+nb, cnt-nb);
441                         x->f->nrpart = cnt-nb;
442                 }
443
444                 pair.s = r;
445                 pair.ns = nr;
446                 send(cwm.cw, &pair);
447                 fc.count = x->count;
448                 filsysrespond(x->fs, x, &fc, nil);
449                 return;
450
451         case Qconsctl:
452                 if(strncmp(x->data, "holdon", 6)==0){
453                         if(w->holding++ == 0)
454                                 wsendctlmesg(w, Holdon, ZR, nil);
455                         break;
456                 }
457                 if(strncmp(x->data, "holdoff", 7)==0 && w->holding){
458                         if(--w->holding == 0)
459                                 wsendctlmesg(w, Holdoff, ZR, nil);
460                         break;
461                 }
462                 if(strncmp(x->data, "rawon", 5)==0){
463                         if(w->holding){
464                                 w->holding = 0;
465                                 wsendctlmesg(w, Holdoff, ZR, nil);
466                         }
467                         if(w->rawing++ == 0)
468                                 wsendctlmesg(w, Rawon, ZR, nil);
469                         break;
470                 }
471                 if(strncmp(x->data, "rawoff", 6)==0 && w->rawing){
472                         if(--w->rawing == 0)
473                                 wsendctlmesg(w, Rawoff, ZR, nil);
474                         break;
475                 }
476                 filsysrespond(x->fs, x, &fc, "unknown control message");
477                 return;
478
479         case Qcursor:
480                 if(cnt < 2*4+2*2*16)
481                         w->cursorp = nil;
482                 else{
483                         w->cursor.offset.x = BGLONG(x->data+0*4);
484                         w->cursor.offset.y = BGLONG(x->data+1*4);
485                         memmove(w->cursor.clr, x->data+2*4, 2*2*16);
486                         w->cursorp = &w->cursor;
487                 }
488                 wsetcursor(w, !sweeping);
489                 break;
490
491         case Qlabel:
492                 if(off != 0){
493                         filsysrespond(x->fs, x, &fc, "non-zero offset writing label");
494                         return;
495                 }
496                 p = realloc(w->label, cnt+1);
497                 if(p == nil){
498                         filsysrespond(x->fs, x, &fc, Enomem);
499                         return;
500                 }
501                 w->label = p;
502                 w->label[cnt] = 0;
503                 memmove(w->label, x->data, cnt);
504                 break;
505
506         case Qmouse:
507                 if(w!=input || Dx(w->screenr)<=0)
508                         break;
509                 if(x->data[0] != 'm'){
510                         filsysrespond(x->fs, x, &fc, Ebadmouse);
511                         return;
512                 }
513                 p = nil;
514                 pt.x = strtoul(x->data+1, &p, 0);
515                 if(p == nil){
516                         filsysrespond(x->fs, x, &fc, Eshort);
517                         return;
518                 }
519                 pt.y = strtoul(p, nil, 0);
520                 if(w==input && wpointto(mouse->xy)==w)
521                         wsendctlmesg(w, Movemouse, Rpt(pt, pt), nil);
522                 break;
523
524         case Qsnarf:
525                 if(cnt == 0)
526                         break;
527                 /* always append only */
528                 if(ntsnarf > MAXSNARF){ /* avoid thrashing when people cut huge text */
529                         filsysrespond(x->fs, x, &fc, Elong);
530                         return;
531                 }
532                 p = realloc(tsnarf, ntsnarf+cnt+1);     /* room for NUL */
533                 if(p == nil){
534                         filsysrespond(x->fs, x, &fc, Enomem);
535                         return;
536                 }
537                 tsnarf = p;
538                 memmove(tsnarf+ntsnarf, x->data, cnt);
539                 ntsnarf += cnt;
540                 snarfversion++;
541                 break;
542
543         case Qwdir:
544                 if(cnt == 0)
545                         break;
546                 if(x->data[cnt-1] == '\n'){
547                         if(cnt == 1)
548                                 break;
549                         x->data[cnt-1] = '\0';
550                 }
551                 /* assume data comes in a single write */
552                 if(x->data[0] == '/'){
553                         p = smprint("%.*s", cnt, x->data);
554                 }else{
555                         p = smprint("%s/%.*s", w->dir, cnt, x->data);
556                 }
557                 if(p == nil){
558                         filsysrespond(x->fs, x, &fc, Enomem);
559                         return;
560                 }
561                 free(w->dir);
562                 w->dir = cleanname(p);
563                 break;
564
565         case Qkbdin:
566                 keyboardsend(x->data, cnt);
567                 break;
568
569         case Qwctl:
570                 if(writewctl(x, err) < 0){
571                         filsysrespond(x->fs, x, &fc, err);
572                         return;
573                 }
574                 break;
575
576         default:
577                 fprint(2, "unknown qid %d in write\n", qid);
578                 filsysrespond(x->fs, x, &fc, "unknown qid in write");
579                 return;
580         }
581         fc.count = cnt;
582         filsysrespond(x->fs, x, &fc, nil);
583 }
584
585 int
586 readwindow(Image *i, char *t, Rectangle r, int offset, int n)
587 {
588         int ww, oo, y, m;
589         uchar *tt;
590
591         ww = bytesperline(r, i->depth);
592         r.min.y += offset/ww;
593         if(r.min.y >= r.max.y)
594                 return 0;
595         y = r.min.y + (n + ww-1)/ww;
596         if(y < r.max.y)
597                 r.max.y = y;
598         m = ww * Dy(r);
599         oo = offset % ww;
600         if(oo == 0 && n >= m)
601                 return unloadimage(i, r, (uchar*)t, n);
602         if((tt = malloc(m)) == nil)
603                 return -1;
604         m = unloadimage(i, r, tt, m) - oo;
605         if(m > 0){
606                 if(n < m) m = n;
607                 memmove(t, tt + oo, m);
608         }
609         free(tt);
610         return m;
611 }
612
613 void
614 xfidread(Xfid *x)
615 {
616         Fcall fc;
617         int n, off, cnt, c;
618         uint qid;
619         char buf[128], *t;
620         char cbuf[30];
621         Window *w;
622         Mouse ms;
623         Rectangle r;
624         Image *i;
625         Channel *c1, *c2;       /* chan (tuple(char*, int)) */
626         Consreadmesg crm;
627         Mousereadmesg mrm;
628         Consreadmesg cwrm;
629         Kbdreadmesg krm;
630         Stringpair pair;
631         enum { CRdata, CRgone, CRflush, NCR };
632         enum { MRdata, MRgone, MRflush, NMR };
633         enum { WCRdata, WCRgone, WCRflush, NWCR };
634         Alt alts[NCR+1];
635
636         w = x->f->w;
637         if(w->deleted){
638                 filsysrespond(x->fs, x, &fc, Edeleted);
639                 return;
640         }
641         qid = FILE(x->f->qid);
642         off = x->offset;
643         cnt = x->count;
644         switch(qid){
645         case Qcons:
646                 alts[CRdata].c = w->consread;
647                 alts[CRdata].v = &crm;
648                 alts[CRdata].op = CHANRCV;
649                 alts[CRgone].c = w->gone;
650                 alts[CRgone].v = nil;
651                 alts[CRgone].op = CHANRCV;
652                 alts[CRflush].c = x->flushc;
653                 alts[CRflush].v = nil;
654                 alts[CRflush].op = CHANRCV;
655                 alts[NCR].op = CHANEND;
656
657                 switch(alt(alts)){
658                 case CRdata:
659                         break;
660                 case CRgone:
661                         filsysrespond(x->fs, x, &fc, Edeleted);
662                         return;
663                 case CRflush:
664                         filsyscancel(x);
665                         return;
666                 }
667
668                 c1 = crm.c1;
669                 c2 = crm.c2;
670                 t = malloc(cnt+UTFmax+1);       /* room to unpack partial rune plus */
671                 pair.s = t;
672                 pair.ns = cnt;
673                 send(c1, &pair);
674                 recv(c2, &pair);
675                 fc.data = pair.s;
676                 fc.count = pair.ns;
677                 filsysrespond(x->fs, x, &fc, nil);
678                 free(t);
679                 break;
680
681         case Qlabel:
682                 n = strlen(w->label);
683                 if(off > n)
684                         off = n;
685                 if(off+cnt > n)
686                         cnt = n-off;
687                 fc.data = w->label+off;
688                 fc.count = cnt;
689                 filsysrespond(x->fs, x, &fc, nil);
690                 break;
691
692         case Qmouse:
693                 alts[MRdata].c = w->mouseread;
694                 alts[MRdata].v = &mrm;
695                 alts[MRdata].op = CHANRCV;
696                 alts[MRgone].c = w->gone;
697                 alts[MRgone].v = nil;
698                 alts[MRgone].op = CHANRCV;
699                 alts[MRflush].c = x->flushc;
700                 alts[MRflush].v = nil;
701                 alts[MRflush].op = CHANRCV;
702                 alts[NMR].op = CHANEND;
703
704                 switch(alt(alts)){
705                 case MRdata:
706                         break;
707                 case MRgone:
708                         filsysrespond(x->fs, x, &fc, Edeleted);
709                         return;
710                 case MRflush:
711                         filsyscancel(x);
712                         return;
713                 }
714
715                 recv(mrm.cm, &ms);
716                 c = 'm';
717                 if(w->resized)
718                         c = 'r';
719                 n = sprint(buf, "%c%11d %11d %11d %11ld ", c, ms.xy.x, ms.xy.y, ms.buttons, ms.msec);
720                 w->resized = 0;
721                 fc.data = buf;
722                 fc.count = min(n, cnt);
723                 filsysrespond(x->fs, x, &fc, nil);
724                 break;
725
726         case Qkbd:
727                 alts[MRdata].c = w->kbdread;
728                 alts[MRdata].v = &krm;
729                 alts[MRdata].op = CHANRCV;
730                 alts[MRgone].c = w->gone;
731                 alts[MRgone].v = nil;
732                 alts[MRgone].op = CHANRCV;
733                 alts[MRflush].c = x->flushc;
734                 alts[MRflush].v = nil;
735                 alts[MRflush].op = CHANRCV;
736                 alts[NMR].op = CHANEND;
737
738                 switch(alt(alts)){
739                 case MRdata:
740                         break;
741                 case MRgone:
742                         filsysrespond(x->fs, x, &fc, Edeleted);
743                         return;
744                 case MRflush:
745                         filsyscancel(x);
746                         return;
747                 }
748
749                 t = recvp(krm.ck);
750                 fc.data = t;
751                 fc.count = strlen(t)+1;
752                 filsysrespond(x->fs, x, &fc, nil);
753                 free(t);
754                 break;
755
756         case Qcursor:
757                 filsysrespond(x->fs, x, &fc, "cursor read not implemented");
758                 break;
759
760         /* The algorithm for snarf and text is expensive but easy and rarely used */
761         case Qsnarf:
762                 getsnarf();
763                 if(nsnarf)
764                         t = runetobyte(snarf, nsnarf, &n);
765                 else {
766                         t = nil;
767                         n = 0;
768                 }
769                 goto Text;
770
771         case Qtext:
772                 t = wcontents(w, &n);
773                 goto Text;
774
775         Text:
776                 if(off > n){
777                         off = n;
778                         cnt = 0;
779                 }
780                 if(off+cnt > n)
781                         cnt = n-off;
782                 fc.data = t+off;
783                 fc.count = cnt;
784                 filsysrespond(x->fs, x, &fc, nil);
785                 free(t);
786                 break;
787
788         case Qwdir:
789                 t = estrdup(w->dir);
790                 n = strlen(t);
791                 goto Text;
792
793         case Qwinid:
794                 n = sprint(buf, "%11d ", w->id);
795                 t = estrdup(buf);
796                 goto Text;
797
798
799         case Qwinname:
800                 n = strlen(w->name);
801                 if(n == 0){
802                         filsysrespond(x->fs, x, &fc, "window has no name");
803                         break;
804                 }
805                 t = estrdup(w->name);
806                 goto Text;
807
808         case Qwindow:
809                 i = w->i;
810                 r = w->screenr;
811                 if(i == nil || Dx(r)<=0){
812                         filsysrespond(x->fs, x, &fc, Enowindow);
813                         return;
814                 }
815                 goto caseImage;
816
817         case Qscreen:
818                 i = screen;
819                 r = screen->r;
820
821         caseImage:
822                 if(off < 5*12){
823                         n = sprint(buf, "%11s %11d %11d %11d %11d ",
824                                 chantostr(cbuf, i->chan),
825                                 r.min.x, r.min.y, r.max.x, r.max.y);
826                         t = estrdup(buf);
827                         goto Text;
828                 }
829                 off -= 5*12;
830                 n = -1;
831                 t = malloc(cnt);
832                 if(t){
833                         fc.data = t;
834                         n = readwindow(i, t, r, off, cnt);      /* careful; fc.count is unsigned */
835                 }
836                 if(n < 0){
837                         buf[0] = 0;
838                         errstr(buf, sizeof buf);
839                         filsysrespond(x->fs, x, &fc, buf);
840                 }else{
841                         fc.count = n;
842                         filsysrespond(x->fs, x, &fc, nil);
843                 }
844                 free(t);
845                 return;
846
847         case Qwctl:     /* read returns rectangle, hangs if not resized */
848                 if(cnt < 4*12){
849                         filsysrespond(x->fs, x, &fc, Etooshort);
850                         break;
851                 }
852
853                 alts[WCRdata].c = w->wctlread;
854                 alts[WCRdata].v = &cwrm;
855                 alts[WCRdata].op = CHANRCV;
856                 alts[WCRgone].c = w->gone;
857                 alts[WCRgone].v = nil;
858                 alts[WCRgone].op = CHANRCV;
859                 alts[WCRflush].c = x->flushc;
860                 alts[WCRflush].v = nil;
861                 alts[WCRflush].op = CHANRCV;
862                 alts[NWCR].op = CHANEND;
863
864                 switch(alt(alts)){
865                 case WCRdata:
866                         break;
867                 case WCRgone:
868                         filsysrespond(x->fs, x, &fc, Edeleted);
869                         return;
870                 case WCRflush:
871                         filsyscancel(x);
872                         return;
873                 }
874
875                 c1 = cwrm.c1;
876                 c2 = cwrm.c2;
877                 t = malloc(cnt+1);      /* be sure to have room for NUL */
878                 pair.s = t;
879                 pair.ns = cnt+1;
880                 send(c1, &pair);
881                 recv(c2, &pair);
882                 fc.data = pair.s;
883                 if(pair.ns > cnt)
884                         pair.ns = cnt;
885                 fc.count = pair.ns;
886                 filsysrespond(x->fs, x, &fc, nil);
887                 free(t);
888                 break;
889
890         default:
891                 fprint(2, "unknown qid %d in read\n", qid);
892                 snprint(buf, sizeof(buf), "unknown qid in read");
893                 filsysrespond(x->fs, x, &fc, buf);
894                 break;
895         }
896 }