rio.c 22 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  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. static Lock shutdownlk;
  279. killprocs();
  280. for(i=0; oknotes[i]; i++)
  281. if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){
  282. lock(&shutdownlk); /* only one can threadexitsall */
  283. threadexitsall(msg);
  284. }
  285. fprint(2, "rio %d: abort: %s\n", getpid(), msg);
  286. abort();
  287. exits(msg);
  288. return 0;
  289. }
  290. void
  291. killprocs(void)
  292. {
  293. int i;
  294. for(i=0; i<nwindow; i++)
  295. postnote(PNGROUP, window[i]->pid, "hangup");
  296. }
  297. void
  298. keyboardthread(void*)
  299. {
  300. Rune buf[2][20], *rp;
  301. int n, i;
  302. threadsetname("keyboardthread");
  303. n = 0;
  304. for(;;){
  305. rp = buf[n];
  306. n = 1-n;
  307. recv(keyboardctl->c, rp);
  308. for(i=1; i<nelem(buf[0])-1; i++)
  309. if(nbrecv(keyboardctl->c, rp+i) <= 0)
  310. break;
  311. rp[i] = L'\0';
  312. if(input != nil)
  313. sendp(input->ck, rp);
  314. }
  315. }
  316. /*
  317. * Used by /dev/kbdin
  318. */
  319. void
  320. keyboardsend(char *s, int cnt)
  321. {
  322. Rune *r;
  323. int i, nb, nr;
  324. r = runemalloc(cnt);
  325. /* BUGlet: partial runes will be converted to error runes */
  326. cvttorunes(s, cnt, r, &nb, &nr, nil);
  327. for(i=0; i<nr; i++)
  328. send(keyboardctl->c, &r[i]);
  329. free(r);
  330. }
  331. int
  332. portion(int x, int lo, int hi)
  333. {
  334. x -= lo;
  335. hi -= lo;
  336. if(x < 20)
  337. return 0;
  338. if(x > hi-20)
  339. return 2;
  340. return 1;
  341. }
  342. int
  343. whichcorner(Window *w, Point p)
  344. {
  345. int i, j;
  346. i = portion(p.x, w->screenr.min.x, w->screenr.max.x);
  347. j = portion(p.y, w->screenr.min.y, w->screenr.max.y);
  348. return 3*j+i;
  349. }
  350. void
  351. cornercursor(Window *w, Point p, int force)
  352. {
  353. if(w!=nil && winborder(w, p))
  354. riosetcursor(corners[whichcorner(w, p)], force);
  355. else
  356. wsetcursor(w, force);
  357. }
  358. /* thread to allow fsysproc to synchronize window closing with main proc */
  359. void
  360. winclosethread(void*)
  361. {
  362. Window *w;
  363. threadsetname("winclosethread");
  364. for(;;){
  365. w = recvp(winclosechan);
  366. wclose(w);
  367. }
  368. }
  369. /* thread to make Deleted windows that the client still holds disappear offscreen after an interval */
  370. void
  371. deletethread(void*)
  372. {
  373. char *s;
  374. Image *i;
  375. threadsetname("deletethread");
  376. for(;;){
  377. s = recvp(deletechan);
  378. i = namedimage(display, s);
  379. if(i != nil){
  380. /* move it off-screen to hide it, since client is slow in letting it go */
  381. originwindow(i, i->r.min, view->r.max);
  382. }
  383. freeimage(i);
  384. free(s);
  385. }
  386. }
  387. void
  388. deletetimeoutproc(void *v)
  389. {
  390. char *s;
  391. s = v;
  392. sleep(750); /* remove window from screen after 3/4 of a second */
  393. sendp(deletechan, s);
  394. }
  395. /*
  396. * Button 6 - keyboard toggle - has been pressed.
  397. * Send event to keyboard, wait for button up, send that.
  398. * Note: there is no coordinate translation done here; this
  399. * is just about getting button 6 to the keyboard simulator.
  400. */
  401. void
  402. keyboardhide(void)
  403. {
  404. send(wkeyboard->mc.c, mouse);
  405. do
  406. readmouse(mousectl);
  407. while(mouse->buttons & (1<<5));
  408. send(wkeyboard->mc.c, mouse);
  409. }
  410. void
  411. mousethread(void*)
  412. {
  413. int sending, inside, scrolling, moving, band;
  414. Window *oin, *w, *winput;
  415. Image *i;
  416. Rectangle r;
  417. Point xy;
  418. Mouse tmp;
  419. enum {
  420. MReshape,
  421. MMouse,
  422. NALT
  423. };
  424. static Alt alts[NALT+1];
  425. threadsetname("mousethread");
  426. sending = FALSE;
  427. scrolling = FALSE;
  428. moving = FALSE;
  429. alts[MReshape].c = mousectl->resizec;
  430. alts[MReshape].v = nil;
  431. alts[MReshape].op = CHANRCV;
  432. alts[MMouse].c = mousectl->c;
  433. alts[MMouse].v = &mousectl->Mouse;
  434. alts[MMouse].op = CHANRCV;
  435. alts[NALT].op = CHANEND;
  436. for(;;)
  437. switch(alt(alts)){
  438. case MReshape:
  439. resized();
  440. break;
  441. case MMouse:
  442. if(wkeyboard!=nil && (mouse->buttons & (1<<5))){
  443. keyboardhide();
  444. break;
  445. }
  446. Again:
  447. winput = input;
  448. /* override everything for the keyboard window */
  449. if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
  450. /* make sure it's on top; this call is free if it is */
  451. wtopme(wkeyboard);
  452. winput = wkeyboard;
  453. }
  454. if(winput!=nil && winput->i!=nil){
  455. /* convert to logical coordinates */
  456. xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
  457. xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
  458. /* the up and down scroll buttons are not subject to the usual rules */
  459. if((mouse->buttons&(8|16)) && !winput->mouseopen)
  460. goto Sending;
  461. inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder));
  462. if(winput->mouseopen)
  463. scrolling = FALSE;
  464. else if(scrolling)
  465. scrolling = mouse->buttons;
  466. else
  467. scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
  468. /* topped will be zero or less if window has been bottomed */
  469. if(sending == FALSE && !scrolling && winborder(winput, mouse->xy) && winput->topped>0){
  470. moving = TRUE;
  471. }else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
  472. sending = TRUE;
  473. }else
  474. sending = FALSE;
  475. if(sending){
  476. Sending:
  477. if(mouse->buttons == 0){
  478. cornercursor(winput, mouse->xy, 0);
  479. sending = FALSE;
  480. }else
  481. wsetcursor(winput, 0);
  482. tmp = mousectl->Mouse;
  483. tmp.xy = xy;
  484. send(winput->mc.c, &tmp);
  485. continue;
  486. }
  487. w = wpointto(mouse->xy);
  488. /* change cursor if over anyone's border */
  489. if(w != nil)
  490. cornercursor(w, mouse->xy, 0);
  491. else
  492. riosetcursor(nil, 0);
  493. if(moving && (mouse->buttons&7)){
  494. oin = winput;
  495. band = mouse->buttons & 3;
  496. sweeping = 1;
  497. if(band)
  498. i = bandsize(winput);
  499. else
  500. i = drag(winput, &r);
  501. sweeping = 0;
  502. if(i != nil){
  503. if(winput == oin){
  504. if(band)
  505. wsendctlmesg(winput, Reshaped, i->r, i);
  506. else
  507. wsendctlmesg(winput, Moved, r, i);
  508. cornercursor(winput, mouse->xy, 1);
  509. }else
  510. freeimage(i);
  511. }
  512. }
  513. if(w != nil)
  514. cornercursor(w, mouse->xy, 0);
  515. /* we're not sending the event, but if button is down maybe we should */
  516. if(mouse->buttons){
  517. /* w->topped will be zero or less if window has been bottomed */
  518. if(w==nil || (w==winput && w->topped>0)){
  519. if(mouse->buttons & 1){
  520. ;
  521. }else if(mouse->buttons & 2){
  522. if(winput && !winput->mouseopen)
  523. button2menu(winput);
  524. }else if(mouse->buttons & 4)
  525. button3menu();
  526. }else{
  527. /* if button 1 event in the window, top the window and wait for button up. */
  528. /* otherwise, top the window and pass the event on */
  529. if(wtop(mouse->xy) && (mouse->buttons!=1 || winborder(w, mouse->xy)))
  530. goto Again;
  531. goto Drain;
  532. }
  533. }
  534. moving = FALSE;
  535. break;
  536. Drain:
  537. do
  538. readmouse(mousectl);
  539. while(mousectl->buttons);
  540. moving = FALSE;
  541. goto Again; /* recalculate mouse position, cursor */
  542. }
  543. }
  544. void
  545. resized(void)
  546. {
  547. Image *im;
  548. int i, j, ishidden;
  549. Rectangle r;
  550. Point o, n;
  551. Window *w;
  552. if(getwindow(display, Refnone) < 0)
  553. error("failed to re-attach window");
  554. freescrtemps();
  555. view = screen;
  556. freescreen(wscreen);
  557. wscreen = allocscreen(screen, background, 0);
  558. if(wscreen == nil)
  559. error("can't re-allocate screen");
  560. draw(view, view->r, background, nil, ZP);
  561. o = subpt(viewr.max, viewr.min);
  562. n = subpt(view->clipr.max, view->clipr.min);
  563. for(i=0; i<nwindow; i++){
  564. w = window[i];
  565. if(w->deleted)
  566. continue;
  567. r = rectsubpt(w->i->r, viewr.min);
  568. r.min.x = (r.min.x*n.x)/o.x;
  569. r.min.y = (r.min.y*n.y)/o.y;
  570. r.max.x = (r.max.x*n.x)/o.x;
  571. r.max.y = (r.max.y*n.y)/o.y;
  572. r = rectaddpt(r, screen->clipr.min);
  573. ishidden = 0;
  574. for(j=0; j<nhidden; j++)
  575. if(w == hidden[j]){
  576. ishidden = 1;
  577. break;
  578. }
  579. if(ishidden)
  580. im = allocimage(display, r, screen->chan, 0, DWhite);
  581. else
  582. im = allocwindow(wscreen, r, Refbackup, DWhite);
  583. if(im)
  584. wsendctlmesg(w, Reshaped, r, im);
  585. }
  586. viewr = screen->r;
  587. flushimage(display, 1);
  588. }
  589. void
  590. button3menu(void)
  591. {
  592. int i;
  593. for(i=0; i<nhidden; i++)
  594. menu3str[i+Hidden] = hidden[i]->label;
  595. menu3str[i+Hidden] = nil;
  596. sweeping = 1;
  597. switch(i = menuhit(3, mousectl, &menu3, wscreen)){
  598. case -1:
  599. break;
  600. case New:
  601. new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
  602. break;
  603. case Reshape:
  604. resize();
  605. break;
  606. case Move:
  607. move();
  608. break;
  609. case Delete:
  610. delete();
  611. break;
  612. case Hide:
  613. hide();
  614. break;
  615. case Exit:
  616. if(Hidden > Exit){
  617. send(exitchan, nil);
  618. break;
  619. }
  620. /* else fall through */
  621. default:
  622. unhide(i);
  623. break;
  624. }
  625. sweeping = 0;
  626. }
  627. void
  628. button2menu(Window *w)
  629. {
  630. if(w->deleted)
  631. return;
  632. incref(w);
  633. if(w->scrolling)
  634. menu2str[Scroll] = "noscroll";
  635. else
  636. menu2str[Scroll] = "scroll";
  637. switch(menuhit(2, mousectl, &menu2, wscreen)){
  638. case Cut:
  639. wsnarf(w);
  640. wcut(w);
  641. wscrdraw(w);
  642. break;
  643. case Snarf:
  644. wsnarf(w);
  645. break;
  646. case Paste:
  647. getsnarf();
  648. wpaste(w);
  649. wscrdraw(w);
  650. break;
  651. case Plumb:
  652. wplumb(w);
  653. break;
  654. case Send:
  655. getsnarf();
  656. wsnarf(w);
  657. if(nsnarf == 0)
  658. break;
  659. if(w->rawing){
  660. waddraw(w, snarf, nsnarf);
  661. if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
  662. waddraw(w, L"\n", 1);
  663. }else{
  664. winsert(w, snarf, nsnarf, w->nr);
  665. if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
  666. winsert(w, L"\n", 1, w->nr);
  667. }
  668. wsetselect(w, w->nr, w->nr);
  669. wshow(w, w->nr);
  670. break;
  671. case Scroll:
  672. if(w->scrolling ^= 1)
  673. wshow(w, w->nr);
  674. break;
  675. }
  676. wclose(w);
  677. wsendctlmesg(w, Wakeup, ZR, nil);
  678. flushimage(display, 1);
  679. }
  680. Point
  681. onscreen(Point p)
  682. {
  683. p.x = max(screen->clipr.min.x, p.x);
  684. p.x = min(screen->clipr.max.x, p.x);
  685. p.y = max(screen->clipr.min.y, p.y);
  686. p.y = min(screen->clipr.max.y, p.y);
  687. return p;
  688. }
  689. Image*
  690. sweep(void)
  691. {
  692. Image *i, *oi;
  693. Rectangle r;
  694. Point p0, p;
  695. i = nil;
  696. menuing = TRUE;
  697. riosetcursor(&crosscursor, 1);
  698. while(mouse->buttons == 0)
  699. readmouse(mousectl);
  700. p0 = onscreen(mouse->xy);
  701. p = p0;
  702. r.min = p;
  703. r.max = p;
  704. oi = nil;
  705. while(mouse->buttons == 4){
  706. readmouse(mousectl);
  707. if(mouse->buttons != 4 && mouse->buttons != 0)
  708. break;
  709. if(!eqpt(mouse->xy, p)){
  710. p = onscreen(mouse->xy);
  711. r = canonrect(Rpt(p0, p));
  712. if(Dx(r)>5 && Dy(r)>5){
  713. i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
  714. freeimage(oi);
  715. if(i == nil)
  716. goto Rescue;
  717. oi = i;
  718. border(i, r, Selborder, red, ZP);
  719. flushimage(display, 1);
  720. }
  721. }
  722. }
  723. if(mouse->buttons != 0)
  724. goto Rescue;
  725. if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height)
  726. goto Rescue;
  727. oi = i;
  728. i = allocwindow(wscreen, oi->r, Refbackup, DWhite);
  729. freeimage(oi);
  730. if(i == nil)
  731. goto Rescue;
  732. border(i, r, Selborder, red, ZP);
  733. cornercursor(input, mouse->xy, 1);
  734. goto Return;
  735. Rescue:
  736. freeimage(i);
  737. i = nil;
  738. cornercursor(input, mouse->xy, 1);
  739. while(mouse->buttons)
  740. readmouse(mousectl);
  741. Return:
  742. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  743. menuing = FALSE;
  744. return i;
  745. }
  746. void
  747. drawedge(Image **bp, Rectangle r)
  748. {
  749. Image *b = *bp;
  750. if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
  751. originwindow(b, r.min, r.min);
  752. else{
  753. freeimage(b);
  754. *bp = allocwindow(wscreen, r, Refbackup, DRed);
  755. }
  756. }
  757. void
  758. drawborder(Rectangle r, int show)
  759. {
  760. static Image *b[4];
  761. int i;
  762. if(show == 0){
  763. for(i = 0; i < 4; i++){
  764. freeimage(b[i]);
  765. b[i] = nil;
  766. }
  767. }else{
  768. r = canonrect(r);
  769. drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
  770. drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
  771. drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
  772. drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
  773. }
  774. }
  775. Image*
  776. drag(Window *w, Rectangle *rp)
  777. {
  778. Image *i, *ni;
  779. Point p, op, d, dm, om;
  780. Rectangle r;
  781. i = w->i;
  782. menuing = TRUE;
  783. om = mouse->xy;
  784. riosetcursor(&boxcursor, 1);
  785. dm = subpt(mouse->xy, w->screenr.min);
  786. d = subpt(i->r.max, i->r.min);
  787. op = subpt(mouse->xy, dm);
  788. drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
  789. flushimage(display, 1);
  790. while(mouse->buttons == 4){
  791. p = subpt(mouse->xy, dm);
  792. if(!eqpt(p, op)){
  793. drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
  794. flushimage(display, 1);
  795. op = p;
  796. }
  797. readmouse(mousectl);
  798. }
  799. r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
  800. drawborder(r, 0);
  801. cornercursor(w, mouse->xy, 1);
  802. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  803. menuing = FALSE;
  804. flushimage(display, 1);
  805. if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){
  806. moveto(mousectl, om);
  807. while(mouse->buttons)
  808. readmouse(mousectl);
  809. *rp = Rect(0, 0, 0, 0);
  810. return nil;
  811. }
  812. draw(ni, ni->r, i, nil, i->r.min);
  813. *rp = r;
  814. return ni;
  815. }
  816. Point
  817. cornerpt(Rectangle r, Point p, int which)
  818. {
  819. switch(which){
  820. case 0: /* top left */
  821. p = Pt(r.min.x, r.min.y);
  822. break;
  823. case 2: /* top right */
  824. p = Pt(r.max.x,r.min.y);
  825. break;
  826. case 6: /* bottom left */
  827. p = Pt(r.min.x, r.max.y);
  828. break;
  829. case 8: /* bottom right */
  830. p = Pt(r.max.x, r.max.y);
  831. break;
  832. case 1: /* top edge */
  833. p = Pt(p.x,r.min.y);
  834. break;
  835. case 5: /* right edge */
  836. p = Pt(r.max.x, p.y);
  837. break;
  838. case 7: /* bottom edge */
  839. p = Pt(p.x, r.max.y);
  840. break;
  841. case 3: /* left edge */
  842. p = Pt(r.min.x, p.y);
  843. break;
  844. }
  845. return p;
  846. }
  847. Rectangle
  848. whichrect(Rectangle r, Point p, int which)
  849. {
  850. switch(which){
  851. case 0: /* top left */
  852. r = Rect(p.x, p.y, r.max.x, r.max.y);
  853. break;
  854. case 2: /* top right */
  855. r = Rect(r.min.x, p.y, p.x, r.max.y);
  856. break;
  857. case 6: /* bottom left */
  858. r = Rect(p.x, r.min.y, r.max.x, p.y);
  859. break;
  860. case 8: /* bottom right */
  861. r = Rect(r.min.x, r.min.y, p.x, p.y);
  862. break;
  863. case 1: /* top edge */
  864. r = Rect(r.min.x, p.y, r.max.x, r.max.y);
  865. break;
  866. case 5: /* right edge */
  867. r = Rect(r.min.x, r.min.y, p.x, r.max.y);
  868. break;
  869. case 7: /* bottom edge */
  870. r = Rect(r.min.x, r.min.y, r.max.x, p.y);
  871. break;
  872. case 3: /* left edge */
  873. r = Rect(p.x, r.min.y, r.max.x, r.max.y);
  874. break;
  875. }
  876. return canonrect(r);
  877. }
  878. Image*
  879. bandsize(Window *w)
  880. {
  881. Image *i;
  882. Rectangle r, or;
  883. Point p, startp;
  884. int which, but;
  885. p = mouse->xy;
  886. but = mouse->buttons;
  887. which = whichcorner(w, p);
  888. p = cornerpt(w->screenr, p, which);
  889. wmovemouse(w, p);
  890. readmouse(mousectl);
  891. r = whichrect(w->screenr, p, which);
  892. drawborder(r, 1);
  893. or = r;
  894. startp = p;
  895. while(mouse->buttons == but){
  896. p = onscreen(mouse->xy);
  897. r = whichrect(w->screenr, p, which);
  898. if(!eqrect(r, or) && goodrect(r)){
  899. drawborder(r, 1);
  900. flushimage(display, 1);
  901. or = r;
  902. }
  903. readmouse(mousectl);
  904. }
  905. p = mouse->xy;
  906. drawborder(or, 0);
  907. flushimage(display, 1);
  908. wsetcursor(w, 1);
  909. if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){
  910. while(mouse->buttons)
  911. readmouse(mousectl);
  912. return nil;
  913. }
  914. if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1)
  915. return nil;
  916. i = allocwindow(wscreen, or, Refbackup, DWhite);
  917. if(i == nil)
  918. return nil;
  919. border(i, r, Selborder, red, ZP);
  920. return i;
  921. }
  922. Window*
  923. pointto(int wait)
  924. {
  925. Window *w;
  926. menuing = TRUE;
  927. riosetcursor(&sightcursor, 1);
  928. while(mouse->buttons == 0)
  929. readmouse(mousectl);
  930. if(mouse->buttons == 4)
  931. w = wpointto(mouse->xy);
  932. else
  933. w = nil;
  934. if(wait){
  935. while(mouse->buttons){
  936. if(mouse->buttons!=4 && w !=nil){ /* cancel */
  937. cornercursor(input, mouse->xy, 0);
  938. w = nil;
  939. }
  940. readmouse(mousectl);
  941. }
  942. if(w != nil && wpointto(mouse->xy) != w)
  943. w = nil;
  944. }
  945. cornercursor(input, mouse->xy, 0);
  946. moveto(mousectl, mouse->xy); /* force cursor update; ugly */
  947. menuing = FALSE;
  948. return w;
  949. }
  950. void
  951. delete(void)
  952. {
  953. Window *w;
  954. w = pointto(TRUE);
  955. if(w)
  956. wsendctlmesg(w, Deleted, ZR, nil);
  957. }
  958. void
  959. resize(void)
  960. {
  961. Window *w;
  962. Image *i;
  963. w = pointto(TRUE);
  964. if(w == nil)
  965. return;
  966. i = sweep();
  967. if(i)
  968. wsendctlmesg(w, Reshaped, i->r, i);
  969. }
  970. void
  971. move(void)
  972. {
  973. Window *w;
  974. Image *i;
  975. Rectangle r;
  976. w = pointto(FALSE);
  977. if(w == nil)
  978. return;
  979. i = drag(w, &r);
  980. if(i)
  981. wsendctlmesg(w, Moved, r, i);
  982. cornercursor(input, mouse->xy, 1);
  983. }
  984. int
  985. whide(Window *w)
  986. {
  987. Image *i;
  988. int j;
  989. for(j=0; j<nhidden; j++)
  990. if(hidden[j] == w) /* already hidden */
  991. return -1;
  992. i = allocimage(display, w->screenr, w->i->chan, 0, DWhite);
  993. if(i){
  994. hidden[nhidden++] = w;
  995. wsendctlmesg(w, Reshaped, ZR, i);
  996. return 1;
  997. }
  998. return 0;
  999. }
  1000. int
  1001. wunhide(int h)
  1002. {
  1003. Image *i;
  1004. Window *w;
  1005. w = hidden[h];
  1006. i = allocwindow(wscreen, w->i->r, Refbackup, DWhite);
  1007. if(i){
  1008. --nhidden;
  1009. memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*));
  1010. wsendctlmesg(w, Reshaped, w->i->r, i);
  1011. return 1;
  1012. }
  1013. return 0;
  1014. }
  1015. void
  1016. hide(void)
  1017. {
  1018. Window *w;
  1019. w = pointto(TRUE);
  1020. if(w == nil)
  1021. return;
  1022. whide(w);
  1023. }
  1024. void
  1025. unhide(int h)
  1026. {
  1027. Window *w;
  1028. h -= Hidden;
  1029. w = hidden[h];
  1030. if(w == nil)
  1031. return;
  1032. wunhide(h);
  1033. }
  1034. Window*
  1035. new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
  1036. {
  1037. Window *w;
  1038. Mousectl *mc;
  1039. Channel *cm, *ck, *cctl, *cpid;
  1040. void **arg;
  1041. if(i == nil)
  1042. return nil;
  1043. cm = chancreate(sizeof(Mouse), 0);
  1044. ck = chancreate(sizeof(Rune*), 0);
  1045. cctl = chancreate(sizeof(Wctlmesg), 4);
  1046. cpid = chancreate(sizeof(int), 0);
  1047. if(cm==nil || ck==nil || cctl==nil)
  1048. error("new: channel alloc failed");
  1049. mc = emalloc(sizeof(Mousectl));
  1050. *mc = *mousectl;
  1051. mc->image = i;
  1052. mc->c = cm;
  1053. w = wmk(i, mc, ck, cctl, scrollit);
  1054. free(mc); /* wmk copies *mc */
  1055. window = erealloc(window, ++nwindow*sizeof(Window*));
  1056. window[nwindow-1] = w;
  1057. if(hideit){
  1058. hidden[nhidden++] = w;
  1059. w->screenr = ZR;
  1060. }
  1061. threadcreate(winctl, w, 8192);
  1062. if(!hideit)
  1063. wcurrent(w);
  1064. flushimage(display, 1);
  1065. if(pid == 0){
  1066. arg = emalloc(5*sizeof(void*));
  1067. arg[0] = w;
  1068. arg[1] = cpid;
  1069. arg[2] = cmd;
  1070. if(argv == nil)
  1071. arg[3] = rcargv;
  1072. else
  1073. arg[3] = argv;
  1074. arg[4] = dir;
  1075. proccreate(winshell, arg, 8192);
  1076. pid = recvul(cpid);
  1077. free(arg);
  1078. }
  1079. if(pid == 0){
  1080. /* window creation failed */
  1081. wsendctlmesg(w, Deleted, ZR, nil);
  1082. chanfree(cpid);
  1083. return nil;
  1084. }
  1085. wsetpid(w, pid, 1);
  1086. wsetname(w);
  1087. if(dir)
  1088. w->dir = estrdup(dir);
  1089. chanfree(cpid);
  1090. return w;
  1091. }