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