<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/cinap_lenrek/rio.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/cinap_lenrek/rio.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
#include &lt;u.h&gt;
#include &lt;libc.h&gt;
#include &lt;draw.h&gt;
#include &lt;thread.h&gt;
#include &lt;cursor.h&gt;
#include &lt;mouse.h&gt;
#include &lt;keyboard.h&gt;
#include &lt;frame.h&gt;
#include &lt;fcall.h&gt;
#include &lt;plumb.h&gt;
#include "dat.h"
#include "fns.h"

/*
*  WASHINGTON (AP) - The Food and Drug Administration warned
* consumers Wednesday not to use ``Rio'' hair relaxer products
* because they may cause severe hair loss or turn hair green....
*    The FDA urged consumers who have experienced problems with Rio
* to notify their local FDA office, local health department or the
* company at 1‑800‑543‑3002.
*/

void            resize(void);
void            move(void);
void            delete(void);
void            hide(void);
void            unhide(int);
void            newtile(int);
Image   *sweep(void);
Image   *bandsize(Window*);
Image*  drag(Window*, Rectangle*);
void            refresh(Rectangle);
void            resized(void);
Channel *exitchan;      /* chan(int) */
Channel *winclosechan; /* chan(Window*); */
Rectangle       viewr;
int             threadrforkflag = 0;    /* should be RFENVG but that hides rio from plumber */

void    mousethread(void*);
void    keyboardthread(void*);
void winclosethread(void*);
void deletethread(void*);
void    initcmd(void*);

char            *fontname, *backname;
int             mainpid;

enum
{
       New,
       Reshape,
       Move,
       Delete,
       Hide,
       Exit,
};

enum
{
       Cut,
       Paste,
       Snarf,
       Plumb,
       Send,
       Scroll,
};

char            *menu2str[] = {
[Cut]          "cut",
[Paste]                "paste",
[Snarf]                "snarf",
[Plumb]                "plumb",
[Send]         "send",
[Scroll]               "scroll",
                       nil
};

Menu menu2 =
{
       menu2str
};

int     Hidden = Exit+1;

char            *menu3str[100] = {
[New]          "New",
[Reshape]      "Resize",
[Move]         "Move",
[Delete]               "Delete",
[Hide]         "Hide",
[Exit]         "Exit",
                       nil
};

Menu menu3 =
{
       menu3str
};

char *rcargv[] = { "rc", "-i", nil };
char *kbdargv[] = { "rc", "-c", nil, nil };

int errorshouldabort = 0;

void
derror(Display*, char *errorstr)
{
       error(errorstr);
}

void
usage(void)
{
       fprint(2, "usage: rio [-b background] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
       exits("usage");
}

void
threadmain(int argc, char *argv[])
{
       char *initstr, *kbdin, *s;
       static void *arg[1];
       char buf[256];
       Image *i;
       Rectangle r;

       if(strstr(argv[0], ".out") == nil){
               menu3str[Exit] = nil;
               Hidden--;
       }
       initstr = nil;
       kbdin = nil;
       maxtab = 0;
       ARGBEGIN{
       case 'b':
               backname = ARGF();
               if(backname == nil)
                       usage();
               break;
       case 'f':
               fontname = ARGF();
               if(fontname == nil)
                       usage();
               break;
       case 'i':
               initstr = ARGF();
               if(initstr == nil)
                       usage();
               break;
       case 'k':
               if(kbdin != nil)
                       usage();
               kbdin = ARGF();
               if(kbdin == nil)
                       usage();
               break;
       case 's':
               scrolling = TRUE;
               break;
       }ARGEND

       mainpid = getpid();
       if(getwd(buf, sizeof buf) == nil)
               startdir = estrdup(".");
       else
               startdir = estrdup(buf);
       if(fontname == nil)
               fontname = getenv("font");
       if(fontname == nil)
               fontname = "/lib/font/bit/lucm/unicode.9.font";
       s = getenv("tabstop");
       if(s != nil)
               maxtab = strtol(s, nil, 0);
       if(maxtab == 0)
               maxtab = 4;
       free(s);
       /* check font before barging ahead */
       if(access(fontname, 0) &lt; 0){
               fprint(2, "rio: can't access %s: %r\n", fontname);
               exits("font open");
       }
       putenv("font", fontname);

       snarffd = open("/dev/snarf", OREAD|OCEXEC);

       if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) &lt; 0){
               fprint(2, "rio: can't open display: %r\n");
               exits("display open");
       }
       iconinit(backname);
       view = screen;
       viewr = view-&gt;r;
       mousectl = initmouse(nil, screen);
       if(mousectl == nil)
               error("can't find mouse");
       mouse = mousectl;
       keyboardctl = initkeyboard(nil);
       if(keyboardctl == nil)
               error("can't find keyboard");
       wscreen = allocscreen(screen, background, 0);
       if(wscreen == nil)
               error("can't allocate screen");
       draw(view, viewr, background, nil, viewr.min);
       flushimage(display, 1);

       exitchan = chancreate(sizeof(int), 0);
       winclosechan = chancreate(sizeof(Window*), 0);
       deletechan = chancreate(sizeof(char*), 0);

       timerinit();
       threadcreate(keyboardthread, nil, STACK);
       threadcreate(mousethread, nil, STACK);
       threadcreate(winclosethread, nil, STACK);
       threadcreate(deletethread, nil, STACK);
       filsys = filsysinit(xfidinit());

       if(filsys == nil)
               fprint(2, "rio: can't create file system server: %r\n");
       else{
               errorshouldabort = 1;   /* suicide if there's trouble after this */
               if(initstr)
                       proccreate(initcmd, initstr, STACK);
               if(kbdin){
                       kbdargv[2] = kbdin;
                       r = screen-&gt;r;
                       r.max.x = r.min.x+300;
                       r.max.y = r.min.y+80;
                       i = allocwindow(wscreen, r, Refbackup, DWhite);
                       wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv);
                       if(wkeyboard == nil)
                               error("can't create keyboard window");
               }
               threadnotify(shutdown, 1);
               recv(exitchan, nil);
       }
       killprocs();
       threadexitsall(nil);
}

/*
* /dev/snarf updates when the file is closed, so we must open our own
* fd here rather than use snarffd
*/
void
putsnarf(void)
{
       int fd, i, n;

       if(snarffd&lt;0 || nsnarf==0)
               return;
       fd = open("/dev/snarf", OWRITE);
       if(fd &lt; 0)
               return;
       /* snarf buffer could be huge, so fprint will truncate; do it in blocks */
       for(i=0; i&lt;nsnarf; i+=n){
               n = nsnarf-i;
               if(n &gt;= 256)
                       n = 256;
               if(fprint(fd, "%.*S", n, snarf+i) &lt; 0)
                       break;
       }
       close(fd);
}

void
getsnarf(void)
{
       int i, n, nb, nulls;
       char *sn, buf[1024];

       if(snarffd &lt; 0)
               return;
       sn = nil;
       i = 0;
       seek(snarffd, 0, 0);
       while((n = read(snarffd, buf, sizeof buf)) &gt; 0){
               sn = erealloc(sn, i+n+1);
               memmove(sn+i, buf, n);
               i += n;
               sn[i] = 0;
       }
       if(i &gt; 0){
               snarf = runerealloc(snarf, i+1);
               cvttorunes(sn, i, snarf, &amp;nb, &amp;nsnarf, &amp;nulls);
               free(sn);
       }
}

void
initcmd(void *arg)
{
       char *cmd;

       cmd = arg;
       rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
       procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil);
       fprint(2, "rio: exec failed: %r\n");
       exits("exec");
}

char *oknotes[] =
{
       "delete",
       "hangup",
       "kill",
       "exit",
       nil
};

int
shutdown(void *, char *msg)
{
       int i;

       killprocs();
       for(i=0; oknotes[i]; i++)
               if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
                       threadexitsall(msg);
       fprint(2, "rio %d: abort: %s\n", getpid(), msg);
       abort();
       exits(msg);
       return 0;
}

void
killprocs(void)
{
       int i;

       for(i=0; i&lt;nwindow; i++)
               postnote(PNGROUP, window[i]-&gt;pid, "hangup");
}

void
nextwin(void)
{
       Window *w;
       int i, j, k;

       for(i=0; i&lt;nwindow; i++){
               if(window[i] == input)
                       break;
       }
       k = nwindow;
       while(k-- &gt; 0){
               i = (i + 1) % nwindow;
               if(w = window[i]){
                       for(j=0; j&lt;nhidden; j++){
                               if(hidden[j] == w)
                                       goto skip;
                       }
                       topwindow(w-&gt;i);
                       wcurrent(w);
                       wtopme(w);
                       break;
skip:                   continue;
               }
       }
}

void
keyboardthread(void*)
{
       Rune buf[2][20], *rp;
       int n, i;
       static int switcher;

       threadsetname("keyboardthread");
       n = 0;
       for(;;){
               rp = buf[n];
               n = 1-n;
               recv(keyboardctl-&gt;c, rp);
               for(i=1; i&lt;nelem(buf[0])-1; i++)
                       if(nbrecv(keyboardctl-&gt;c, rp+i) &lt;= 0)
                               break;
               rp[i] = L'\0';

               if(input &amp;&amp; strcmp(input-&gt;label, "rio")==0){
                       if(((nwindow - nhidden) &gt; 1) &amp;&amp; *rp == 0xe){
                               nextwin();
                               continue;
                       }
               } else {
                       if(*rp == 0xd || *rp == 0xe){
                               nextwin();
                               continue;
                       }
               }

               if(input != nil)
                       sendp(input-&gt;ck, rp);
       }
}

/*
* Used by /dev/kbdin
*/
void
keyboardsend(char *s, int cnt)
{
       Rune *r;
       int i, nb, nr;

       r = runemalloc(cnt);
       /* BUGlet: partial runes will be converted to error runes */
       cvttorunes(s, cnt, r, &amp;nb, &amp;nr, nil);
       for(i=0; i&lt;nr; i++)
               send(keyboardctl-&gt;c, &amp;r[i]);
       free(r);
}

int
portion(int x, int lo, int hi)
{
       x -= lo;
       hi -= lo;
       if(x &lt; 20)
               return 0;
       if(x &gt; hi-20)
               return 2;
       return 1;
}

int
whichcorner(Window *w, Point p)
{
       int i, j;

       i = portion(p.x, w-&gt;screenr.min.x, w-&gt;screenr.max.x);
       j = portion(p.y, w-&gt;screenr.min.y, w-&gt;screenr.max.y);
       return 3*j+i;
}

void
cornercursor(Window *w, Point p, int force)
{
       if(w!=nil &amp;&amp; winborder(w, p))
               riosetcursor(corners[whichcorner(w, p)], force);
       else
               wsetcursor(w, force);
}

/* thread to allow fsysproc to synchronize window closing with main proc */
void
winclosethread(void*)
{
       Window *w;

       threadsetname("winclosethread");
       for(;;){
               w = recvp(winclosechan);
               wclose(w);
       }
}

/* thread to make Deleted windows that the client still holds disappear offscreen after an interval */
void
deletethread(void*)
{
       char *s;
       Image *i;

       threadsetname("deletethread");
       for(;;){
               s = recvp(deletechan);
               i = namedimage(display, s);
               if(i != nil){
                       /* move it off-screen to hide it, since client is slow in letting it go */
                       originwindow(i, i-&gt;r.min, view-&gt;r.max);
               }
               freeimage(i);
               free(s);
       }
}

void
deletetimeoutproc(void *v)
{
       char *s;

       s = v;
       sleep(750);     /* remove window from screen after 3/4 of a second */
       sendp(deletechan, s);
}

/*
* Button 6 - keyboard toggle - has been pressed.
* Send event to keyboard, wait for button up, send that.
* Note: there is no coordinate translation done here; this
* is just about getting button 6 to the keyboard simulator.
*/
void
keyboardhide(void)
{
       send(wkeyboard-&gt;mc.c, mouse);
       do
               readmouse(mousectl);
       while(mouse-&gt;buttons &amp; (1&lt;&lt;5));
       send(wkeyboard-&gt;mc.c, mouse);
}

void
mousethread(void*)
{
       int sending, inside, scrolling, moving, band;
       Window *oin, *w, *winput;
       Image *i;
       Rectangle r;
       Point xy;
       Mouse tmp;
       enum {
               MReshape,
               MMouse,
               NALT
       };
       static Alt alts[NALT+1];

       threadsetname("mousethread");
       sending = FALSE;
       scrolling = FALSE;
       moving = FALSE;

       alts[MReshape].c = mousectl-&gt;resizec;
       alts[MReshape].v = nil;
       alts[MReshape].op = CHANRCV;
       alts[MMouse].c = mousectl-&gt;c;
       alts[MMouse].v = &amp;mousectl-&gt;Mouse;
       alts[MMouse].op = CHANRCV;
       alts[NALT].op = CHANEND;

       for(;;)
           switch(alt(alts)){
               case MReshape:
                       resized();
                       break;
               case MMouse:
                       if(wkeyboard!=nil &amp;&amp; (mouse-&gt;buttons &amp; (1&lt;&lt;5))){
                               keyboardhide();
                               break;
                       }
               Again:
                       winput = input;
                       /* override everything for the keyboard window */
                       if(wkeyboard!=nil &amp;&amp; ptinrect(mouse-&gt;xy, wkeyboard-&gt;screenr)){
                               /* make sure it's on top; this call is free if it is */
                               wtopme(wkeyboard);
                               winput = wkeyboard;
                       }
                       if(winput!=nil &amp;&amp; winput-&gt;i!=nil){
                               /* convert to logical coordinates */
                               xy.x = mouse-&gt;xy.x + (winput-&gt;i-&gt;r.min.x-winput-&gt;screenr.min.x);
                               xy.y = mouse-&gt;xy.y + (winput-&gt;i-&gt;r.min.y-winput-&gt;screenr.min.y);

                               /* the up and down scroll buttons are not subject to the usual rules */
                               if((mouse-&gt;buttons&amp;(8|16)) &amp;&amp; !winput-&gt;mouseopen)
                                       goto Sending;

                               inside = ptinrect(mouse-&gt;xy, insetrect(winput-&gt;screenr, Selborder));
                               if(winput-&gt;mouseopen)
                                       scrolling = FALSE;
                               else if(scrolling)
                                       scrolling = mouse-&gt;buttons;
                               else
                                       scrolling = mouse-&gt;buttons &amp;&amp; ptinrect(xy, winput-&gt;scrollr);
                               /* topped will be zero if window has been bottomed */
                               if(sending == FALSE &amp;&amp; !scrolling &amp;&amp; winborder(winput, mouse-&gt;xy) &amp;&amp; winput-&gt;topped&gt;0){
                                       moving = TRUE;
                               }else if(inside &amp;&amp; (scrolling || winput-&gt;mouseopen || (mouse-&gt;buttons&amp;1)))
                                       sending = TRUE;
                       }else
                               sending = FALSE;
                       w = wpointto(mouse-&gt;xy);
                       if((w != nil) &amp;&amp; (w != input) &amp;&amp; (!moving) &amp;&amp; (!mouse-&gt;buttons)){
                               wcurrent(w);
                               wborder(w, Selborder);
                               flushimage(display, 1);
                               continue;
                       }
                       if(sending){
                       Sending:
                               if(mouse-&gt;buttons == 0){
                                       cornercursor(winput, mouse-&gt;xy, 0);
                                       sending = FALSE;
                               }else
                                       wsetcursor(winput, 0);
                               tmp = mousectl-&gt;Mouse;
                               tmp.xy = xy;
                               send(winput-&gt;mc.c, &amp;tmp);
                               continue;
                       }
                       /* change cursor if over anyone's border */
                       if(w != nil)
                               cornercursor(w, mouse-&gt;xy, 0);
                       else
                               riosetcursor(nil, 0);
                       if(moving &amp;&amp; (mouse-&gt;buttons&amp;7)){
                               oin = winput;
                               band = mouse-&gt;buttons &amp; 3;
                               sweeping = 1;
                               if(band)
                                       i = bandsize(winput);
                               else
                                       i = drag(winput, &amp;r);
                               sweeping = 0;
                               if(i != nil){
                                       if(winput == oin){
                                               if(band)
                                                       wsendctlmesg(winput, Reshaped, i-&gt;r, i);
                                               else
                                                       wsendctlmesg(winput, Moved, r, i);
                                               cornercursor(winput, mouse-&gt;xy, 1);
                                       }else
                                               freeimage(i);
                               }
                       }

                       if(w != nil)
                               cornercursor(w, mouse-&gt;xy, 0);
                       /* we're not sending the event, but if button is down maybe we should */
                       if(mouse-&gt;buttons){
                               /* w-&gt;topped will be zero if window has been bottomed */
                               if(w==nil || (w==winput &amp;&amp; w-&gt;topped&gt;0)){
                                       if(mouse-&gt;buttons &amp; 1){
                                               ;
                                       }else if(mouse-&gt;buttons &amp; 2){
                                               if(winput &amp;&amp; !winput-&gt;mouseopen)
                                                       button2menu(winput);
                                       }else if(mouse-&gt;buttons &amp; 4)
                                               button3menu();
                               }else{
                                       /* if button 1 event in the window, top the window and wait for button up. */
                                       /* otherwise, top the window and pass the event on */
                                       if(wtop(mouse-&gt;xy) &amp;&amp; (mouse-&gt;buttons!=1 || winborder(w, mouse-&gt;xy)))
                                               goto Again;
                                       goto Drain;
                               }
                       }
                       moving = FALSE;
                       break;

               Drain:
                       do
                               readmouse(mousectl);
                       while(mousectl-&gt;buttons);
                       moving = FALSE;
                       goto Again;     /* recalculate mouse position, cursor */
               }
}

void
resized(void)
{
       Image *im;
       int i, j, ishidden;
       Rectangle r;
       Point o, n;
       Window *w;

       if(getwindow(display, Refnone) &lt; 0)
               error("failed to re-attach window");
       freescrtemps();
       view = screen;
       freescreen(wscreen);
       wscreen = allocscreen(screen, background, 0);
       if(wscreen == nil)
               error("can't re-allocate screen");
       draw(view, view-&gt;r, background, nil, view-&gt;r.min);
       o = subpt(viewr.max, viewr.min);
       n = subpt(view-&gt;clipr.max, view-&gt;clipr.min);
       for(i=0; i&lt;nwindow; i++){
               w = window[i];
               if(w-&gt;deleted)
                       continue;
               r = rectsubpt(w-&gt;i-&gt;r, viewr.min);
/*
               r.min.x = (r.min.x*n.x)/o.x;
               r.min.y = (r.min.y*n.y)/o.y;
               r.max.x = (r.max.x*n.x)/o.x;
               r.max.y = (r.max.y*n.y)/o.y;
               if(Dx(r)&lt;100){
                       r.max.x = r.min.x + 100;
               }
               if(Dy(r)&lt;3*font-&gt;height){
                       r.max.y = r.min.y + 3*font-&gt;height;
               }
*/
               r = rectaddpt(r, screen-&gt;clipr.min);
               ishidden = 0;
               for(j=0; j&lt;nhidden; j++)
                       if(w == hidden[j]){
                               ishidden = 1;
                               break;
                       }
               if(ishidden)
                       im = allocimage(display, r, screen-&gt;chan, 0, DWhite);
               else
                       im = allocwindow(wscreen, r, Refbackup, DWhite);
               if(im)
                       wsendctlmesg(w, Reshaped, r, im);
       }
       viewr = screen-&gt;r;
       flushimage(display, 1);
}

void
button3menu(void)
{
       int i;

       for(i=0; i&lt;nhidden; i++)
               menu3str[i+Hidden] = hidden[i]-&gt;label;
       menu3str[i+Hidden] = nil;

       sweeping = 1;
       switch(i = piemenuhit(3, mousectl, &amp;menu3, wscreen)){
       case -1:
               break;
       case New:
               new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
               break;
       case Reshape:
               resize();
               break;
       case Move:
               move();
               break;
       case Delete:
               delete();
               break;
       case Hide:
               hide();
               break;
       case Exit:
               if(Hidden &gt; Exit){
                       send(exitchan, nil);
                       break;
               }
               /* else fall through */
       default:
               unhide(i);
               break;
       }
       sweeping = 0;
}

void
button2menu(Window *w)
{
       if(w-&gt;deleted)
               return;
       incref(w);
       if(w-&gt;scrolling)
               menu2str[Scroll] = "noscroll";
       else
               menu2str[Scroll] = "scroll";
       switch(piemenuhit(2, mousectl, &amp;menu2, wscreen)){
       case Cut:
               wsnarf(w);
               wcut(w);
               wscrdraw(w);
               break;

       case Snarf:
               wsnarf(w);
               break;

       case Paste:
               getsnarf();
               wpaste(w);
               wscrdraw(w);
               break;

       case Plumb:
               wplumb(w);
               break;

       case Send:
               getsnarf();
               wsnarf(w);
               if(nsnarf == 0)
                       break;
               if(w-&gt;rawing){
                       waddraw(w, snarf, nsnarf);
                       if(snarf[nsnarf-1]!='\n' &amp;&amp; snarf[nsnarf-1]!='\004')
                               waddraw(w, L"\n", 1);
               }else{
                       winsert(w, snarf, nsnarf, w-&gt;nr);
                       if(snarf[nsnarf-1]!='\n' &amp;&amp; snarf[nsnarf-1]!='\004')
                               winsert(w, L"\n", 1, w-&gt;nr);
               }
               wsetselect(w, w-&gt;nr, w-&gt;nr);
               wshow(w, w-&gt;nr);
               break;

       case Scroll:
               if(w-&gt;scrolling ^= 1)
                       wshow(w, w-&gt;nr);
               break;
       }
       wclose(w);
       wsendctlmesg(w, Wakeup, ZR, nil);
       flushimage(display, 1);
}

Point
onscreen(Point p)
{
       p.x = max(screen-&gt;clipr.min.x, p.x);
       p.x = min(screen-&gt;clipr.max.x, p.x);
       p.y = max(screen-&gt;clipr.min.y, p.y);
       p.y = min(screen-&gt;clipr.max.y, p.y);
       return p;
}

Image*
sweep(void)
{
       Image *i, *oi;
       Rectangle r;
       Point p0, p;

       i = nil;
       menuing = TRUE;
       riosetcursor(&amp;crosscursor, 1);
       while(mouse-&gt;buttons == 0)
               readmouse(mousectl);
       p0 = onscreen(mouse-&gt;xy);
       p = p0;
       r.min = p;
       r.max = p;
       oi = nil;
       while(mouse-&gt;buttons == 4){
               readmouse(mousectl);
               if(mouse-&gt;buttons != 4 &amp;&amp; mouse-&gt;buttons != 0)
                       break;
               if(!eqpt(mouse-&gt;xy, p)){
                       p = onscreen(mouse-&gt;xy);
                       r = canonrect(Rpt(p0, p));
                       if(Dx(r)&gt;5 &amp;&amp; Dy(r)&gt;5){
                               i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
                               freeimage(oi);
                               if(i == nil)
                                       goto Rescue;
                               oi = i;
                               border(i, r, Selborder, red, ZP);
                               flushimage(display, 1);
                       }
               }
       }
       if(mouse-&gt;buttons != 0)
               goto Rescue;
       if(i==nil || Dx(i-&gt;r)&lt;100 || Dy(i-&gt;r)&lt;3*font-&gt;height)
               goto Rescue;
       oi = i;
       i = allocwindow(wscreen, oi-&gt;r, Refbackup, DWhite);
       freeimage(oi);
       if(i == nil)
               goto Rescue;
       border(i, r, Selborder, red, ZP);
       cornercursor(input, mouse-&gt;xy, 1);
       goto Return;

Rescue:
       freeimage(i);
       i = nil;
       cornercursor(input, mouse-&gt;xy, 1);
       while(mouse-&gt;buttons)
               readmouse(mousectl);

Return:
       moveto(mousectl, mouse-&gt;xy); /* force cursor update; ugly */
       menuing = FALSE;
       return i;
}

void
drawedge(Image **bp, Rectangle r)
{
       Image *b = *bp;
       if(b != nil &amp;&amp; Dx(b-&gt;r) == Dx(r) &amp;&amp; Dy(b-&gt;r) == Dy(r))
               originwindow(b, r.min, r.min);
       else{
               freeimage(b);
               *bp = allocwindow(wscreen, r, Refbackup, DRed);
       }
}

void
drawborder(Rectangle r, int show)
{
       static Image *b[4];
       int i;
       if(show == 0){
               for(i = 0; i &lt; 4; i++){
                       freeimage(b[i]);
                       b[i] = nil;
               }
       }else{
               r = canonrect(r);
               drawedge(&amp;b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
               drawedge(&amp;b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
               drawedge(&amp;b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
               drawedge(&amp;b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
       }
}

Image*
drag(Window *w, Rectangle *rp)
{
       Image *i, *ni;
       Point p, op, d, dm, om;
       Rectangle r;

       i = w-&gt;i;
       menuing = TRUE;
       om = mouse-&gt;xy;
       riosetcursor(&amp;boxcursor, 1);
       dm = subpt(mouse-&gt;xy, w-&gt;screenr.min);
       d = subpt(i-&gt;r.max, i-&gt;r.min);
       op = subpt(mouse-&gt;xy, dm);
       drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
       flushimage(display, 1);
       while(mouse-&gt;buttons == 4){
               p = subpt(mouse-&gt;xy, dm);
               if(!eqpt(p, op)){
                       drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
                       flushimage(display, 1);
                       op = p;
               }
               readmouse(mousectl);
       }
       r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
       drawborder(r, 0);
       cornercursor(w, mouse-&gt;xy, 1);
       moveto(mousectl, mouse-&gt;xy); /* force cursor update; ugly */
       menuing = FALSE;
       flushimage(display, 1);
       if(mouse-&gt;buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){
               moveto(mousectl, om);
               while(mouse-&gt;buttons)
                       readmouse(mousectl);
               *rp = Rect(0, 0, 0, 0);
               return nil;
       }
       draw(ni, ni-&gt;r, i, nil, i-&gt;r.min);
       *rp = r;
       return ni;
}

Point
cornerpt(Rectangle r, Point p, int which)
{
       switch(which){
       case 0: /* top left */
               p = Pt(r.min.x, r.min.y);
               break;
       case 2: /* top right */
               p = Pt(r.max.x,r.min.y);
               break;
       case 6: /* bottom left */
               p = Pt(r.min.x, r.max.y);
               break;
       case 8: /* bottom right */
               p = Pt(r.max.x, r.max.y);
               break;
       case 1: /* top edge */
               p = Pt(p.x,r.min.y);
               break;
       case 5: /* right edge */
               p = Pt(r.max.x, p.y);
               break;
       case 7: /* bottom edge */
               p = Pt(p.x, r.max.y);
               break;
       case 3:         /* left edge */
               p = Pt(r.min.x, p.y);
               break;
       }
       return p;
}

Rectangle
whichrect(Rectangle r, Point p, int which)
{
       switch(which){
       case 0: /* top left */
               r = Rect(p.x, p.y, r.max.x, r.max.y);
               break;
       case 2: /* top right */
               r = Rect(r.min.x, p.y, p.x, r.max.y);
               break;
       case 6: /* bottom left */
               r = Rect(p.x, r.min.y, r.max.x, p.y);
               break;
       case 8: /* bottom right */
               r = Rect(r.min.x, r.min.y, p.x, p.y);
               break;
       case 1: /* top edge */
               r = Rect(r.min.x, p.y, r.max.x, r.max.y);
               break;
       case 5: /* right edge */
               r = Rect(r.min.x, r.min.y, p.x, r.max.y);
               break;
       case 7: /* bottom edge */
               r = Rect(r.min.x, r.min.y, r.max.x, p.y);
               break;
       case 3:         /* left edge */
               r = Rect(p.x, r.min.y, r.max.x, r.max.y);
               break;
       }
       return canonrect(r);
}

Image*
bandsize(Window *w)
{
       Image *i;
       Rectangle r, or;
       Point p, startp;
       int which, but;

       p = mouse-&gt;xy;

       which = whichcorner(w, p);
       p = cornerpt(w-&gt;screenr, p, which);
       wmovemouse(w, p);
       readmouse(mousectl);
       r = whichrect(w-&gt;screenr, p, which);
       drawborder(r, 1);
       or = r;
       startp = p;
       but = mouse-&gt;buttons;
       while(mouse-&gt;buttons == but){
               p = onscreen(mouse-&gt;xy);
               r = whichrect(w-&gt;screenr, p, which);
               if(!eqrect(r, or) &amp;&amp; goodrect(r)){
                       drawborder(r, 1);
                       flushimage(display, 1);
                       or = r;
               }
               readmouse(mousectl);
       }
       p = mouse-&gt;xy;
       drawborder(or, 0);
       flushimage(display, 1);
       wsetcursor(w, 1);
       if(mouse-&gt;buttons!=0 || Dx(or)&lt;100 || Dy(or)&lt;3*font-&gt;height){
               while(mouse-&gt;buttons)
                       readmouse(mousectl);
               return nil;
       }
       if(abs(p.x-startp.x)+abs(p.y-startp.y) &lt;= 1)
               return nil;
       i = allocwindow(wscreen, or, Refbackup, DWhite);
       if(i == nil)
               return nil;
       border(i, r, Selborder, red, ZP);
       return i;
}

Window*
pointto(int wait)
{
       Window *w;

       menuing = TRUE;
       riosetcursor(&amp;sightcursor, 1);
       while(mouse-&gt;buttons == 0)
               readmouse(mousectl);
       if(mouse-&gt;buttons == 4)
               w = wpointto(mouse-&gt;xy);
       else
               w = nil;
       if(wait)
               while(mouse-&gt;buttons){
                       if(mouse-&gt;buttons!=4 &amp;&amp; w !=nil){    /* cancel */
                               cornercursor(input, mouse-&gt;xy, 0);
                               w = nil;
                       }
                       readmouse(mousectl);
               }
       cornercursor(input, mouse-&gt;xy, 0);
       moveto(mousectl, mouse-&gt;xy); /* force cursor update; ugly */
       menuing = FALSE;
       return w;
}

void
delete(void)
{
       Window *w;

       w = pointto(TRUE);
       if(w)
               wsendctlmesg(w, Deleted, ZR, nil);
}

void
resize(void)
{
       Window *w;
       Image *i;

       w = pointto(TRUE);
       if(w == nil)
               return;
       i = sweep();
       if(i)
               wsendctlmesg(w, Reshaped, i-&gt;r, i);
}

void
move(void)
{
       Window *w;
       Image *i;
       Rectangle r;

       w = pointto(FALSE);
       if(w == nil)
               return;
       i = drag(w, &amp;r);
       if(i)
               wsendctlmesg(w, Moved, r, i);
       cornercursor(input, mouse-&gt;xy, 1);
}

int
whide(Window *w)
{
       Image *i;
       int j;

       for(j=0; j&lt;nhidden; j++)
               if(hidden[j] == w)      /* already hidden */
                       return -1;
       i = allocimage(display, w-&gt;screenr, w-&gt;i-&gt;chan, 0, DWhite);
       if(i){
               hidden[nhidden++] = w;
               wsendctlmesg(w, Reshaped, ZR, i);
               return 1;
       }
       return 0;
}

int
wunhide(int h)
{
       Image *i;
       Window *w;

       w = hidden[h];
       i = allocwindow(wscreen, w-&gt;i-&gt;r, Refbackup, DWhite);
       if(i){
               --nhidden;
               memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*));
               wsendctlmesg(w, Reshaped, w-&gt;i-&gt;r, i);
               return 1;
       }
       return 0;
}

void
hide(void)
{
       Window *w;

       w = pointto(TRUE);
       if(w == nil)
               return;
       whide(w);
}

void
unhide(int h)
{
       Window *w;

       h -= Hidden;
       w = hidden[h];
       if(w == nil)
               return;
       wunhide(h);
}

Window*
new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
{
       Window *w;
       Mousectl *mc;
       Channel *cm, *ck, *cctl, *cpid;
       void **arg;

       if(i == nil)
               return nil;
       cm = chancreate(sizeof(Mouse), 0);
       ck = chancreate(sizeof(Rune*), 0);
       cctl = chancreate(sizeof(Wctlmesg), 4);
       cpid = chancreate(sizeof(int), 0);
       if(cm==nil || ck==nil || cctl==nil)
               error("new: channel alloc failed");
       mc = emalloc(sizeof(Mousectl));
       *mc = *mousectl;
       mc-&gt;image = i;
       mc-&gt;c = cm;
       w = wmk(i, mc, ck, cctl, scrollit);
       free(mc);       /* wmk copies *mc */
       window = erealloc(window, ++nwindow*sizeof(Window*));
       window[nwindow-1] = w;
       if(hideit){
               hidden[nhidden++] = w;
               w-&gt;screenr = ZR;
       }
       threadcreate(winctl, w, 8192);
       if(!hideit)
               wcurrent(w);
       flushimage(display, 1);
       if(pid == 0){
               arg = emalloc(5*sizeof(void*));
               arg[0] = w;
               arg[1] = cpid;
               arg[2] = cmd;
               if(argv == nil)
                       arg[3] = rcargv;
               else
                       arg[3] = argv;
               arg[4] = dir;
               proccreate(winshell, arg, 8192);
               pid = recvul(cpid);
               free(arg);
       }
       if(pid == 0){
               /* window creation failed */
               wsendctlmesg(w, Deleted, ZR, nil);
               chanfree(cpid);
               return nil;
       }
       wsetpid(w, pid, 1);
       wsetname(w);
       if(dir)
               w-&gt;dir = estrdup(dir);
       chanfree(cpid);
       return w;
}
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>