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