rio.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  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, 0);
  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. inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder));
  456. if(winput->mouseopen)
  457. scrolling = FALSE;
  458. else if(scrolling)
  459. scrolling = mouse->buttons;
  460. else
  461. scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
  462. /* topped will be zero if window has been bottomed */
  463. if(sending == FALSE && !scrolling && winborder(winput, mouse->xy) && winput->topped>0){
  464. moving = TRUE;
  465. }else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
  466. sending = TRUE;
  467. }else
  468. sending = FALSE;
  469. if(sending){
  470. if(mouse->buttons == 0){
  471. cornercursor(winput, mouse->xy, 0);
  472. sending = FALSE;
  473. }else
  474. wsetcursor(winput, 0);
  475. tmp = mousectl->Mouse;
  476. tmp.xy = xy;
  477. send(winput->mc.c, &tmp);
  478. continue;
  479. }
  480. w = wpointto(mouse->xy);
  481. /* change cursor if over anyone's border */
  482. if(w != nil)
  483. cornercursor(w, mouse->xy, 0);
  484. else
  485. riosetcursor(nil, 0);
  486. if(moving && (mouse->buttons&7)){
  487. oin = winput;
  488. band = mouse->buttons & 3;
  489. sweeping = 1;
  490. if(band)
  491. i = bandsize(winput);
  492. else
  493. i = drag(winput, &r);
  494. sweeping = 0;
  495. if(i != nil){
  496. if(winput == oin){
  497. if(band)
  498. wsendctlmesg(winput, Reshaped, i->r, i);
  499. else
  500. wsendctlmesg(winput, Moved, r, i);
  501. cornercursor(winput, mouse->xy, 1);
  502. }else
  503. freeimage(i);
  504. }
  505. }
  506. if(w != nil)
  507. cornercursor(w, mouse->xy, 0);
  508. /* we're not sending the event, but if button is down maybe we should */
  509. if(mouse->buttons){
  510. /* w->topped will be zero if window has been bottomed */
  511. if(w==nil || (w==winput && w->topped>0)){
  512. if(mouse->buttons & 1){
  513. ;
  514. }else if(mouse->buttons & 2){
  515. if(winput && !winput->mouseopen)
  516. button2menu(winput);
  517. }else if(mouse->buttons & 4)
  518. button3menu();
  519. }else{
  520. /* if button 1 event in the window, top the window and wait for button up. */
  521. /* otherwise, top the window and pass the event on */
  522. if(wtop(mouse->xy) && (mouse->buttons!=1 || winborder(w, mouse->xy)))
  523. goto Again;
  524. goto Drain;
  525. }
  526. }
  527. moving = FALSE;
  528. break;
  529. Drain:
  530. do
  531. readmouse(mousectl);
  532. while(mousectl->buttons);
  533. moving = FALSE;
  534. goto Again; /* recalculate mouse position, cursor */
  535. }
  536. }
  537. void
  538. resized(void)
  539. {
  540. Image *im;
  541. int i, j, ishidden;
  542. Rectangle r;
  543. Point o, n;
  544. Window *w;
  545. if(getwindow(display, Refnone) < 0)
  546. error("failed to re-attach window");
  547. freescrtemps();
  548. view = screen;
  549. freescreen(wscreen);
  550. wscreen = allocscreen(screen, background, 0);
  551. if(wscreen == nil)
  552. error("can't re-allocate screen");
  553. draw(view, view->r, background, nil, ZP);
  554. o = subpt(viewr.max, viewr.min);
  555. n = subpt(view->clipr.max, view->clipr.min);
  556. for(i=0; i<nwindow; i++){
  557. w = window[i];
  558. if(w->deleted)
  559. continue;
  560. r = rectsubpt(w->i->r, viewr.min);
  561. r.min.x = (r.min.x*n.x)/o.x;
  562. r.min.y = (r.min.y*n.y)/o.y;
  563. r.max.x = (r.max.x*n.x)/o.x;
  564. r.max.y = (r.max.y*n.y)/o.y;
  565. r = rectaddpt(r, screen->clipr.min);
  566. ishidden = 0;
  567. for(j=0; j<nhidden; j++)
  568. if(w == hidden[j]){
  569. ishidden = 1;
  570. break;
  571. }
  572. if(ishidden)
  573. im = allocimage(display, r, screen->chan, 0, DWhite);
  574. else
  575. im = allocwindow(wscreen, r, Refbackup, DWhite);
  576. if(im)
  577. wsendctlmesg(w, Reshaped, r, im);
  578. }
  579. viewr = screen->r;
  580. flushimage(display, 1);}
  581. void
  582. button3menu(void)
  583. {
  584. int i;
  585. for(i=0; i<nhidden; i++)
  586. menu3str[i+Hidden] = hidden[i]->label;
  587. menu3str[i+Hidden] = nil;
  588. sweeping = 1;
  589. switch(i = menuhit(3, mousectl, &menu3, wscreen)){
  590. case -1:
  591. break;
  592. case New:
  593. new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
  594. break;
  595. case Reshape:
  596. resize();
  597. break;
  598. case Move:
  599. move();
  600. break;
  601. case Delete:
  602. delete();
  603. break;
  604. case Hide:
  605. hide();
  606. break;
  607. case Exit:
  608. if(Hidden > Exit){
  609. send(exitchan, nil);
  610. break;
  611. }
  612. /* else fall through */
  613. default:
  614. unhide(i);
  615. break;
  616. }
  617. sweeping = 0;
  618. }
  619. void
  620. button2menu(Window *w)
  621. {
  622. if(w->deleted)
  623. return;
  624. incref(w);
  625. if(w->scrolling)
  626. menu2str[Scroll] = "noscroll";
  627. else
  628. menu2str[Scroll] = "scroll";
  629. switch(menuhit(2, mousectl, &menu2, wscreen)){
  630. case Cut:
  631. wsnarf(w);
  632. wcut(w);
  633. wscrdraw(w);
  634. break;
  635. case Snarf:
  636. wsnarf(w);
  637. break;
  638. case Paste:
  639. getsnarf();
  640. wpaste(w);
  641. wscrdraw(w);
  642. break;
  643. case Plumb:
  644. wplumb(w);
  645. break;
  646. case Send:
  647. getsnarf();
  648. wsnarf(w);
  649. if(nsnarf == 0)
  650. break;
  651. if(w->rawing){
  652. waddraw(w, snarf, nsnarf);
  653. if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
  654. waddraw(w, L"\n", 1);
  655. }else{
  656. winsert(w, snarf, nsnarf, w->nr);
  657. if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
  658. winsert(w, L"\n", 1, w->nr);
  659. }
  660. wsetselect(w, w->nr, w->nr);
  661. wshow(w, w->nr);
  662. break;
  663. case Scroll:
  664. if(w->scrolling ^= 1)
  665. wshow(w, w->nr);
  666. break;
  667. }
  668. wclose(w);
  669. wsendctlmesg(w, Wakeup, ZR, nil);
  670. flushimage(display, 1);
  671. }
  672. Point
  673. onscreen(Point p)
  674. {
  675. p.x = max(screen->clipr.min.x, p.x);
  676. p.x = min(screen->clipr.max.x, p.x);
  677. p.y = max(screen->clipr.min.y, p.y);
  678. p.y = min(screen->clipr.max.y, p.y);
  679. return p;
  680. }
  681. Image*
  682. sweep(void)
  683. {
  684. Image *i, *oi;
  685. Rectangle r;
  686. Point p0, p;
  687. i = nil;
  688. menuing = TRUE;
  689. riosetcursor(&crosscursor, 1);
  690. while(mouse->buttons == 0)
  691. readmouse(mousectl);
  692. p0 = onscreen(mouse->xy);
  693. p = p0;
  694. r.min = p;
  695. r.max = p;
  696. oi = nil;
  697. while(mouse->buttons == 4){
  698. readmouse(mousectl);
  699. if(mouse->buttons != 4 && mouse->buttons != 0)
  700. break;
  701. if(!eqpt(mouse->xy, p)){
  702. p = onscreen(mouse->xy);
  703. r = canonrect(Rpt(p0, p));
  704. if(Dx(r)>5 && Dy(r)>5){
  705. i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
  706. freeimage(oi);
  707. if(i == nil)
  708. goto Rescue;
  709. oi = i;
  710. border(i, r, Selborder, red, ZP);
  711. flushimage(display, 1);
  712. }
  713. }
  714. }
  715. if(mouse->buttons != 0)
  716. goto Rescue;
  717. if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height)
  718. goto Rescue;
  719. oi = i;
  720. i = allocwindow(wscreen, oi->r, Refbackup, DWhite);
  721. freeimage(oi);
  722. if(i == nil)
  723. goto Rescue;
  724. border(i, r, Selborder, red, ZP);
  725. cornercursor(input, mouse->xy, 1);
  726. goto Return;
  727. Rescue:
  728. freeimage(i);
  729. i = nil;
  730. cornercursor(input, mouse->xy, 1);
  731. while(mouse->buttons)
  732. readmouse(mousectl);
  733. Return:
  734. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  735. menuing = FALSE;
  736. return i;
  737. }
  738. /*
  739. * BUG: should interlock so applications don't change screen
  740. * while tmp[] holds backing store
  741. */
  742. Image*
  743. drag(Window *w, Rectangle *rp)
  744. {
  745. Image *i, *ni;
  746. Point p, op, d, dm, om;
  747. Rectangle r;
  748. i = w->i;
  749. menuing = TRUE;
  750. om = mouse->xy;
  751. riosetcursor(&boxcursor, 1);
  752. dm = subpt(mouse->xy, w->screenr.min);
  753. d = subpt(i->r.max, i->r.min);
  754. op = subpt(mouse->xy, dm);
  755. drawgetrect(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
  756. flushimage(display, 1);
  757. while(mouse->buttons == 4){
  758. p = subpt(mouse->xy, dm);
  759. if(!eqpt(p, op)){
  760. drawgetrect(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 0);
  761. drawgetrect(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
  762. flushimage(display, 1);
  763. op = p;
  764. }
  765. readmouse(mousectl);
  766. }
  767. r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
  768. drawgetrect(r, 0);
  769. cornercursor(w, mouse->xy, 1);
  770. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  771. menuing = FALSE;
  772. flushimage(display, 1);
  773. if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){
  774. moveto(mousectl, om);
  775. while(mouse->buttons)
  776. readmouse(mousectl);
  777. *rp = Rect(0, 0, 0, 0);
  778. return nil;
  779. }
  780. draw(ni, ni->r, i, nil, i->r.min);
  781. *rp = r;
  782. return ni;
  783. }
  784. Rectangle
  785. whichrect(Rectangle r, Point p, int which)
  786. {
  787. switch(which){
  788. case 0: /* top left */
  789. r = Rect(p.x, p.y, r.max.x, r.max.y);
  790. break;
  791. case 2: /* top right */
  792. r = Rect(r.min.x, p.y, p.x, r.max.y);
  793. break;
  794. case 6: /* bottom left */
  795. r = Rect(p.x, r.min.y, r.max.x, p.y);
  796. break;
  797. case 8: /* bottom right */
  798. r = Rect(r.min.x, r.min.y, p.x, p.y);
  799. break;
  800. case 1: /* top edge */
  801. r = Rect(r.min.x, p.y, r.max.x, r.max.y);
  802. break;
  803. case 5: /* right edge */
  804. r = Rect(r.min.x, r.min.y, p.x, r.max.y);
  805. break;
  806. case 7: /* bottom edge */
  807. r = Rect(r.min.x, r.min.y, r.max.x, p.y);
  808. break;
  809. case 3: /* left edge */
  810. r = Rect(p.x, r.min.y, r.max.x, r.max.y);
  811. break;
  812. }
  813. return canonrect(r);
  814. }
  815. Image*
  816. bandsize(Window *w)
  817. {
  818. Image *i;
  819. Rectangle r, or;
  820. Point p, startp;
  821. int which, but;
  822. p = mouse->xy;
  823. startp = p;
  824. which = whichcorner(w, p);
  825. r = whichrect(w->screenr, p, which);
  826. drawgetrect(r, 1);
  827. or = r;
  828. but = mouse->buttons;
  829. while(mouse->buttons == but){
  830. p = onscreen(mouse->xy);
  831. r = whichrect(w->screenr, p, which);
  832. if(!eqrect(r, or) && goodrect(r)){
  833. drawgetrect(or, 0);
  834. drawgetrect(r, 1);
  835. flushimage(display, 1);
  836. or = r;
  837. }
  838. readmouse(mousectl);
  839. }
  840. p = mouse->xy;
  841. drawgetrect(or, 0);
  842. flushimage(display, 1);
  843. wsetcursor(w, 1);
  844. if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){
  845. while(mouse->buttons)
  846. readmouse(mousectl);
  847. return nil;
  848. }
  849. if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1)
  850. return nil;
  851. i = allocwindow(wscreen, or, Refbackup, DWhite);
  852. if(i == nil)
  853. return nil;
  854. border(i, r, Selborder, red, ZP);
  855. return i;
  856. }
  857. Window*
  858. pointto(int wait)
  859. {
  860. Window *w;
  861. menuing = TRUE;
  862. riosetcursor(&sightcursor, 1);
  863. while(mouse->buttons == 0)
  864. readmouse(mousectl);
  865. if(mouse->buttons == 4)
  866. w = wpointto(mouse->xy);
  867. else
  868. w = nil;
  869. if(wait)
  870. while(mouse->buttons){
  871. if(mouse->buttons!=4 && w !=nil){ /* cancel */
  872. cornercursor(input, mouse->xy, 0);
  873. w = nil;
  874. }
  875. readmouse(mousectl);
  876. }
  877. cornercursor(input, mouse->xy, 0);
  878. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  879. menuing = FALSE;
  880. return w;
  881. }
  882. void
  883. delete(void)
  884. {
  885. Window *w;
  886. w = pointto(TRUE);
  887. if(w)
  888. wsendctlmesg(w, Deleted, ZR, nil);
  889. }
  890. void
  891. resize(void)
  892. {
  893. Window *w;
  894. Image *i;
  895. w = pointto(TRUE);
  896. if(w == nil)
  897. return;
  898. i = sweep();
  899. if(i)
  900. wsendctlmesg(w, Reshaped, i->r, i);
  901. }
  902. void
  903. move(void)
  904. {
  905. Window *w;
  906. Image *i;
  907. Rectangle r;
  908. w = pointto(FALSE);
  909. if(w == nil)
  910. return;
  911. i = drag(w, &r);
  912. if(i)
  913. wsendctlmesg(w, Moved, r, i);
  914. cornercursor(input, mouse->xy, 1);
  915. }
  916. int
  917. whide(Window *w)
  918. {
  919. Image *i;
  920. int j;
  921. for(j=0; j<nhidden; j++)
  922. if(hidden[j] == w) /* already hidden */
  923. return -1;
  924. i = allocimage(display, w->screenr, w->i->chan, 0, DWhite);
  925. if(i){
  926. hidden[nhidden++] = w;
  927. wsendctlmesg(w, Reshaped, ZR, i);
  928. return 1;
  929. }
  930. return 0;
  931. }
  932. int
  933. wunhide(int h)
  934. {
  935. Image *i;
  936. Window *w;
  937. w = hidden[h];
  938. i = allocwindow(wscreen, w->i->r, Refbackup, DWhite);
  939. if(i){
  940. --nhidden;
  941. memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*));
  942. wsendctlmesg(w, Reshaped, w->i->r, i);
  943. return 1;
  944. }
  945. return 0;
  946. }
  947. void
  948. hide(void)
  949. {
  950. Window *w;
  951. w = pointto(TRUE);
  952. if(w == nil)
  953. return;
  954. whide(w);
  955. }
  956. void
  957. unhide(int h)
  958. {
  959. Window *w;
  960. h -= Hidden;
  961. w = hidden[h];
  962. if(w == nil)
  963. return;
  964. wunhide(h);
  965. }
  966. Window*
  967. new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
  968. {
  969. Window *w;
  970. Mousectl *mc;
  971. Channel *cm, *ck, *cctl, *cpid;
  972. void **arg;
  973. if(i == nil)
  974. return nil;
  975. cm = chancreate(sizeof(Mouse), 0);
  976. ck = chancreate(sizeof(Rune*), 0);
  977. cctl = chancreate(sizeof(Wctlmesg), 4);
  978. cpid = chancreate(sizeof(int), 0);
  979. if(cm==nil || ck==nil || cctl==nil)
  980. error("new: channel alloc failed");
  981. mc = emalloc(sizeof(Mousectl));
  982. *mc = *mousectl;
  983. mc->image = i;
  984. mc->c = cm;
  985. w = wmk(i, mc, ck, cctl, scrollit);
  986. free(mc); /* wmk copies *mc */
  987. window = erealloc(window, ++nwindow*sizeof(Window*));
  988. window[nwindow-1] = w;
  989. if(hideit){
  990. hidden[nhidden++] = w;
  991. w->screenr = ZR;
  992. }
  993. threadcreate(winctl, w, 8192);
  994. if(!hideit)
  995. wcurrent(w);
  996. flushimage(display, 1);
  997. if(pid == 0){
  998. arg = emalloc(5*sizeof(void*));
  999. arg[0] = w;
  1000. arg[1] = cpid;
  1001. arg[2] = cmd;
  1002. if(argv == nil)
  1003. arg[3] = rcargv;
  1004. else
  1005. arg[3] = argv;
  1006. arg[4] = dir;
  1007. proccreate(winshell, arg, 8192);
  1008. pid = recvul(cpid);
  1009. free(arg);
  1010. }
  1011. if(pid == 0){
  1012. /* window creation failed */
  1013. wsendctlmesg(w, Deleted, ZR, nil);
  1014. chanfree(cpid);
  1015. return nil;
  1016. }
  1017. wsetpid(w, pid, 1);
  1018. wsetname(w);
  1019. if(dir)
  1020. w->dir = estrdup(dir);
  1021. chanfree(cpid);
  1022. return w;
  1023. }