patch9/rio
view rio.c @ 1:0f5800109226
Added the riows rc script to run several rios as workspaces
| author | yiyus@yiyus |
|---|---|
| date | Fri Jun 05 21:27:05 2009 -0500 (2009-06-05 ago) |
| parents | |
| children |
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"
14 /*
15 * WASHINGTON (AP) - The Food and Drug Administration warned
16 * consumers Wednesday not to use ``Rio'' hair relaxer products
17 * because they may cause severe hair loss or turn hair green....
18 * The FDA urged consumers who have experienced problems with Rio
19 * to notify their local FDA office, local health department or the
20 * company at 1‑800‑543‑3002.
21 */
23 void resize(void);
24 void move(void);
25 void delete(void);
26 void hide(void);
27 void unhide(int);
28 void newtile(int);
29 Image *sweep(void);
30 Image *bandsize(Window*);
31 Image* drag(Window*, Rectangle*);
32 void refresh(Rectangle);
33 void resized(void);
34 Channel *exitchan; /* chan(int) */
35 Channel *winclosechan; /* chan(Window*); */
36 Rectangle viewr;
37 int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */
39 void mousethread(void*);
40 void keyboardthread(void*);
41 void winclosethread(void*);
42 void deletethread(void*);
43 void initcmd(void*);
45 char *fontname;
46 int mainpid;
48 enum
49 {
50 New,
51 Reshape,
52 Move,
53 Delete,
54 Hide,
55 Exit,
56 };
58 enum
59 {
60 Cut,
61 Paste,
62 Snarf,
63 Plumb,
64 Send,
65 Scroll,
66 };
68 char *menu2str[] = {
69 [Cut] "cut",
70 [Paste] "paste",
71 [Snarf] "snarf",
72 [Plumb] "plumb",
73 [Send] "send",
74 [Scroll] "scroll",
75 nil
76 };
78 Menu menu2 =
79 {
80 menu2str
81 };
83 int Hidden = Exit+1;
85 char *menu3str[100] = {
86 [New] "New",
87 [Reshape] "Resize",
88 [Move] "Move",
89 [Delete] "Delete",
90 [Hide] "Hide",
91 [Exit] "Exit",
92 nil
93 };
95 Menu menu3 =
96 {
97 menu3str
98 };
100 char *rcargv[] = { "rc", "-i", nil };
101 char *kbdargv[] = { "rc", "-c", nil, nil };
103 int errorshouldabort = 0;
105 void
106 derror(Display*, char *errorstr)
107 {
108 error(errorstr);
109 }
111 void
112 usage(void)
113 {
114 fprint(2, "usage: rio [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
115 exits("usage");
116 }
118 void
119 threadmain(int argc, char *argv[])
120 {
121 char *initstr, *kbdin, *s;
122 static void *arg[1];
123 char buf[256];
124 Image *i;
125 Rectangle r;
127 if(strstr(argv[0], ".out") == nil){
128 menu3str[Exit] = nil;
129 Hidden--;
130 }
131 initstr = nil;
132 kbdin = nil;
133 maxtab = 0;
134 selborder = Selborder;
135 ARGBEGIN{
136 case 'f':
137 fontname = ARGF();
138 if(fontname == nil)
139 usage();
140 break;
141 case 'i':
142 initstr = ARGF();
143 if(initstr == nil)
144 usage();
145 break;
146 case 'I':
147 selborder = 0;
148 break;
149 case 'k':
150 if(kbdin != nil)
151 usage();
152 kbdin = ARGF();
153 if(kbdin == nil)
154 usage();
155 break;
156 case 's':
157 scrolling = TRUE;
158 break;
159 }ARGEND
161 mainpid = getpid();
162 if(getwd(buf, sizeof buf) == nil)
163 startdir = estrdup(".");
164 else
165 startdir = estrdup(buf);
166 if(fontname == nil)
167 fontname = getenv("font");
168 if(fontname == nil)
169 fontname = "/lib/font/bit/lucm/unicode.9.font";
170 s = getenv("tabstop");
171 if(s != nil)
172 maxtab = strtol(s, nil, 0);
173 if(maxtab == 0)
174 maxtab = 4;
175 free(s);
176 /* check font before barging ahead */
177 if(access(fontname, 0) < 0){
178 fprint(2, "rio: can't access %s: %r\n", fontname);
179 exits("font open");
180 }
181 putenv("font", fontname);
183 snarffd = open("/dev/snarf", OREAD|OCEXEC);
185 if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){
186 fprint(2, "rio: can't open display: %r\n");
187 exits("display open");
188 }
189 iconinit();
190 view = screen;
191 viewr = view->r;
192 mousectl = initmouse(nil, screen);
193 if(mousectl == nil)
194 error("can't find mouse");
195 mouse = mousectl;
196 keyboardctl = initkeyboard(nil);
197 if(keyboardctl == nil)
198 error("can't find keyboard");
199 wscreen = allocscreen(screen, background, 0);
200 if(wscreen == nil)
201 error("can't allocate screen");
202 draw(view, viewr, background, nil, ZP);
203 flushimage(display, 1);
205 exitchan = chancreate(sizeof(int), 0);
206 winclosechan = chancreate(sizeof(Window*), 0);
207 deletechan = chancreate(sizeof(char*), 0);
209 timerinit();
210 threadcreate(keyboardthread, nil, STACK);
211 threadcreate(mousethread, nil, STACK);
212 threadcreate(winclosethread, nil, STACK);
213 threadcreate(deletethread, nil, STACK);
214 filsys = filsysinit(xfidinit());
215 if(filsys == nil)
216 fprint(2, "rio: can't create file system server: %r\n");
217 else{
218 errorshouldabort = 1; /* suicide if there's trouble after this */
219 if(initstr)
220 proccreate(initcmd, initstr, STACK);
221 if(kbdin){
222 kbdargv[2] = kbdin;
223 r = screen->r;
224 r.max.x = r.min.x+300;
225 r.max.y = r.min.y+80;
226 i = allocwindow(wscreen, r, Refbackup, DWhite);
227 wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv);
228 if(wkeyboard == nil)
229 error("can't create keyboard window");
230 }
231 threadnotify(shutdown, 1);
232 recv(exitchan, nil);
233 }
234 killprocs();
235 threadexitsall(nil);
236 }
238 /*
239 * /dev/snarf updates when the file is closed, so we must open our own
240 * fd here rather than use snarffd
241 */
242 void
243 putsnarf(void)
244 {
245 int fd, i, n;
247 if(snarffd<0 || nsnarf==0)
248 return;
249 fd = open("/dev/snarf", OWRITE);
250 if(fd < 0)
251 return;
252 /* snarf buffer could be huge, so fprint will truncate; do it in blocks */
253 for(i=0; i<nsnarf; i+=n){
254 n = nsnarf-i;
255 if(n >= 256)
256 n = 256;
257 if(fprint(fd, "%.*S", n, snarf+i) < 0)
258 break;
259 }
260 close(fd);
261 }
263 void
264 getsnarf(void)
265 {
266 int i, n, nb, nulls;
267 char *sn, buf[1024];
269 if(snarffd < 0)
270 return;
271 sn = nil;
272 i = 0;
273 seek(snarffd, 0, 0);
274 while((n = read(snarffd, buf, sizeof buf)) > 0){
275 sn = erealloc(sn, i+n+1);
276 memmove(sn+i, buf, n);
277 i += n;
278 sn[i] = 0;
279 }
280 if(i > 0){
281 snarf = runerealloc(snarf, i+1);
282 cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
283 free(sn);
284 }
285 }
287 void
288 initcmd(void *arg)
289 {
290 char *cmd;
292 cmd = arg;
293 rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
294 procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil);
295 fprint(2, "rio: exec failed: %r\n");
296 exits("exec");
297 }
299 char *oknotes[] =
300 {
301 "delete",
302 "hangup",
303 "kill",
304 "exit",
305 nil
306 };
308 int
309 shutdown(void *, char *msg)
310 {
311 int i;
312 static Lock shutdownlk;
314 killprocs();
315 for(i=0; oknotes[i]; i++)
316 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){
317 lock(&shutdownlk); /* only one can threadexitsall */
318 threadexitsall(msg);
319 }
320 fprint(2, "rio %d: abort: %s\n", getpid(), msg);
321 abort();
322 exits(msg);
323 return 0;
324 }
326 void
327 killprocs(void)
328 {
329 int i;
331 for(i=0; i<nwindow; i++)
332 postnote(PNGROUP, window[i]->pid, "hangup");
333 }
335 void
336 keyboardthread(void*)
337 {
338 Rune buf[2][20], *rp;
339 int n, i;
341 threadsetname("keyboardthread");
342 n = 0;
343 for(;;){
344 rp = buf[n];
345 n = 1-n;
346 recv(keyboardctl->c, rp);
347 for(i=1; i<nelem(buf[0])-1; i++)
348 if(nbrecv(keyboardctl->c, rp+i) <= 0)
349 break;
350 rp[i] = L'\0';
351 if(input != nil)
352 sendp(input->ck, rp);
353 }
354 }
356 /*
357 * Used by /dev/kbdin
358 */
359 void
360 keyboardsend(char *s, int cnt)
361 {
362 Rune *r;
363 int i, nb, nr;
365 r = runemalloc(cnt);
366 /* BUGlet: partial runes will be converted to error runes */
367 cvttorunes(s, cnt, r, &nb, &nr, nil);
368 for(i=0; i<nr; i++)
369 send(keyboardctl->c, &r[i]);
370 free(r);
371 }
373 int
374 portion(int x, int lo, int hi)
375 {
376 x -= lo;
377 hi -= lo;
378 if(x < 20)
379 return 0;
380 if(x > hi-20)
381 return 2;
382 return 1;
383 }
385 int
386 whichcorner(Window *w, Point p)
387 {
388 int i, j;
390 i = portion(p.x, w->screenr.min.x, w->screenr.max.x);
391 j = portion(p.y, w->screenr.min.y, w->screenr.max.y);
392 return 3*j+i;
393 }
395 void
396 cornercursor(Window *w, Point p, int force)
397 {
398 if(w!=nil && w!=wroot && winborder(w, p))
399 riosetcursor(corners[whichcorner(w, p)], force);
400 else
401 wsetcursor(w, force);
402 }
404 /* thread to allow fsysproc to synchronize window closing with main proc */
405 void
406 winclosethread(void*)
407 {
408 Window *w;
410 threadsetname("winclosethread");
411 for(;;){
412 w = recvp(winclosechan);
413 wclose(w);
414 }
415 }
417 /* thread to make Deleted windows that the client still holds disappear offscreen after an interval */
418 void
419 deletethread(void*)
420 {
421 char *s;
422 Image *i;
424 threadsetname("deletethread");
425 for(;;){
426 s = recvp(deletechan);
427 i = namedimage(display, s);
428 if(i != nil){
429 /* move it off-screen to hide it, since client is slow in letting it go */
430 originwindow(i, i->r.min, view->r.max);
431 }
432 freeimage(i);
433 free(s);
434 }
435 }
437 void
438 deletetimeoutproc(void *v)
439 {
440 char *s;
442 s = v;
443 sleep(750); /* remove window from screen after 3/4 of a second */
444 sendp(deletechan, s);
445 }
447 /*
448 * Button 6 - keyboard toggle - has been pressed.
449 * Send event to keyboard, wait for button up, send that.
450 * Note: there is no coordinate translation done here; this
451 * is just about getting button 6 to the keyboard simulator.
452 */
453 void
454 keyboardhide(void)
455 {
456 send(wkeyboard->mc.c, mouse);
457 do
458 readmouse(mousectl);
459 while(mouse->buttons & (1<<5));
460 send(wkeyboard->mc.c, mouse);
461 }
463 void
464 mousethread(void*)
465 {
466 int sending, inside, scrolling, moving, band;
467 Window *oin, *w, *winput;
468 Image *i;
469 Rectangle r;
470 Point xy;
471 Mouse tmp;
472 enum {
473 MReshape,
474 MMouse,
475 NALT
476 };
477 static Alt alts[NALT+1];
479 threadsetname("mousethread");
480 sending = FALSE;
481 scrolling = FALSE;
482 moving = FALSE;
484 alts[MReshape].c = mousectl->resizec;
485 alts[MReshape].v = nil;
486 alts[MReshape].op = CHANRCV;
487 alts[MMouse].c = mousectl->c;
488 alts[MMouse].v = &mousectl->Mouse;
489 alts[MMouse].op = CHANRCV;
490 alts[NALT].op = CHANEND;
492 for(;;)
493 switch(alt(alts)){
494 case MReshape:
495 resized();
496 break;
497 case MMouse:
498 if(wkeyboard!=nil && (mouse->buttons & (1<<5))){
499 keyboardhide();
500 break;
501 }
502 Again:
503 winput = input;
504 if(selborder == 0)
505 wtop(mouse->xy);
506 /* override everything for the keyboard window */
507 if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
508 /* make sure it's on top; this call is free if it is */
509 wtopme(wkeyboard);
510 winput = wkeyboard;
511 }
512 if(winput!=nil && winput->i!=nil){
513 /* convert to logical coordinates */
514 xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
515 xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
517 /* the up and down scroll buttons are not subject to the usual rules */
518 if((mouse->buttons&(8|16)) && !winput->mouseopen)
519 goto Sending;
521 inside = ptinrect(mouse->xy, insetrect(winput->screenr, selborder));
522 if(winput->mouseopen)
523 scrolling = FALSE;
524 else if(scrolling)
525 scrolling = mouse->buttons;
526 else
527 scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
528 /* topped will be zero or less if window has been bottomed */
529 if(sending == FALSE && !scrolling && winborder(winput, mouse->xy) && winput->topped>0){
530 if(winput != wroot)
531 moving = TRUE;
532 }else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
533 sending = TRUE;
534 }else
535 sending = FALSE;
536 if(sending){
537 Sending:
538 if(mouse->buttons == 0){
539 cornercursor(winput, mouse->xy, 0);
540 sending = FALSE;
541 }else
542 wsetcursor(winput, 0);
543 tmp = mousectl->Mouse;
544 tmp.xy = xy;
545 send(winput->mc.c, &tmp);
546 continue;
547 }
548 w = wpointto(mouse->xy);
549 /* change cursor if over anyone's border */
550 if(w != nil)
551 cornercursor(w, mouse->xy, 0);
552 else
553 riosetcursor(nil, 0);
554 if(moving && (mouse->buttons&7)){
555 oin = winput;
556 band = mouse->buttons & 3;
557 sweeping = 1;
558 if(band)
559 i = bandsize(winput);
560 else
561 i = drag(winput, &r);
562 sweeping = 0;
563 if(i != nil){
564 if(winput == oin){
565 if(band)
566 wsendctlmesg(winput, Reshaped, i->r, i);
567 else
568 wsendctlmesg(winput, Moved, r, i);
569 cornercursor(winput, mouse->xy, 1);
570 }else
571 freeimage(i);
572 }
573 }
574 if(w != nil)
575 cornercursor(w, mouse->xy, 0);
576 /* we're not sending the event, but if button is down maybe we should */
577 if(mouse->buttons){
578 /* w->topped will be zero or less if window has been bottomed */
579 if(w==nil || (w==winput && w->topped>0)){
580 if(mouse->buttons & 1){
581 ;
582 }else if(mouse->buttons & 2){
583 if(winput && !winput->mouseopen)
584 button2menu(winput);
585 }else if(mouse->buttons & 4){
586 if(wroot)
587 goto Drain;
588 else if(selborder != 0)
589 button3menu();
590 }
591 }else if(winput != wroot){
592 /* if button 1 event in the window, top the window and wait for button up. */
593 /* otherwise, top the window and pass the event on */
594 if(wtop(mouse->xy) && (mouse->buttons!=1 || winborder(w, mouse->xy)))
595 goto Again;
596 goto Drain;
597 }
598 }
599 moving = FALSE;
600 break;
602 Drain:
603 do
604 readmouse(mousectl);
605 while(mousectl->buttons);
606 moving = FALSE;
607 goto Again; /* recalculate mouse position, cursor */
608 }
609 }
611 void
612 resized(void)
613 {
614 Image *im;
615 int i, j, ishidden;
616 Rectangle r;
617 Point o, n;
618 Window *w;
620 if(getwindow(display, Refnone) < 0)
621 error("failed to re-attach window");
622 freescrtemps();
623 view = screen;
624 freescreen(wscreen);
625 wscreen = allocscreen(screen, background, 0);
626 if(wscreen == nil)
627 error("can't re-allocate screen");
628 draw(view, view->r, background, nil, ZP);
629 o = subpt(viewr.max, viewr.min);
630 n = subpt(view->clipr.max, view->clipr.min);
631 for(i=0; i<nwindow; i++){
632 w = window[i];
633 if(w->deleted)
634 continue;
635 r = rectsubpt(w->i->r, viewr.min);
636 r.min.x = (r.min.x*n.x)/o.x;
637 r.min.y = (r.min.y*n.y)/o.y;
638 r.max.x = (r.max.x*n.x)/o.x;
639 r.max.y = (r.max.y*n.y)/o.y;
640 r = rectaddpt(r, screen->clipr.min);
641 ishidden = 0;
642 for(j=0; j<nhidden; j++)
643 if(w == hidden[j]){
644 ishidden = 1;
645 break;
646 }
647 if(ishidden)
648 im = allocimage(display, r, screen->chan, 0, DWhite);
649 else
650 im = allocwindow(wscreen, r, Refbackup, DWhite);
651 if(im)
652 wsendctlmesg(w, Reshaped, r, im);
653 }
654 viewr = screen->r;
655 flushimage(display, 1);
656 }
658 void
659 button3menu(void)
660 {
661 int i;
663 for(i=0; i<nhidden; i++)
664 menu3str[i+Hidden] = hidden[i]->label;
665 menu3str[i+Hidden] = nil;
666 sweeping = 1;
667 switch(i = menuhit(3, mousectl, &menu3, wscreen)){
668 case -1:
669 break;
670 case New:
671 new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
672 break;
673 case Reshape:
674 resize();
675 break;
676 case Move:
677 move();
678 break;
679 case Delete:
680 delete();
681 break;
682 case Hide:
683 hide();
684 break;
685 case Exit:
686 if(Hidden > Exit){
687 send(exitchan, nil);
688 break;
689 }
690 /* else fall through */
691 default:
692 unhide(i);
693 break;
694 }
695 sweeping = 0;
696 }
698 void
699 button2menu(Window *w)
700 {
701 if(w->deleted)
702 return;
703 incref(w);
704 if(w->scrolling)
705 menu2str[Scroll] = "noscroll";
706 else
707 menu2str[Scroll] = "scroll";
708 switch(menuhit(2, mousectl, &menu2, wscreen)){
709 case Cut:
710 wsnarf(w);
711 wcut(w);
712 wscrdraw(w);
713 break;
715 case Snarf:
716 wsnarf(w);
717 break;
719 case Paste:
720 getsnarf();
721 wpaste(w);
722 wscrdraw(w);
723 break;
725 case Plumb:
726 wplumb(w);
727 break;
729 case Send:
730 getsnarf();
731 wsnarf(w);
732 if(nsnarf == 0)
733 break;
734 if(w->rawing){
735 waddraw(w, snarf, nsnarf);
736 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
737 waddraw(w, L"\n", 1);
738 }else{
739 winsert(w, snarf, nsnarf, w->nr);
740 if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
741 winsert(w, L"\n", 1, w->nr);
742 }
743 wsetselect(w, w->nr, w->nr);
744 wshow(w, w->nr);
745 break;
747 case Scroll:
748 if(w->scrolling ^= 1)
749 wshow(w, w->nr);
750 break;
751 }
752 wclose(w);
753 wsendctlmesg(w, Wakeup, ZR, nil);
754 flushimage(display, 1);
755 }
757 Point
758 onscreen(Point p)
759 {
760 p.x = max(screen->clipr.min.x, p.x);
761 p.x = min(screen->clipr.max.x, p.x);
762 p.y = max(screen->clipr.min.y, p.y);
763 p.y = min(screen->clipr.max.y, p.y);
764 return p;
765 }
767 Image*
768 sweep(void)
769 {
770 Image *i, *oi;
771 Rectangle r;
772 Point p0, p;
774 i = nil;
775 menuing = TRUE;
776 riosetcursor(&crosscursor, 1);
777 while(mouse->buttons == 0)
778 readmouse(mousectl);
779 p0 = onscreen(mouse->xy);
780 p = p0;
781 r.min = p;
782 r.max = p;
783 oi = nil;
784 while(mouse->buttons == 4){
785 readmouse(mousectl);
786 if(mouse->buttons != 4 && mouse->buttons != 0)
787 break;
788 if(!eqpt(mouse->xy, p)){
789 p = onscreen(mouse->xy);
790 r = canonrect(Rpt(p0, p));
791 if(Dx(r)>5 && Dy(r)>5){
792 i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
793 freeimage(oi);
794 if(i == nil)
795 goto Rescue;
796 oi = i;
797 border(i, r, Selborder, red, ZP);
798 flushimage(display, 1);
799 }
800 }
801 }
802 if(mouse->buttons != 0)
803 goto Rescue;
804 if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height)
805 goto Rescue;
806 oi = i;
807 i = allocwindow(wscreen, oi->r, Refbackup, DWhite);
808 freeimage(oi);
809 if(i == nil)
810 goto Rescue;
811 border(i, r, Selborder, red, ZP);
812 cornercursor(input, mouse->xy, 1);
813 goto Return;
815 Rescue:
816 freeimage(i);
817 i = nil;
818 cornercursor(input, mouse->xy, 1);
819 while(mouse->buttons)
820 readmouse(mousectl);
822 Return:
823 moveto(mousectl, mouse->xy); /* force cursor update; ugly */
824 menuing = FALSE;
825 return i;
826 }
828 void
829 drawedge(Image **bp, Rectangle r)
830 {
831 Image *b = *bp;
832 if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
833 originwindow(b, r.min, r.min);
834 else{
835 freeimage(b);
836 *bp = allocwindow(wscreen, r, Refbackup, DRed);
837 }
838 }
840 void
841 drawborder(Rectangle r, int show)
842 {
843 static Image *b[4];
844 int i;
845 if(show == 0){
846 for(i = 0; i < 4; i++){
847 freeimage(b[i]);
848 b[i] = nil;
849 }
850 }else{
851 r = canonrect(r);
852 drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
853 drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
854 drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
855 drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
856 }
857 }
859 Image*
860 drag(Window *w, Rectangle *rp)
861 {
862 Image *i, *ni;
863 Point p, op, d, dm, om;
864 Rectangle r;
866 i = w->i;
867 menuing = TRUE;
868 om = mouse->xy;
869 riosetcursor(&boxcursor, 1);
870 dm = subpt(mouse->xy, w->screenr.min);
871 d = subpt(i->r.max, i->r.min);
872 op = subpt(mouse->xy, dm);
873 drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
874 flushimage(display, 1);
875 while(mouse->buttons == 4){
876 p = subpt(mouse->xy, dm);
877 if(!eqpt(p, op)){
878 drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
879 flushimage(display, 1);
880 op = p;
881 }
882 readmouse(mousectl);
883 }
884 r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
885 drawborder(r, 0);
886 cornercursor(w, mouse->xy, 1);
887 moveto(mousectl, mouse->xy); /* force cursor update; ugly */
888 menuing = FALSE;
889 flushimage(display, 1);
890 if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){
891 moveto(mousectl, om);
892 while(mouse->buttons)
893 readmouse(mousectl);
894 *rp = Rect(0, 0, 0, 0);
895 return nil;
896 }
897 draw(ni, ni->r, i, nil, i->r.min);
898 *rp = r;
899 return ni;
900 }
902 Point
903 cornerpt(Rectangle r, Point p, int which)
904 {
905 switch(which){
906 case 0: /* top left */
907 p = Pt(r.min.x, r.min.y);
908 break;
909 case 2: /* top right */
910 p = Pt(r.max.x,r.min.y);
911 break;
912 case 6: /* bottom left */
913 p = Pt(r.min.x, r.max.y);
914 break;
915 case 8: /* bottom right */
916 p = Pt(r.max.x, r.max.y);
917 break;
918 case 1: /* top edge */
919 p = Pt(p.x,r.min.y);
920 break;
921 case 5: /* right edge */
922 p = Pt(r.max.x, p.y);
923 break;
924 case 7: /* bottom edge */
925 p = Pt(p.x, r.max.y);
926 break;
927 case 3: /* left edge */
928 p = Pt(r.min.x, p.y);
929 break;
930 }
931 return p;
932 }
934 Rectangle
935 whichrect(Rectangle r, Point p, int which)
936 {
937 switch(which){
938 case 0: /* top left */
939 r = Rect(p.x, p.y, r.max.x, r.max.y);
940 break;
941 case 2: /* top right */
942 r = Rect(r.min.x, p.y, p.x, r.max.y);
943 break;
944 case 6: /* bottom left */
945 r = Rect(p.x, r.min.y, r.max.x, p.y);
946 break;
947 case 8: /* bottom right */
948 r = Rect(r.min.x, r.min.y, p.x, p.y);
949 break;
950 case 1: /* top edge */
951 r = Rect(r.min.x, p.y, r.max.x, r.max.y);
952 break;
953 case 5: /* right edge */
954 r = Rect(r.min.x, r.min.y, p.x, r.max.y);
955 break;
956 case 7: /* bottom edge */
957 r = Rect(r.min.x, r.min.y, r.max.x, p.y);
958 break;
959 case 3: /* left edge */
960 r = Rect(p.x, r.min.y, r.max.x, r.max.y);
961 break;
962 }
963 return canonrect(r);
964 }
966 Image*
967 bandsize(Window *w)
968 {
969 Image *i;
970 Rectangle r, or;
971 Point p, startp;
972 int which, but;
974 p = mouse->xy;
975 but = mouse->buttons;
976 which = whichcorner(w, p);
977 p = cornerpt(w->screenr, p, which);
978 wmovemouse(w, p);
979 readmouse(mousectl);
980 r = whichrect(w->screenr, p, which);
981 drawborder(r, 1);
982 or = r;
983 startp = p;
985 while(mouse->buttons == but){
986 p = onscreen(mouse->xy);
987 r = whichrect(w->screenr, p, which);
988 if(!eqrect(r, or) && goodrect(r)){
989 drawborder(r, 1);
990 flushimage(display, 1);
991 or = r;
992 }
993 readmouse(mousectl);
994 }
995 p = mouse->xy;
996 drawborder(or, 0);
997 flushimage(display, 1);
998 wsetcursor(w, 1);
999 if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){
1000 while(mouse->buttons)
1001 readmouse(mousectl);
1002 return nil;
1003 }
1004 if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1)
1005 return nil;
1006 i = allocwindow(wscreen, or, Refbackup, DWhite);
1007 if(i == nil)
1008 return nil;
1009 border(i, r, Selborder, red, ZP);
1010 return i;
1011 }
1013 Window*
1014 pointto(int wait)
1015 {
1016 Window *w;
1018 menuing = TRUE;
1019 riosetcursor(&sightcursor, 1);
1020 while(mouse->buttons == 0)
1021 readmouse(mousectl);
1022 if(mouse->buttons == 4)
1023 w = wpointto(mouse->xy);
1024 else
1025 w = nil;
1026 if(wait){
1027 while(mouse->buttons){
1028 if(mouse->buttons!=4 && w !=nil){ /* cancel */
1029 cornercursor(input, mouse->xy, 0);
1030 w = nil;
1031 }
1032 readmouse(mousectl);
1033 }
1034 if(w != nil && wpointto(mouse->xy) != w)
1035 w = nil;
1036 }
1037 cornercursor(input, mouse->xy, 0);
1038 moveto(mousectl, mouse->xy); /* force cursor update; ugly */
1039 menuing = FALSE;
1040 return w;
1041 }
1043 void
1044 delete(void)
1045 {
1046 Window *w;
1048 w = pointto(TRUE);
1049 if(w)
1050 wsendctlmesg(w, Deleted, ZR, nil);
1051 }
1053 void
1054 resize(void)
1055 {
1056 Window *w;
1057 Image *i;
1059 w = pointto(TRUE);
1060 if(w == nil)
1061 return;
1062 i = sweep();
1063 if(i)
1064 wsendctlmesg(w, Reshaped, i->r, i);
1065 }
1067 void
1068 move(void)
1069 {
1070 Window *w;
1071 Image *i;
1072 Rectangle r;
1074 w = pointto(FALSE);
1075 if(w == nil)
1076 return;
1077 i = drag(w, &r);
1078 if(i)
1079 wsendctlmesg(w, Moved, r, i);
1080 cornercursor(input, mouse->xy, 1);
1081 }
1083 int
1084 whide(Window *w)
1085 {
1086 Image *i;
1087 int j;
1089 for(j=0; j<nhidden; j++)
1090 if(hidden[j] == w) /* already hidden */
1091 return -1;
1092 i = allocimage(display, w->screenr, w->i->chan, 0, DWhite);
1093 if(i){
1094 hidden[nhidden++] = w;
1095 wsendctlmesg(w, Reshaped, ZR, i);
1096 return 1;
1097 }
1098 return 0;
1099 }
1101 int
1102 wunhide(int h)
1103 {
1104 Image *i;
1105 Window *w;
1107 w = hidden[h];
1108 i = allocwindow(wscreen, w->i->r, Refbackup, DWhite);
1109 if(i){
1110 --nhidden;
1111 memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*));
1112 wsendctlmesg(w, Reshaped, w->i->r, i);
1113 return 1;
1114 }
1115 return 0;
1116 }
1118 void
1119 hide(void)
1120 {
1121 Window *w;
1123 w = pointto(TRUE);
1124 if(w == nil)
1125 return;
1126 whide(w);
1127 }
1129 void
1130 unhide(int h)
1131 {
1132 Window *w;
1134 h -= Hidden;
1135 w = hidden[h];
1136 if(w == nil)
1137 return;
1138 wunhide(h);
1139 }
1141 Window*
1142 new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
1143 {
1144 Window *w;
1145 Mousectl *mc;
1146 Channel *cm, *ck, *cctl, *cpid;
1147 void **arg;
1149 if(i == nil)
1150 return nil;
1151 cm = chancreate(sizeof(Mouse), 0);
1152 ck = chancreate(sizeof(Rune*), 0);
1153 cctl = chancreate(sizeof(Wctlmesg), 4);
1154 cpid = chancreate(sizeof(int), 0);
1155 if(cm==nil || ck==nil || cctl==nil)
1156 error("new: channel alloc failed");
1157 mc = emalloc(sizeof(Mousectl));
1158 *mc = *mousectl;
1159 mc->image = i;
1160 mc->c = cm;
1161 w = wmk(i, mc, ck, cctl, scrollit);
1162 free(mc); /* wmk copies *mc */
1163 window = erealloc(window, ++nwindow*sizeof(Window*));
1164 window[nwindow-1] = w;
1165 if(hideit){
1166 hidden[nhidden++] = w;
1167 w->screenr = ZR;
1168 }
1169 threadcreate(winctl, w, 8192);
1170 if(!hideit)
1171 wcurrent(w);
1172 flushimage(display, 1);
1173 if(pid == 0){
1174 arg = emalloc(5*sizeof(void*));
1175 arg[0] = w;
1176 arg[1] = cpid;
1177 arg[2] = cmd;
1178 if(argv == nil)
1179 arg[3] = rcargv;
1180 else
1181 arg[3] = argv;
1182 arg[4] = dir;
1183 proccreate(winshell, arg, 8192);
1184 pid = recvul(cpid);
1185 free(arg);
1186 }
1187 if(pid == 0){
1188 /* window creation failed */
1189 wsendctlmesg(w, Deleted, ZR, nil);
1190 chanfree(cpid);
1191 return nil;
1192 }
1193 wsetpid(w, pid, 1);
1194 wsetname(w);
1195 if(dir)
1196 w->dir = estrdup(dir);
1197 chanfree(cpid);
1198 return w;
1199 }