view.c 22 KB

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