view.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062
  1. /*
  2. * the actual viewer that handles screen stuff
  3. */
  4. #include <u.h>
  5. #include <libc.h>
  6. #include <draw.h>
  7. #include <cursor.h>
  8. #include <event.h>
  9. #include <bio.h>
  10. #include <plumb.h>
  11. #include <ctype.h>
  12. #include <keyboard.h>
  13. #include "page.h"
  14. Document *doc;
  15. Image *im;
  16. Image *tofree;
  17. int page;
  18. int angle = 0;
  19. int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */
  20. Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */
  21. Point ul; /* the upper left corner of the image is at this point on the screen */
  22. Point pclip(Point, Rectangle);
  23. Rectangle mkrange(Rectangle screenr, Rectangle imr);
  24. void redraw(Image*);
  25. Cursor reading={
  26. {-1, -1},
  27. {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
  28. 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
  29. 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
  30. 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
  31. {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
  32. 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
  33. 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
  34. 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
  35. };
  36. Cursor query = {
  37. {-7,-7},
  38. {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
  39. 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
  40. 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
  41. 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
  42. {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
  43. 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
  44. 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
  45. 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
  46. };
  47. enum {
  48. Left = 1,
  49. Middle = 2,
  50. Right = 4,
  51. RMenu = 3,
  52. };
  53. static void
  54. delayfreeimage(Image *m)
  55. {
  56. if(m == tofree)
  57. return;
  58. if(tofree)
  59. freeimage(tofree);
  60. tofree = m;
  61. }
  62. void
  63. unhide(void)
  64. {
  65. static int wctl = -1;
  66. if(wctl < 0)
  67. wctl = open("/dev/wctl", OWRITE);
  68. if(wctl < 0)
  69. return;
  70. write(wctl, "unhide", 6);
  71. }
  72. int
  73. max(int a, int b)
  74. {
  75. return a > b ? a : b;
  76. }
  77. int
  78. min(int a, int b)
  79. {
  80. return a < b ? a : b;
  81. }
  82. char*
  83. menugen(int n)
  84. {
  85. static char menustr[32];
  86. char *p;
  87. int len;
  88. if(n == doc->npage)
  89. return "exit";
  90. if(n > doc->npage)
  91. return nil;
  92. if(reverse)
  93. n = doc->npage-1-n;
  94. p = doc->pagename(doc, n);
  95. len = (sizeof menustr)-2;
  96. if(strlen(p) > len && strrchr(p, '/'))
  97. p = strrchr(p, '/')+1;
  98. if(strlen(p) > len)
  99. p = p+strlen(p)-len;
  100. strcpy(menustr+1, p);
  101. if(page == n)
  102. menustr[0] = '>';
  103. else
  104. menustr[0] = ' ';
  105. return menustr;
  106. }
  107. void
  108. showpage(int page, Menu *m)
  109. {
  110. if(doc->fwdonly)
  111. m->lasthit = 0; /* this page */
  112. else
  113. m->lasthit = reverse ? doc->npage-1-page : page;
  114. esetcursor(&reading);
  115. delayfreeimage(nil);
  116. im = cachedpage(doc, angle, page);
  117. if(im == nil)
  118. wexits(0);
  119. if(resizing)
  120. resize(Dx(im->r), Dy(im->r));
  121. esetcursor(nil);
  122. if(showbottom){
  123. ul.y = screen->r.max.y - Dy(im->r);
  124. showbottom = 0;
  125. }
  126. redraw(screen);
  127. flushimage(display, 1);
  128. }
  129. char*
  130. writebitmap(void)
  131. {
  132. char basename[64];
  133. char name[64+30];
  134. static char result[200];
  135. char *p, *q;
  136. int fd;
  137. if(im == nil)
  138. return "no image";
  139. memset(basename, 0, sizeof basename);
  140. if(doc->docname)
  141. strncpy(basename, doc->docname, sizeof(basename)-1);
  142. else if((p = menugen(page)) && p[0] != '\0')
  143. strncpy(basename, p+1, sizeof(basename)-1);
  144. if(basename[0]) {
  145. if(q = strrchr(basename, '/'))
  146. q++;
  147. else
  148. q = basename;
  149. if(p = strchr(q, '.'))
  150. *p = 0;
  151. memset(name, 0, sizeof name);
  152. snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
  153. if(access(name, 0) >= 0) {
  154. strcat(name, "XXXX");
  155. mktemp(name);
  156. }
  157. if(access(name, 0) >= 0)
  158. return "couldn't think of a name for bitmap";
  159. } else {
  160. strcpy(name, "bitXXXX");
  161. mktemp(name);
  162. if(access(name, 0) >= 0)
  163. return "couldn't think of a name for bitmap";
  164. }
  165. if((fd = create(name, OWRITE, 0666)) < 0) {
  166. snprint(result, sizeof result, "cannot create %s: %r", name);
  167. return result;
  168. }
  169. if(writeimage(fd, im, 0) < 0) {
  170. snprint(result, sizeof result, "cannot writeimage: %r");
  171. close(fd);
  172. return result;
  173. }
  174. close(fd);
  175. snprint(result, sizeof result, "wrote %s", name);
  176. return result;
  177. }
  178. static void translate(Point);
  179. static int
  180. showdata(Plumbmsg *msg)
  181. {
  182. char *s;
  183. s = plumblookup(msg->attr, "action");
  184. return s && strcmp(s, "showdata")==0;
  185. }
  186. /* correspond to entries in miditems[] below,
  187. * changing one means you need to change
  188. */
  189. enum{
  190. Restore = 0,
  191. Zin,
  192. Fit,
  193. Rot,
  194. Upside,
  195. Empty1,
  196. Next,
  197. Prev,
  198. Zerox,
  199. Empty2,
  200. Reverse,
  201. Del,
  202. Write,
  203. Empty3,
  204. Exit,
  205. };
  206. void
  207. viewer(Document *dd)
  208. {
  209. int i, fd, n, oldpage;
  210. int nxt;
  211. Menu menu, midmenu;
  212. Mouse m;
  213. Event e;
  214. Point dxy, oxy, xy0;
  215. Rectangle r;
  216. Image *tmp;
  217. static char *fwditems[] = { "this page", "next page", "exit", 0 };
  218. static char *miditems[] = {
  219. "orig size",
  220. "zoom in",
  221. "fit window",
  222. "rotate 90",
  223. "upside down",
  224. "",
  225. "next",
  226. "prev",
  227. "zerox",
  228. "",
  229. "reverse",
  230. "discard",
  231. "write",
  232. "",
  233. "quit",
  234. 0
  235. };
  236. char *s;
  237. enum { Eplumb = 4 };
  238. Plumbmsg *pm;
  239. doc = dd; /* save global for menuhit */
  240. ul = screen->r.min;
  241. einit(Emouse|Ekeyboard);
  242. if(doc->addpage != nil)
  243. eplumb(Eplumb, "image");
  244. esetcursor(&reading);
  245. r.min = ZP;
  246. /*
  247. * im is a global pointer to the current image.
  248. * eventually, i think we will have a layer between
  249. * the display routines and the ps/pdf/whatever routines
  250. * to perhaps cache and handle images of different
  251. * sizes, etc.
  252. */
  253. im = 0;
  254. page = reverse ? doc->npage-1 : 0;
  255. if(doc->fwdonly) {
  256. menu.item = fwditems;
  257. menu.gen = 0;
  258. menu.lasthit = 0;
  259. } else {
  260. menu.item = 0;
  261. menu.gen = menugen;
  262. menu.lasthit = 0;
  263. }
  264. midmenu.item = miditems;
  265. midmenu.gen = 0;
  266. midmenu.lasthit = Next;
  267. showpage(page, &menu);
  268. esetcursor(nil);
  269. nxt = 0;
  270. for(;;) {
  271. /*
  272. * throughout, if doc->fwdonly is set, we restrict the functionality
  273. * a fair amount. we don't care about doc->npage anymore, and
  274. * all that can be done is select the next page.
  275. */
  276. unlockdisplay(display);
  277. i = eread(Emouse|Ekeyboard|Eplumb, &e);
  278. lockdisplay(display);
  279. switch(i){
  280. case Ekeyboard:
  281. if(e.kbdc <= 0xFF && isdigit(e.kbdc)) {
  282. nxt = nxt*10+e.kbdc-'0';
  283. break;
  284. } else if(e.kbdc != '\n')
  285. nxt = 0;
  286. switch(e.kbdc) {
  287. case 'r': /* reverse page order */
  288. if(doc->fwdonly)
  289. break;
  290. reverse = !reverse;
  291. menu.lasthit = doc->npage-1-menu.lasthit;
  292. /*
  293. * the theory is that if we are reversing the
  294. * document order and are on the first or last
  295. * page then we're just starting and really want
  296. * to view the other end. maybe the if
  297. * should be dropped and this should happen always.
  298. */
  299. if(page == 0 || page == doc->npage-1) {
  300. page = doc->npage-1-page;
  301. showpage(page, &menu);
  302. }
  303. break;
  304. case 'w': /* write bitmap of current screen */
  305. esetcursor(&reading);
  306. s = writebitmap();
  307. if(s)
  308. string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
  309. display->defaultfont, s);
  310. esetcursor(nil);
  311. flushimage(display, 1);
  312. break;
  313. case 'd': /* remove image from working set */
  314. if(doc->rmpage && page < doc->npage) {
  315. if(doc->rmpage(doc, page) >= 0) {
  316. if(doc->npage < 0)
  317. wexits(0);
  318. if(page >= doc->npage)
  319. page = doc->npage-1;
  320. showpage(page, &menu);
  321. }
  322. }
  323. break;
  324. case 'q':
  325. case 0x04: /* ctrl-d */
  326. wexits(0);
  327. case 'u':
  328. if(im==nil)
  329. break;
  330. angle = (angle+180) % 360;
  331. showpage(page, &menu);
  332. break;
  333. case '-':
  334. case '\b':
  335. case Kleft:
  336. if(page > 0 && !doc->fwdonly) {
  337. --page;
  338. showpage(page, &menu);
  339. }
  340. break;
  341. case '\n':
  342. if(nxt) {
  343. nxt--;
  344. if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
  345. showpage(page=nxt, &menu);
  346. nxt = 0;
  347. break;
  348. }
  349. goto Gotonext;
  350. case Kright:
  351. case ' ':
  352. Gotonext:
  353. if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
  354. wexits(0);
  355. showpage(page, &menu);
  356. break;
  357. /*
  358. * The upper y coordinate of the image is at ul.y in screen->r.
  359. * Panning up means moving the upper left corner down. If the
  360. * upper left corner is currently visible, we need to go back a page.
  361. */
  362. case Kup:
  363. if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
  364. if(page > 0 && !doc->fwdonly){
  365. --page;
  366. showbottom = 1;
  367. showpage(page, &menu);
  368. }
  369. } else {
  370. i = Dy(screen->r)/2;
  371. if(i > 10)
  372. i -= 10;
  373. if(i+ul.y > screen->r.min.y)
  374. i = screen->r.min.y - ul.y;
  375. translate(Pt(0, i));
  376. }
  377. break;
  378. /*
  379. * If the lower y coordinate is on the screen, we go to the next page.
  380. * The lower y coordinate is at ul.y + Dy(im->r).
  381. */
  382. case Kdown:
  383. i = ul.y + Dy(im->r);
  384. if(screen->r.min.y <= i && i <= screen->r.max.y){
  385. ul.y = screen->r.min.y;
  386. goto Gotonext;
  387. } else {
  388. i = -Dy(screen->r)/2;
  389. if(i < -10)
  390. i += 10;
  391. if(i+ul.y+Dy(im->r) <= screen->r.max.y)
  392. i = screen->r.max.y - Dy(im->r) - ul.y - 1;
  393. translate(Pt(0, i));
  394. }
  395. break;
  396. default:
  397. esetcursor(&query);
  398. sleep(1000);
  399. esetcursor(nil);
  400. break;
  401. }
  402. break;
  403. case Emouse:
  404. m = e.mouse;
  405. switch(m.buttons){
  406. case Left:
  407. oxy = m.xy;
  408. xy0 = oxy;
  409. do {
  410. dxy = subpt(m.xy, oxy);
  411. oxy = m.xy;
  412. translate(dxy);
  413. unlockdisplay(display);
  414. m = emouse();
  415. lockdisplay(display);
  416. } while(m.buttons == Left);
  417. if(m.buttons) {
  418. dxy = subpt(xy0, oxy);
  419. translate(dxy);
  420. }
  421. break;
  422. case Middle:
  423. if(doc->npage == 0)
  424. break;
  425. unlockdisplay(display);
  426. n = emenuhit(Middle, &m, &midmenu);
  427. lockdisplay(display);
  428. if(n == -1)
  429. break;
  430. switch(n){
  431. case Next: /* next */
  432. if(reverse)
  433. page--;
  434. else
  435. page++;
  436. if(page < 0) {
  437. if(reverse) return;
  438. else page = 0;
  439. }
  440. if((page >= doc->npage) && !doc->fwdonly)
  441. return;
  442. showpage(page, &menu);
  443. nxt = 0;
  444. break;
  445. case Prev: /* prev */
  446. if(reverse)
  447. page++;
  448. else
  449. page--;
  450. if(page < 0) {
  451. if(reverse) return;
  452. else page = 0;
  453. }
  454. if((page >= doc->npage) && !doc->fwdonly && !reverse)
  455. return;
  456. showpage(page, &menu);
  457. nxt = 0;
  458. break;
  459. case Zerox: /* prev */
  460. zerox();
  461. break;
  462. case Zin: /* zoom in */
  463. {
  464. double delta;
  465. Rectangle r;
  466. r = egetrect(Middle, &m);
  467. if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
  468. Dx(r) == 0 || Dy(r) == 0)
  469. break;
  470. /* use the smaller side to expand */
  471. if(Dx(r) < Dy(r))
  472. delta = (double)Dx(im->r)/(double)Dx(r);
  473. else
  474. delta = (double)Dy(im->r)/(double)Dy(r);
  475. esetcursor(&reading);
  476. tmp = xallocimage(display,
  477. Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
  478. im->chan, 0, DBlack);
  479. if(tmp == nil) {
  480. fprint(2, "out of memory during zoom: %r\n");
  481. wexits("memory");
  482. }
  483. resample(im, tmp);
  484. im = tmp;
  485. delayfreeimage(tmp);
  486. esetcursor(nil);
  487. ul = screen->r.min;
  488. redraw(screen);
  489. flushimage(display, 1);
  490. break;
  491. }
  492. case Fit: /* fit */
  493. {
  494. double delta;
  495. Rectangle r;
  496. delta = (double)Dx(screen->r)/(double)Dx(im->r);
  497. if((double)Dy(im->r)*delta > Dy(screen->r))
  498. delta = (double)Dy(screen->r)/(double)Dy(im->r);
  499. r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
  500. esetcursor(&reading);
  501. tmp = xallocimage(display, r, im->chan, 0, DBlack);
  502. if(tmp == nil) {
  503. fprint(2, "out of memory during fit: %r\n");
  504. wexits("memory");
  505. }
  506. resample(im, tmp);
  507. im = tmp;
  508. delayfreeimage(tmp);
  509. esetcursor(nil);
  510. ul = screen->r.min;
  511. redraw(screen);
  512. flushimage(display, 1);
  513. break;
  514. }
  515. case Rot: /* rotate 90 */
  516. angle = (angle+90) % 360;
  517. showpage(page, &menu);
  518. break;
  519. case Upside: /* upside-down */
  520. angle = (angle+180) % 360;
  521. showpage(page, &menu);
  522. break;
  523. case Restore: /* restore */
  524. showpage(page, &menu);
  525. break;
  526. case Reverse: /* reverse */
  527. if(doc->fwdonly)
  528. break;
  529. reverse = !reverse;
  530. menu.lasthit = doc->npage-1-menu.lasthit;
  531. if(page == 0 || page == doc->npage-1) {
  532. page = doc->npage-1-page;
  533. showpage(page, &menu);
  534. }
  535. break;
  536. case Write: /* write */
  537. esetcursor(&reading);
  538. s = writebitmap();
  539. if(s)
  540. string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
  541. display->defaultfont, s);
  542. esetcursor(nil);
  543. flushimage(display, 1);
  544. break;
  545. case Del: /* delete */
  546. if(doc->rmpage && page < doc->npage) {
  547. if(doc->rmpage(doc, page) >= 0) {
  548. if(doc->npage < 0)
  549. wexits(0);
  550. if(page >= doc->npage)
  551. page = doc->npage-1;
  552. showpage(page, &menu);
  553. }
  554. }
  555. break;
  556. case Exit: /* exit */
  557. return;
  558. case Empty1:
  559. case Empty2:
  560. case Empty3:
  561. break;
  562. };
  563. case Right:
  564. if(doc->npage == 0)
  565. break;
  566. oldpage = page;
  567. unlockdisplay(display);
  568. n = emenuhit(RMenu, &m, &menu);
  569. lockdisplay(display);
  570. if(n == -1)
  571. break;
  572. if(doc->fwdonly) {
  573. switch(n){
  574. case 0: /* this page */
  575. break;
  576. case 1: /* next page */
  577. showpage(++page, &menu);
  578. break;
  579. case 2: /* exit */
  580. return;
  581. }
  582. break;
  583. }
  584. if(n == doc->npage)
  585. return;
  586. else
  587. page = reverse ? doc->npage-1-n : n;
  588. if(oldpage != page)
  589. showpage(page, &menu);
  590. nxt = 0;
  591. break;
  592. }
  593. break;
  594. case Eplumb:
  595. pm = e.v;
  596. if(pm->ndata <= 0){
  597. plumbfree(pm);
  598. break;
  599. }
  600. if(showdata(pm)) {
  601. s = estrdup("/tmp/pageplumbXXXXXXX");
  602. fd = opentemp(s);
  603. write(fd, pm->data, pm->ndata);
  604. /* lose fd reference on purpose; the file is open ORCLOSE */
  605. } else if(pm->data[0] == '/') {
  606. s = estrdup(pm->data);
  607. } else {
  608. s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
  609. sprint(s, "%s/%s", pm->wdir, pm->data);
  610. cleanname(s);
  611. }
  612. if((i = doc->addpage(doc, s)) >= 0) {
  613. page = i;
  614. unhide();
  615. showpage(page, &menu);
  616. }
  617. free(s);
  618. plumbfree(pm);
  619. break;
  620. }
  621. }
  622. }
  623. Image *gray;
  624. /*
  625. * A draw operation that touches only the area contained in bot but not in top.
  626. * mp and sp get aligned with bot.min.
  627. */
  628. static void
  629. gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
  630. Image *src, Point sp, Image *mask, Point mp, int op)
  631. {
  632. Rectangle r;
  633. Point origin;
  634. Point delta;
  635. USED(op);
  636. if(Dx(bot)*Dy(bot) == 0)
  637. return;
  638. /* no points in bot - top */
  639. if(rectinrect(bot, top))
  640. return;
  641. /* bot - top ≡ bot */
  642. if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
  643. gendrawop(dst, bot, src, sp, mask, mp, op);
  644. return;
  645. }
  646. origin = bot.min;
  647. /* split bot into rectangles that don't intersect top */
  648. /* left side */
  649. if(bot.min.x < top.min.x){
  650. r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
  651. delta = subpt(r.min, origin);
  652. gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
  653. bot.min.x = top.min.x;
  654. }
  655. /* right side */
  656. if(bot.max.x > top.max.x){
  657. r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
  658. delta = subpt(r.min, origin);
  659. gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
  660. bot.max.x = top.max.x;
  661. }
  662. /* top */
  663. if(bot.min.y < top.min.y){
  664. r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
  665. delta = subpt(r.min, origin);
  666. gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
  667. bot.min.y = top.min.y;
  668. }
  669. /* bottom */
  670. if(bot.max.y > top.max.y){
  671. r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
  672. delta = subpt(r.min, origin);
  673. gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
  674. bot.max.y = top.max.y;
  675. }
  676. }
  677. static void
  678. drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
  679. {
  680. gendrawdiff(dst, bot, top, src, p, mask, p, op);
  681. }
  682. /*
  683. * Translate the image in the window by delta.
  684. */
  685. static void
  686. translate(Point delta)
  687. {
  688. Point u;
  689. Rectangle r, or;
  690. if(im == nil)
  691. return;
  692. u = pclip(addpt(ul, delta), ulrange);
  693. delta = subpt(u, ul);
  694. if(delta.x == 0 && delta.y == 0)
  695. return;
  696. /*
  697. * The upper left corner of the image is currently at ul.
  698. * We want to move it to u.
  699. */
  700. or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
  701. r = rectaddpt(or, delta);
  702. drawop(screen, r, screen, nil, ul, S);
  703. ul = u;
  704. /* fill in gray where image used to be but isn't. */
  705. drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
  706. /* fill in black border */
  707. drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
  708. /* fill in image where it used to be off the screen. */
  709. if(rectclip(&or, screen->r))
  710. drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
  711. else
  712. drawop(screen, r, im, nil, im->r.min, S);
  713. flushimage(display, 1);
  714. }
  715. void
  716. redraw(Image *screen)
  717. {
  718. Rectangle r;
  719. if(im == nil)
  720. return;
  721. ulrange.max = screen->r.max;
  722. ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
  723. ul = pclip(ul, ulrange);
  724. drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
  725. if(im->repl)
  726. return;
  727. /* fill in any outer edges */
  728. /* black border */
  729. r = rectaddpt(im->r, subpt(ul, im->r.min));
  730. border(screen, r, -2, display->black, ZP);
  731. r.min = subpt(r.min, Pt(2,2));
  732. r.max = addpt(r.max, Pt(2,2));
  733. /* gray for the rest */
  734. if(gray == nil) {
  735. gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
  736. if(gray == nil) {
  737. fprint(2, "g out of memory: %r\n");
  738. wexits("mem");
  739. }
  740. }
  741. border(screen, r, -4000, gray, ZP);
  742. // flushimage(display, 0);
  743. }
  744. void
  745. eresized(int new)
  746. {
  747. Rectangle r;
  748. r = screen->r;
  749. if(new && getwindow(display, Refnone) < 0)
  750. fprint(2,"can't reattach to window");
  751. ul = addpt(ul, subpt(screen->r.min, r.min));
  752. redraw(screen);
  753. }
  754. /* clip p to be in r */
  755. Point
  756. pclip(Point p, Rectangle r)
  757. {
  758. if(p.x < r.min.x)
  759. p.x = r.min.x;
  760. else if(p.x >= r.max.x)
  761. p.x = r.max.x-1;
  762. if(p.y < r.min.y)
  763. p.y = r.min.y;
  764. else if(p.y >= r.max.y)
  765. p.y = r.max.y-1;
  766. return p;
  767. }
  768. /*
  769. * resize is perhaps a misnomer.
  770. * this really just grows the window to be at least dx across
  771. * and dy high. if the window hits the bottom or right edge,
  772. * it is backed up until it hits the top or left edge.
  773. */
  774. void
  775. resize(int dx, int dy)
  776. {
  777. static Rectangle sr;
  778. Rectangle r, or;
  779. dx += 2*Borderwidth;
  780. dy += 2*Borderwidth;
  781. if(wctlfd < 0){
  782. wctlfd = open("/dev/wctl", OWRITE);
  783. if(wctlfd < 0)
  784. return;
  785. }
  786. r = insetrect(screen->r, -Borderwidth);
  787. if(Dx(r) >= dx && Dy(r) >= dy)
  788. return;
  789. if(Dx(sr)*Dy(sr) == 0)
  790. sr = screenrect();
  791. or = r;
  792. r.max.x = max(r.min.x+dx, r.max.x);
  793. r.max.y = max(r.min.y+dy, r.max.y);
  794. if(r.max.x > sr.max.x){
  795. if(Dx(r) > Dx(sr)){
  796. r.min.x = 0;
  797. r.max.x = sr.max.x;
  798. }else
  799. r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
  800. }
  801. if(r.max.y > sr.max.y){
  802. if(Dy(r) > Dy(sr)){
  803. r.min.y = 0;
  804. r.max.y = sr.max.y;
  805. }else
  806. r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
  807. }
  808. /*
  809. * Sometimes we can't actually grow the window big enough,
  810. * and resizing it to the same shape makes it flash.
  811. */
  812. if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
  813. return;
  814. fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n",
  815. r.min.x, r.min.y, r.max.x, r.max.y);
  816. }
  817. /*
  818. * If we allocimage after a resize but before flushing the draw buffer,
  819. * we won't have seen the reshape event, and we won't have called
  820. * getwindow, and allocimage will fail. So we flushimage before every alloc.
  821. */
  822. Image*
  823. xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
  824. {
  825. flushimage(display, 0);
  826. return allocimage(d, r, chan, repl, val);
  827. }
  828. /* all code below this line should be in the library, but is stolen from colors instead */
  829. static char*
  830. rdenv(char *name)
  831. {
  832. char *v;
  833. int fd, size;
  834. fd = open(name, OREAD);
  835. if(fd < 0)
  836. return 0;
  837. size = seek(fd, 0, 2);
  838. v = malloc(size+1);
  839. if(v == 0){
  840. fprint(2, "page: can't malloc: %r\n");
  841. wexits("no mem");
  842. }
  843. seek(fd, 0, 0);
  844. read(fd, v, size);
  845. v[size] = 0;
  846. close(fd);
  847. return v;
  848. }
  849. void
  850. newwin(void)
  851. {
  852. char *srv, *mntsrv;
  853. char spec[100];
  854. int srvfd, cons, pid;
  855. switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){
  856. case -1:
  857. fprint(2, "page: can't fork: %r\n");
  858. wexits("no fork");
  859. case 0:
  860. break;
  861. default:
  862. wexits(0);
  863. }
  864. srv = rdenv("/env/wsys");
  865. if(srv == 0){
  866. mntsrv = rdenv("/mnt/term/env/wsys");
  867. if(mntsrv == 0){
  868. fprint(2, "page: can't find $wsys\n");
  869. wexits("srv");
  870. }
  871. srv = malloc(strlen(mntsrv)+10);
  872. sprint(srv, "/mnt/term%s", mntsrv);
  873. free(mntsrv);
  874. pid = 0; /* can't send notes to remote processes! */
  875. }else
  876. pid = getpid();
  877. srvfd = open(srv, ORDWR);
  878. if(srvfd == -1){
  879. fprint(2, "page: can't open %s: %r\n", srv);
  880. wexits("no srv");
  881. }
  882. free(srv);
  883. sprint(spec, "new -pid %d", pid);
  884. if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){
  885. fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec);
  886. wexits("no mount");
  887. }
  888. close(srvfd);
  889. unmount("/mnt/acme", "/dev");
  890. bind("/mnt/wsys", "/dev", MBEFORE);
  891. cons = open("/dev/cons", OREAD);
  892. if(cons==-1){
  893. NoCons:
  894. fprint(2, "page: can't open /dev/cons: %r");
  895. wexits("no cons");
  896. }
  897. dup(cons, 0);
  898. close(cons);
  899. cons = open("/dev/cons", OWRITE);
  900. if(cons==-1)
  901. goto NoCons;
  902. dup(cons, 1);
  903. dup(cons, 2);
  904. close(cons);
  905. // wctlfd = open("/dev/wctl", OWRITE);
  906. }
  907. Rectangle
  908. screenrect(void)
  909. {
  910. int fd;
  911. char buf[12*5];
  912. fd = open("/dev/screen", OREAD);
  913. if(fd == -1)
  914. fd=open("/mnt/term/dev/screen", OREAD);
  915. if(fd == -1){
  916. fprint(2, "page: can't open /dev/screen: %r\n");
  917. wexits("window read");
  918. }
  919. if(read(fd, buf, sizeof buf) != sizeof buf){
  920. fprint(2, "page: can't read /dev/screen: %r\n");
  921. wexits("screen read");
  922. }
  923. close(fd);
  924. return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
  925. }
  926. void
  927. zerox(void)
  928. {
  929. int pfd[2];
  930. pipe(pfd);
  931. switch(rfork(RFFDG|RFREND|RFPROC)) {
  932. case -1:
  933. wexits("cannot fork in zerox: %r");
  934. case 0:
  935. dup(pfd[1], 0);
  936. close(pfd[0]);
  937. execl("/bin/page", "page", "-w", nil);
  938. wexits("cannot exec in zerox: %r\n");
  939. default:
  940. close(pfd[1]);
  941. writeimage(pfd[0], im, 0);
  942. close(pfd[0]);
  943. break;
  944. }
  945. }