rio.c 22 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  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. }
  586. void
  587. button3menu(void)
  588. {
  589. int i;
  590. for(i=0; i<nhidden; i++)
  591. menu3str[i+Hidden] = hidden[i]->label;
  592. menu3str[i+Hidden] = nil;
  593. sweeping = 1;
  594. switch(i = menuhit(3, mousectl, &menu3, wscreen)){
  595. case -1:
  596. break;
  597. case New:
  598. new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
  599. break;
  600. case Reshape:
  601. resize();
  602. break;
  603. case Move:
  604. move();
  605. break;
  606. case Delete:
  607. delete();
  608. break;
  609. case Hide:
  610. hide();
  611. break;
  612. case Exit:
  613. if(Hidden > Exit){
  614. send(exitchan, nil);
  615. break;
  616. }
  617. /* else fall through */
  618. default:
  619. unhide(i);
  620. break;
  621. }
  622. sweeping = 0;
  623. }
  624. void
  625. button2menu(Window *w)
  626. {
  627. if(w->deleted)
  628. return;
  629. incref(w);
  630. if(w->scrolling)
  631. menu2str[Scroll] = "noscroll";
  632. else
  633. menu2str[Scroll] = "scroll";
  634. switch(menuhit(2, mousectl, &menu2, wscreen)){
  635. case Cut:
  636. wsnarf(w);
  637. wcut(w);
  638. wscrdraw(w);
  639. break;
  640. case Snarf:
  641. wsnarf(w);
  642. break;
  643. case Paste:
  644. getsnarf();
  645. wpaste(w);
  646. wscrdraw(w);
  647. break;
  648. case Plumb:
  649. wplumb(w);
  650. break;
  651. case Send:
  652. getsnarf();
  653. wsnarf(w);
  654. if(nsnarf == 0)
  655. break;
  656. if(w->rawing){
  657. waddraw(w, snarf, nsnarf);
  658. if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
  659. waddraw(w, L"\n", 1);
  660. }else{
  661. winsert(w, snarf, nsnarf, w->nr);
  662. if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
  663. winsert(w, L"\n", 1, w->nr);
  664. }
  665. wsetselect(w, w->nr, w->nr);
  666. wshow(w, w->nr);
  667. break;
  668. case Scroll:
  669. if(w->scrolling ^= 1)
  670. wshow(w, w->nr);
  671. break;
  672. }
  673. wclose(w);
  674. wsendctlmesg(w, Wakeup, ZR, nil);
  675. flushimage(display, 1);
  676. }
  677. Point
  678. onscreen(Point p)
  679. {
  680. p.x = max(screen->clipr.min.x, p.x);
  681. p.x = min(screen->clipr.max.x, p.x);
  682. p.y = max(screen->clipr.min.y, p.y);
  683. p.y = min(screen->clipr.max.y, p.y);
  684. return p;
  685. }
  686. Image*
  687. sweep(void)
  688. {
  689. Image *i, *oi;
  690. Rectangle r;
  691. Point p0, p;
  692. i = nil;
  693. menuing = TRUE;
  694. riosetcursor(&crosscursor, 1);
  695. while(mouse->buttons == 0)
  696. readmouse(mousectl);
  697. p0 = onscreen(mouse->xy);
  698. p = p0;
  699. r.min = p;
  700. r.max = p;
  701. oi = nil;
  702. while(mouse->buttons == 4){
  703. readmouse(mousectl);
  704. if(mouse->buttons != 4 && mouse->buttons != 0)
  705. break;
  706. if(!eqpt(mouse->xy, p)){
  707. p = onscreen(mouse->xy);
  708. r = canonrect(Rpt(p0, p));
  709. if(Dx(r)>5 && Dy(r)>5){
  710. i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
  711. freeimage(oi);
  712. if(i == nil)
  713. goto Rescue;
  714. oi = i;
  715. border(i, r, Selborder, red, ZP);
  716. flushimage(display, 1);
  717. }
  718. }
  719. }
  720. if(mouse->buttons != 0)
  721. goto Rescue;
  722. if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height)
  723. goto Rescue;
  724. oi = i;
  725. i = allocwindow(wscreen, oi->r, Refbackup, DWhite);
  726. freeimage(oi);
  727. if(i == nil)
  728. goto Rescue;
  729. border(i, r, Selborder, red, ZP);
  730. cornercursor(input, mouse->xy, 1);
  731. goto Return;
  732. Rescue:
  733. freeimage(i);
  734. i = nil;
  735. cornercursor(input, mouse->xy, 1);
  736. while(mouse->buttons)
  737. readmouse(mousectl);
  738. Return:
  739. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  740. menuing = FALSE;
  741. return i;
  742. }
  743. void
  744. drawedge(Image **bp, Rectangle r)
  745. {
  746. Image *b = *bp;
  747. if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
  748. originwindow(b, r.min, r.min);
  749. else{
  750. freeimage(b);
  751. *bp = allocwindow(wscreen, r, Refbackup, DRed);
  752. }
  753. }
  754. void
  755. drawborder(Rectangle r, int show)
  756. {
  757. static Image *b[4];
  758. int i;
  759. if(show == 0){
  760. for(i = 0; i < 4; i++){
  761. freeimage(b[i]);
  762. b[i] = nil;
  763. }
  764. }else{
  765. r = canonrect(r);
  766. drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
  767. drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
  768. drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
  769. drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
  770. }
  771. }
  772. Image*
  773. drag(Window *w, Rectangle *rp)
  774. {
  775. Image *i, *ni;
  776. Point p, op, d, dm, om;
  777. Rectangle r;
  778. i = w->i;
  779. menuing = TRUE;
  780. om = mouse->xy;
  781. riosetcursor(&boxcursor, 1);
  782. dm = subpt(mouse->xy, w->screenr.min);
  783. d = subpt(i->r.max, i->r.min);
  784. op = subpt(mouse->xy, dm);
  785. drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
  786. flushimage(display, 1);
  787. while(mouse->buttons == 4){
  788. p = subpt(mouse->xy, dm);
  789. if(!eqpt(p, op)){
  790. drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
  791. flushimage(display, 1);
  792. op = p;
  793. }
  794. readmouse(mousectl);
  795. }
  796. r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
  797. drawborder(r, 0);
  798. cornercursor(w, mouse->xy, 1);
  799. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  800. menuing = FALSE;
  801. flushimage(display, 1);
  802. if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){
  803. moveto(mousectl, om);
  804. while(mouse->buttons)
  805. readmouse(mousectl);
  806. *rp = Rect(0, 0, 0, 0);
  807. return nil;
  808. }
  809. draw(ni, ni->r, i, nil, i->r.min);
  810. *rp = r;
  811. return ni;
  812. }
  813. Rectangle
  814. whichrect(Rectangle r, Point p, int which)
  815. {
  816. switch(which){
  817. case 0: /* top left */
  818. r = Rect(p.x, p.y, r.max.x, r.max.y);
  819. break;
  820. case 2: /* top right */
  821. r = Rect(r.min.x, p.y, p.x, r.max.y);
  822. break;
  823. case 6: /* bottom left */
  824. r = Rect(p.x, r.min.y, r.max.x, p.y);
  825. break;
  826. case 8: /* bottom right */
  827. r = Rect(r.min.x, r.min.y, p.x, p.y);
  828. break;
  829. case 1: /* top edge */
  830. r = Rect(r.min.x, p.y, r.max.x, r.max.y);
  831. break;
  832. case 5: /* right edge */
  833. r = Rect(r.min.x, r.min.y, p.x, r.max.y);
  834. break;
  835. case 7: /* bottom edge */
  836. r = Rect(r.min.x, r.min.y, r.max.x, p.y);
  837. break;
  838. case 3: /* left edge */
  839. r = Rect(p.x, r.min.y, r.max.x, r.max.y);
  840. break;
  841. }
  842. return canonrect(r);
  843. }
  844. Image*
  845. bandsize(Window *w)
  846. {
  847. Image *i;
  848. Rectangle r, or;
  849. Point p, startp;
  850. int which, but;
  851. p = mouse->xy;
  852. startp = p;
  853. which = whichcorner(w, p);
  854. r = whichrect(w->screenr, p, which);
  855. drawborder(r, 1);
  856. or = r;
  857. but = mouse->buttons;
  858. while(mouse->buttons == but){
  859. p = onscreen(mouse->xy);
  860. r = whichrect(w->screenr, p, which);
  861. if(!eqrect(r, or) && goodrect(r)){
  862. drawborder(r, 1);
  863. flushimage(display, 1);
  864. or = r;
  865. }
  866. readmouse(mousectl);
  867. }
  868. p = mouse->xy;
  869. drawborder(or, 0);
  870. flushimage(display, 1);
  871. wsetcursor(w, 1);
  872. if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){
  873. while(mouse->buttons)
  874. readmouse(mousectl);
  875. return nil;
  876. }
  877. if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1)
  878. return nil;
  879. i = allocwindow(wscreen, or, Refbackup, DWhite);
  880. if(i == nil)
  881. return nil;
  882. border(i, r, Selborder, red, ZP);
  883. return i;
  884. }
  885. Window*
  886. pointto(int wait)
  887. {
  888. Window *w;
  889. menuing = TRUE;
  890. riosetcursor(&sightcursor, 1);
  891. while(mouse->buttons == 0)
  892. readmouse(mousectl);
  893. if(mouse->buttons == 4)
  894. w = wpointto(mouse->xy);
  895. else
  896. w = nil;
  897. if(wait)
  898. while(mouse->buttons){
  899. if(mouse->buttons!=4 && w !=nil){ /* cancel */
  900. cornercursor(input, mouse->xy, 0);
  901. w = nil;
  902. }
  903. readmouse(mousectl);
  904. }
  905. cornercursor(input, mouse->xy, 0);
  906. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  907. menuing = FALSE;
  908. return w;
  909. }
  910. void
  911. delete(void)
  912. {
  913. Window *w;
  914. w = pointto(TRUE);
  915. if(w)
  916. wsendctlmesg(w, Deleted, ZR, nil);
  917. }
  918. void
  919. resize(void)
  920. {
  921. Window *w;
  922. Image *i;
  923. w = pointto(TRUE);
  924. if(w == nil)
  925. return;
  926. i = sweep();
  927. if(i)
  928. wsendctlmesg(w, Reshaped, i->r, i);
  929. }
  930. void
  931. move(void)
  932. {
  933. Window *w;
  934. Image *i;
  935. Rectangle r;
  936. w = pointto(FALSE);
  937. if(w == nil)
  938. return;
  939. i = drag(w, &r);
  940. if(i)
  941. wsendctlmesg(w, Moved, r, i);
  942. cornercursor(input, mouse->xy, 1);
  943. }
  944. int
  945. whide(Window *w)
  946. {
  947. Image *i;
  948. int j;
  949. for(j=0; j<nhidden; j++)
  950. if(hidden[j] == w) /* already hidden */
  951. return -1;
  952. i = allocimage(display, w->screenr, w->i->chan, 0, DWhite);
  953. if(i){
  954. hidden[nhidden++] = w;
  955. wsendctlmesg(w, Reshaped, ZR, i);
  956. return 1;
  957. }
  958. return 0;
  959. }
  960. int
  961. wunhide(int h)
  962. {
  963. Image *i;
  964. Window *w;
  965. w = hidden[h];
  966. i = allocwindow(wscreen, w->i->r, Refbackup, DWhite);
  967. if(i){
  968. --nhidden;
  969. memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*));
  970. wsendctlmesg(w, Reshaped, w->i->r, i);
  971. return 1;
  972. }
  973. return 0;
  974. }
  975. void
  976. hide(void)
  977. {
  978. Window *w;
  979. w = pointto(TRUE);
  980. if(w == nil)
  981. return;
  982. whide(w);
  983. }
  984. void
  985. unhide(int h)
  986. {
  987. Window *w;
  988. h -= Hidden;
  989. w = hidden[h];
  990. if(w == nil)
  991. return;
  992. wunhide(h);
  993. }
  994. Window*
  995. new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
  996. {
  997. Window *w;
  998. Mousectl *mc;
  999. Channel *cm, *ck, *cctl, *cpid;
  1000. void **arg;
  1001. if(i == nil)
  1002. return nil;
  1003. cm = chancreate(sizeof(Mouse), 0);
  1004. ck = chancreate(sizeof(Rune*), 0);
  1005. cctl = chancreate(sizeof(Wctlmesg), 4);
  1006. cpid = chancreate(sizeof(int), 0);
  1007. if(cm==nil || ck==nil || cctl==nil)
  1008. error("new: channel alloc failed");
  1009. mc = emalloc(sizeof(Mousectl));
  1010. *mc = *mousectl;
  1011. mc->image = i;
  1012. mc->c = cm;
  1013. w = wmk(i, mc, ck, cctl, scrollit);
  1014. free(mc); /* wmk copies *mc */
  1015. window = erealloc(window, ++nwindow*sizeof(Window*));
  1016. window[nwindow-1] = w;
  1017. if(hideit){
  1018. hidden[nhidden++] = w;
  1019. w->screenr = ZR;
  1020. }
  1021. threadcreate(winctl, w, 8192);
  1022. if(!hideit)
  1023. wcurrent(w);
  1024. flushimage(display, 1);
  1025. if(pid == 0){
  1026. arg = emalloc(5*sizeof(void*));
  1027. arg[0] = w;
  1028. arg[1] = cpid;
  1029. arg[2] = cmd;
  1030. if(argv == nil)
  1031. arg[3] = rcargv;
  1032. else
  1033. arg[3] = argv;
  1034. arg[4] = dir;
  1035. proccreate(winshell, arg, 8192);
  1036. pid = recvul(cpid);
  1037. free(arg);
  1038. }
  1039. if(pid == 0){
  1040. /* window creation failed */
  1041. wsendctlmesg(w, Deleted, ZR, nil);
  1042. chanfree(cpid);
  1043. return nil;
  1044. }
  1045. wsetpid(w, pid, 1);
  1046. wsetname(w);
  1047. if(dir)
  1048. w->dir = estrdup(dir);
  1049. chanfree(cpid);
  1050. return w;
  1051. }