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