URI:
       twind.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       twind.c (14859B)
       ---
            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 <libsec.h>
           12 #include "dat.h"
           13 #include "fns.h"
           14 
           15 int        winid;
           16 
           17 void
           18 wininit(Window *w, Window *clone, Rectangle r)
           19 {
           20         Rectangle r1, br;
           21         File *f;
           22         Reffont *rf;
           23         Rune *rp;
           24         int nc;
           25 
           26         w->tag.w = w;
           27         w->taglines = 1;
           28         w->tagexpand = TRUE;
           29         w->body.w = w;
           30         w->id = ++winid;
           31         incref(&w->ref);
           32         if(globalincref)
           33                 incref(&w->ref);
           34         w->ctlfid = ~0;
           35         w->utflastqid = -1;
           36         r1 = r;
           37 
           38         w->tagtop = r;
           39         w->tagtop.max.y = r.min.y + font->height;
           40         r1.max.y = r1.min.y + w->taglines*font->height;
           41 
           42         incref(&reffont.ref);
           43         f = fileaddtext(nil, &w->tag);
           44         textinit(&w->tag, f, r1, &reffont, tagcols);
           45         w->tag.what = Tag;
           46         /* tag is a copy of the contents, not a tracked image */
           47         if(clone){
           48                 textdelete(&w->tag, 0, w->tag.file->b.nc, TRUE);
           49                 nc = clone->tag.file->b.nc;
           50                 rp = runemalloc(nc);
           51                 bufread(&clone->tag.file->b, 0, rp, nc);
           52                 textinsert(&w->tag, 0, rp, nc, TRUE);
           53                 free(rp);
           54                 filereset(w->tag.file);
           55                 textsetselect(&w->tag, nc, nc);
           56         }
           57         r1 = r;
           58         r1.min.y += w->taglines*font->height + 1;
           59         if(r1.max.y < r1.min.y)
           60                 r1.max.y = r1.min.y;
           61         f = nil;
           62         if(clone){
           63                 f = clone->body.file;
           64                 w->body.org = clone->body.org;
           65                 w->isscratch = clone->isscratch;
           66                 rf = rfget(FALSE, FALSE, FALSE, clone->body.reffont->f->name);
           67         }else
           68                 rf = rfget(FALSE, FALSE, FALSE, nil);
           69         f = fileaddtext(f, &w->body);
           70         w->body.what = Body;
           71         textinit(&w->body, f, r1, rf, textcols);
           72         r1.min.y -= 1;
           73         r1.max.y = r1.min.y+1;
           74         draw(screen, r1, tagcols[BORD], nil, ZP);
           75         textscrdraw(&w->body);
           76         w->r = r;
           77         br.min = w->tag.scrollr.min;
           78         br.max.x = br.min.x + Dx(button->r);
           79         br.max.y = br.min.y + Dy(button->r);
           80         draw(screen, br, button, nil, button->r.min);
           81         w->filemenu = TRUE;
           82         w->maxlines = w->body.fr.maxlines;
           83         w->autoindent = globalautoindent;
           84         if(clone){
           85                 w->dirty = clone->dirty;
           86                 w->autoindent = clone->autoindent;
           87                 textsetselect(&w->body, clone->body.q0, clone->body.q1);
           88                 winsettag(w);
           89         }
           90 }
           91 
           92 /*
           93  * Draw the appropriate button.
           94  */
           95 void
           96 windrawbutton(Window *w)
           97 {
           98         Image *b;
           99         Rectangle br;
          100 
          101         b = button;
          102         if(!w->isdir && !w->isscratch && (w->body.file->mod || w->body.ncache))
          103                 b = modbutton;
          104         br.min = w->tag.scrollr.min;
          105         br.max.x = br.min.x + Dx(b->r);
          106         br.max.y = br.min.y + Dy(b->r);
          107         draw(screen, br, b, nil, b->r.min);
          108 }
          109 
          110 int
          111 delrunepos(Window *w)
          112 {
          113         Rune *r;
          114         int i;
          115 
          116         r = parsetag(w, 0, &i);
          117         free(r);
          118         i += 2;
          119         if(i >= w->tag.file->b.nc)
          120                 return -1;
          121         return i;
          122 }
          123 
          124 void
          125 movetodel(Window *w)
          126 {
          127         int n;
          128 
          129         n = delrunepos(w);
          130         if(n < 0)
          131                 return;
          132         moveto(mousectl, addpt(frptofchar(&w->tag.fr, n), Pt(4, w->tag.fr.font->height-4)));
          133 }
          134 
          135 /*
          136  * Compute number of tag lines required
          137  * to display entire tag text.
          138  */
          139 int
          140 wintaglines(Window *w, Rectangle r)
          141 {
          142         int n;
          143         Rune rune;
          144         Point p;
          145 
          146         if(!w->tagexpand && !w->showdel)
          147                 return 1;
          148         w->showdel = FALSE;
          149         w->tag.fr.noredraw = 1;
          150         textresize(&w->tag, r, TRUE);
          151         w->tag.fr.noredraw = 0;
          152         w->tagsafe = FALSE;
          153 
          154         if(!w->tagexpand) {
          155                 /* use just as many lines as needed to show the Del */
          156                 n = delrunepos(w);
          157                 if(n < 0)
          158                         return 1;
          159                 p = subpt(frptofchar(&w->tag.fr, n), w->tag.fr.r.min);
          160                 return 1 + p.y / w->tag.fr.font->height;
          161         }
          162 
          163         /* can't use more than we have */
          164         if(w->tag.fr.nlines >= w->tag.fr.maxlines)
          165                 return w->tag.fr.maxlines;
          166 
          167         /* if tag ends with \n, include empty line at end for typing */
          168         n = w->tag.fr.nlines;
          169         if(w->tag.file->b.nc > 0){
          170                 bufread(&w->tag.file->b, w->tag.file->b.nc-1, &rune, 1);
          171                 if(rune == '\n')
          172                         n++;
          173         }
          174         if(n == 0)
          175                 n = 1;
          176         return n;
          177 }
          178 
          179 int
          180 winresize(Window *w, Rectangle r, int safe, int keepextra)
          181 {
          182         int oy, y, mouseintag, mouseinbody;
          183         Point p;
          184         Rectangle r1;
          185 
          186         mouseintag = ptinrect(mouse->xy, w->tag.all);
          187         mouseinbody = ptinrect(mouse->xy, w->body.all);
          188 
          189         /* tagtop is first line of tag */
          190         w->tagtop = r;
          191         w->tagtop.max.y = r.min.y+font->height;
          192 
          193         r1 = r;
          194         r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
          195 
          196         /* If needed, recompute number of lines in tag. */
          197         if(!safe || !w->tagsafe || !eqrect(w->tag.all, r1)){
          198                 w->taglines = wintaglines(w, r);
          199                 r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
          200         }
          201 
          202         /* If needed, resize & redraw tag. */
          203         y = r1.max.y;
          204         if(!safe || !w->tagsafe || !eqrect(w->tag.all, r1)){
          205                 textresize(&w->tag, r1, TRUE);
          206                 y = w->tag.fr.r.max.y;
          207                 windrawbutton(w);
          208                 w->tagsafe = TRUE;
          209 
          210                 /* If mouse is in tag, pull up as tag closes. */
          211                 if(mouseintag && !ptinrect(mouse->xy, w->tag.all)){
          212                         p = mouse->xy;
          213                         p.y = w->tag.all.max.y-3;
          214                         moveto(mousectl, p);
          215                 }
          216 
          217                 /* If mouse is in body, push down as tag expands. */
          218                 if(mouseinbody && ptinrect(mouse->xy, w->tag.all)){
          219                         p = mouse->xy;
          220                         p.y = w->tag.all.max.y+3;
          221                         moveto(mousectl, p);
          222                 }
          223         }
          224 
          225         /* If needed, resize & redraw body. */
          226         r1 = r;
          227         r1.min.y = y;
          228         if(!safe || !eqrect(w->body.all, r1)){
          229                 oy = y;
          230                 if(y+1+w->body.fr.font->height <= r.max.y){        /* room for one line */
          231                         r1.min.y = y;
          232                         r1.max.y = y+1;
          233                         draw(screen, r1, tagcols[BORD], nil, ZP);
          234                         y++;
          235                         r1.min.y = min(y, r.max.y);
          236                         r1.max.y = r.max.y;
          237                 }else{
          238                         r1.min.y = y;
          239                         r1.max.y = y;
          240                 }
          241                 y = textresize(&w->body, r1, keepextra);
          242                 w->r = r;
          243                 w->r.max.y = y;
          244                 textscrdraw(&w->body);
          245                 w->body.all.min.y = oy;
          246         }
          247         w->maxlines = min(w->body.fr.nlines, max(w->maxlines, w->body.fr.maxlines));
          248         return w->r.max.y;
          249 }
          250 
          251 void
          252 winlock1(Window *w, int owner)
          253 {
          254         incref(&w->ref);
          255         qlock(&w->lk);
          256         w->owner = owner;
          257 }
          258 
          259 void
          260 winlock(Window *w, int owner)
          261 {
          262         int i;
          263         File *f;
          264 
          265         f = w->body.file;
          266         for(i=0; i<f->ntext; i++)
          267                 winlock1(f->text[i]->w, owner);
          268 }
          269 
          270 void
          271 winunlock(Window *w)
          272 {
          273         int i;
          274         File *f;
          275 
          276         /*
          277          * subtle: loop runs backwards to avoid tripping over
          278          * winclose indirectly editing f->text and freeing f
          279          * on the last iteration of the loop.
          280          */
          281         f = w->body.file;
          282         for(i=f->ntext-1; i>=0; i--){
          283                 w = f->text[i]->w;
          284                 w->owner = 0;
          285                 qunlock(&w->lk);
          286                 winclose(w);
          287         }
          288 }
          289 
          290 void
          291 winmousebut(Window *w)
          292 {
          293         moveto(mousectl, addpt(w->tag.scrollr.min,
          294                 divpt(Pt(Dx(w->tag.scrollr), font->height), 2)));
          295 }
          296 
          297 void
          298 windirfree(Window *w)
          299 {
          300         int i;
          301         Dirlist *dl;
          302 
          303         if(w->isdir){
          304                 for(i=0; i<w->ndl; i++){
          305                         dl = w->dlp[i];
          306                         free(dl->r);
          307                         free(dl);
          308                 }
          309                 free(w->dlp);
          310         }
          311         w->dlp = nil;
          312         w->ndl = 0;
          313 }
          314 
          315 void
          316 winclose(Window *w)
          317 {
          318         int i;
          319 
          320         if(decref(&w->ref) == 0){
          321                 xfidlog(w, "del");
          322                 windirfree(w);
          323                 textclose(&w->tag);
          324                 textclose(&w->body);
          325                 if(activewin == w)
          326                         activewin = nil;
          327                 for(i=0; i<w->nincl; i++)
          328                         free(w->incl[i]);
          329                 free(w->incl);
          330                 free(w->events);
          331                 free(w);
          332         }
          333 }
          334 
          335 void
          336 windelete(Window *w)
          337 {
          338         Xfid *x;
          339 
          340         x = w->eventx;
          341         if(x){
          342                 w->nevents = 0;
          343                 free(w->events);
          344                 w->events = nil;
          345                 w->eventx = nil;
          346                 sendp(x->c, nil);        /* wake him up */
          347         }
          348 }
          349 
          350 void
          351 winundo(Window *w, int isundo)
          352 {
          353         Text *body;
          354         int i;
          355         File *f;
          356         Window *v;
          357 
          358         w->utflastqid = -1;
          359         body = &w->body;
          360         fileundo(body->file, isundo, &body->q0, &body->q1);
          361         textshow(body, body->q0, body->q1, 1);
          362         f = body->file;
          363         for(i=0; i<f->ntext; i++){
          364                 v = f->text[i]->w;
          365                 v->dirty = (f->seq != v->putseq);
          366                 if(v != w){
          367                         v->body.q0 = v->body.fr.p0+v->body.org;
          368                         v->body.q1 = v->body.fr.p1+v->body.org;
          369                 }
          370         }
          371         winsettag(w);
          372 }
          373 
          374 void
          375 winsetname(Window *w, Rune *name, int n)
          376 {
          377         Text *t;
          378         Window *v;
          379         int i;
          380         static Rune Lslashguide[] = { '/', 'g', 'u', 'i', 'd', 'e', 0 };
          381         static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
          382 
          383         t = &w->body;
          384         if(runeeq(t->file->name, t->file->nname, name, n) == TRUE)
          385                 return;
          386         w->isscratch = FALSE;
          387         if(n>=6 && runeeq(Lslashguide, 6, name+(n-6), 6))
          388                 w->isscratch = TRUE;
          389         else if(n>=7 && runeeq(Lpluserrors, 7, name+(n-7), 7))
          390                 w->isscratch = TRUE;
          391         filesetname(t->file, name, n);
          392         for(i=0; i<t->file->ntext; i++){
          393                 v = t->file->text[i]->w;
          394                 winsettag(v);
          395                 v->isscratch = w->isscratch;
          396         }
          397 }
          398 
          399 void
          400 wintype(Window *w, Text *t, Rune r)
          401 {
          402         int i;
          403 
          404         texttype(t, r);
          405         if(t->what == Body)
          406                 for(i=0; i<t->file->ntext; i++)
          407                         textscrdraw(t->file->text[i]);
          408         winsettag(w);
          409 }
          410 
          411 void
          412 wincleartag(Window *w)
          413 {
          414         int i, n;
          415         Rune *r;
          416 
          417         /* w must be committed */
          418         n = w->tag.file->b.nc;
          419         r = parsetag(w, 0, &i);
          420         for(; i<n; i++)
          421                 if(r[i] == '|')
          422                         break;
          423         if(i == n)
          424                 return;
          425         i++;
          426         textdelete(&w->tag, i, n, TRUE);
          427         free(r);
          428         w->tag.file->mod = FALSE;
          429         if(w->tag.q0 > i)
          430                 w->tag.q0 = i;
          431         if(w->tag.q1 > i)
          432                 w->tag.q1 = i;
          433         textsetselect(&w->tag, w->tag.q0, w->tag.q1);
          434 }
          435 
          436 Rune*
          437 parsetag(Window *w, int extra, int *len)
          438 {
          439         static Rune Ldelsnarf[] = { ' ', 'D', 'e', 'l', ' ', 'S', 'n', 'a', 'r', 'f', 0 };
          440         static Rune Lspacepipe[] = { ' ', '|', 0 };
          441         static Rune Ltabpipe[] = { '\t', '|', 0 };
          442         int i;
          443         Rune *r, *p, *pipe;
          444 
          445         r = runemalloc(w->tag.file->b.nc+extra+1);
          446         bufread(&w->tag.file->b, 0, r, w->tag.file->b.nc);
          447         r[w->tag.file->b.nc] = '\0';
          448 
          449         /*
          450          * " |" or "\t|" ends left half of tag
          451          * If we find " Del Snarf" in the left half of the tag
          452          * (before the pipe), that ends the file name.
          453          */
          454         pipe = runestrstr(r, Lspacepipe);
          455         if((p = runestrstr(r, Ltabpipe)) != nil && (pipe == nil || p < pipe))
          456                 pipe = p;
          457         if((p = runestrstr(r, Ldelsnarf)) != nil && (pipe == nil || p < pipe))
          458                 i = p - r;
          459         else {
          460                 for(i=0; i<w->tag.file->b.nc; i++)
          461                         if(r[i]==' ' || r[i]=='\t')
          462                                 break;
          463         }
          464         *len = i;
          465         return r;
          466 }
          467 
          468 void
          469 winsettag1(Window *w)
          470 {
          471         int i, j, k, n, bar, dirty, resize;
          472         Rune *new, *old, *r;
          473         uint q0, q1;
          474         static Rune Ldelsnarf[] = { ' ', 'D', 'e', 'l', ' ',
          475                 'S', 'n', 'a', 'r', 'f', 0 };
          476         static Rune Lundo[] = { ' ', 'U', 'n', 'd', 'o', 0 };
          477         static Rune Lredo[] = { ' ', 'R', 'e', 'd', 'o', 0 };
          478         static Rune Lget[] = { ' ', 'G', 'e', 't', 0 };
          479         static Rune Lput[] = { ' ', 'P', 'u', 't', 0 };
          480         static Rune Llook[] = { ' ', 'L', 'o', 'o', 'k', ' ', 0 };
          481         static Rune Lpipe[] = { ' ', '|', 0 };
          482 
          483         /* there are races that get us here with stuff in the tag cache, so we take extra care to sync it */
          484         if(w->tag.ncache!=0 || w->tag.file->mod)
          485                 wincommit(w, &w->tag);        /* check file name; also guarantees we can modify tag contents */
          486         old = parsetag(w, 0, &i);
          487         if(runeeq(old, i, w->body.file->name, w->body.file->nname) == FALSE){
          488                 textdelete(&w->tag, 0, i, TRUE);
          489                 textinsert(&w->tag, 0, w->body.file->name, w->body.file->nname, TRUE);
          490                 free(old);
          491                 old = runemalloc(w->tag.file->b.nc+1);
          492                 bufread(&w->tag.file->b, 0, old, w->tag.file->b.nc);
          493                 old[w->tag.file->b.nc] = '\0';
          494         }
          495 
          496         /* compute the text for the whole tag, replacing current only if it differs */
          497         new = runemalloc(w->body.file->nname+100);
          498         i = 0;
          499         if(w->body.file->nname != 0)
          500                 runemove(new, w->body.file->name, w->body.file->nname);
          501         i += w->body.file->nname;
          502         runemove(new+i, Ldelsnarf, 10);
          503         i += 10;
          504         if(w->filemenu){
          505                 if(w->body.needundo || w->body.file->delta.nc>0 || w->body.ncache){
          506                         runemove(new+i, Lundo, 5);
          507                         i += 5;
          508                 }
          509                 if(w->body.file->epsilon.nc > 0){
          510                         runemove(new+i, Lredo, 5);
          511                         i += 5;
          512                 }
          513                 dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq);
          514                 if(!w->isdir && dirty){
          515                         runemove(new+i, Lput, 4);
          516                         i += 4;
          517                 }
          518         }
          519         if(w->isdir){
          520                 runemove(new+i, Lget, 4);
          521                 i += 4;
          522         }
          523         runemove(new+i, Lpipe, 2);
          524         i += 2;
          525         r = runestrchr(old, '|');
          526         if(r)
          527                 k = r-old+1;
          528         else{
          529                 k = w->tag.file->b.nc;
          530                 if(w->body.file->seq == 0){
          531                         runemove(new+i, Llook, 6);
          532                         i += 6;
          533                 }
          534         }
          535         new[i] = 0;
          536 
          537         /* replace tag if the new one is different */
          538         resize = 0;
          539         if(runeeq(new, i, old, k) == FALSE){
          540                 resize = 1;
          541                 n = k;
          542                 if(n > i)
          543                         n = i;
          544                 for(j=0; j<n; j++)
          545                         if(old[j] != new[j])
          546                                 break;
          547                 q0 = w->tag.q0;
          548                 q1 = w->tag.q1;
          549                 textdelete(&w->tag, j, k, TRUE);
          550                 textinsert(&w->tag, j, new+j, i-j, TRUE);
          551                 /* try to preserve user selection */
          552                 r = runestrchr(old, '|');
          553                 if(r){
          554                         bar = r-old;
          555                         if(q0 > bar){
          556                                 bar = (runestrchr(new, '|')-new)-bar;
          557                                 w->tag.q0 = q0+bar;
          558                                 w->tag.q1 = q1+bar;
          559                         }
          560                 }
          561         }
          562         free(old);
          563         free(new);
          564         w->tag.file->mod = FALSE;
          565         n = w->tag.file->b.nc+w->tag.ncache;
          566         if(w->tag.q0 > n)
          567                 w->tag.q0 = n;
          568         if(w->tag.q1 > n)
          569                 w->tag.q1 = n;
          570         textsetselect(&w->tag, w->tag.q0, w->tag.q1);
          571         windrawbutton(w);
          572         if(resize){
          573                 w->tagsafe = 0;
          574                 winresize(w, w->r, TRUE, TRUE);
          575         }
          576 }
          577 
          578 void
          579 winsettag(Window *w)
          580 {
          581         int i;
          582         File *f;
          583         Window *v;
          584 
          585         f = w->body.file;
          586         for(i=0; i<f->ntext; i++){
          587                 v = f->text[i]->w;
          588                 if(v->col->safe || v->body.fr.maxlines>0)
          589                         winsettag1(v);
          590         }
          591 }
          592 
          593 void
          594 wincommit(Window *w, Text *t)
          595 {
          596         Rune *r;
          597         int i;
          598         File *f;
          599 
          600         textcommit(t, TRUE);
          601         f = t->file;
          602         if(f->ntext > 1)
          603                 for(i=0; i<f->ntext; i++)
          604                         textcommit(f->text[i], FALSE);        /* no-op for t */
          605         if(t->what == Body)
          606                 return;
          607         r = parsetag(w, 0, &i);
          608         if(runeeq(r, i, w->body.file->name, w->body.file->nname) == FALSE){
          609                 seq++;
          610                 filemark(w->body.file);
          611                 w->body.file->mod = TRUE;
          612                 w->dirty = TRUE;
          613                 winsetname(w, r, i);
          614                 winsettag(w);
          615         }
          616         free(r);
          617 }
          618 
          619 void
          620 winaddincl(Window *w, Rune *r, int n)
          621 {
          622         char *a;
          623         Dir *d;
          624         Runestr rs;
          625 
          626         a = runetobyte(r, n);
          627         d = dirstat(a);
          628         if(d == nil){
          629                 if(a[0] == '/')
          630                         goto Rescue;
          631                 rs = dirname(&w->body, r, n);
          632                 r = rs.r;
          633                 n = rs.nr;
          634                 free(a);
          635                 a = runetobyte(r, n);
          636                 d = dirstat(a);
          637                 if(d == nil)
          638                         goto Rescue;
          639                 r = runerealloc(r, n+1);
          640                 r[n] = 0;
          641         }
          642         free(a);
          643         if((d->qid.type&QTDIR) == 0){
          644                 free(d);
          645                 warning(nil, "%s: not a directory\n", a);
          646                 free(r);
          647                 return;
          648         }
          649         free(d);
          650         w->nincl++;
          651         w->incl = realloc(w->incl, w->nincl*sizeof(Rune*));
          652         memmove(w->incl+1, w->incl, (w->nincl-1)*sizeof(Rune*));
          653         w->incl[0] = runemalloc(n+1);
          654         runemove(w->incl[0], r, n);
          655         free(r);
          656         return;
          657 
          658 Rescue:
          659         warning(nil, "%s: %r\n", a);
          660         free(r);
          661         free(a);
          662         return;
          663 }
          664 
          665 int
          666 winclean(Window *w, int conservative)
          667 {
          668         if(w->isscratch || w->isdir)        /* don't whine if it's a guide file, error window, etc. */
          669                 return TRUE;
          670         if(!conservative && w->nopen[QWevent]>0)
          671                 return TRUE;
          672         if(w->dirty){
          673                 if(w->body.file->nname)
          674                         warning(nil, "%.*S modified\n", w->body.file->nname, w->body.file->name);
          675                 else{
          676                         if(w->body.file->b.nc < 100)        /* don't whine if it's too small */
          677                                 return TRUE;
          678                         warning(nil, "unnamed file modified\n");
          679                 }
          680                 w->dirty = FALSE;
          681                 return FALSE;
          682         }
          683         return TRUE;
          684 }
          685 
          686 char*
          687 winctlprint(Window *w, char *buf, int fonts)
          688 {
          689         sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->b.nc,
          690                 w->body.file->b.nc, w->isdir, w->dirty);
          691         if(fonts)
          692                 return smprint("%s%11d %q %11d %11d %11d ", buf, Dx(w->body.fr.r),
          693                         w->body.reffont->f->name, w->body.fr.maxtab, seqof(w, 1) != 0, seqof(w, 0) != 0);
          694         return buf;
          695 }
          696 
          697 void
          698 winevent(Window *w, char *fmt, ...)
          699 {
          700         int n;
          701         char *b;
          702         Xfid *x;
          703         va_list arg;
          704 
          705         if(w->nopen[QWevent] == 0)
          706                 return;
          707         if(w->owner == 0)
          708                 error("no window owner");
          709         va_start(arg, fmt);
          710         b = vsmprint(fmt, arg);
          711         va_end(arg);
          712         if(b == nil)
          713                 error("vsmprint failed");
          714         n = strlen(b);
          715         w->events = erealloc(w->events, w->nevents+1+n);
          716         w->events[w->nevents++] = w->owner;
          717         memmove(w->events+w->nevents, b, n);
          718         free(b);
          719         w->nevents += n;
          720         x = w->eventx;
          721         if(x){
          722                 w->eventx = nil;
          723                 sendp(x->c, nil);
          724         }
          725 }