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