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