rio.c 22 KB

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