A Simple X Image Viewer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.c 16 KiB

14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. /* sxiv: main.c
  2. * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. */
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <sys/select.h>
  22. #include <sys/stat.h>
  23. #include <sys/time.h>
  24. #include <sys/wait.h>
  25. #include <unistd.h>
  26. #include <X11/Xlib.h>
  27. #include <X11/Xutil.h>
  28. #include <X11/keysym.h>
  29. #include "image.h"
  30. #include "options.h"
  31. #include "thumbs.h"
  32. #include "util.h"
  33. #include "window.h"
  34. #define FNAME_CNT 1024
  35. #define TITLE_LEN 256
  36. typedef enum {
  37. MODE_NORMAL = 0,
  38. MODE_THUMBS
  39. } appmode_t;
  40. typedef struct {
  41. KeySym ksym;
  42. Bool reload;
  43. const char *cmdline;
  44. } command_t;
  45. #define MAIN_C
  46. #include "config.h"
  47. void run();
  48. appmode_t mode;
  49. img_t img;
  50. tns_t tns;
  51. win_t win;
  52. const char **filenames;
  53. int filecnt, fileidx;
  54. size_t filesize;
  55. char win_title[TITLE_LEN];
  56. void cleanup() {
  57. static int in = 0;
  58. if (!in++) {
  59. img_close(&img, 0);
  60. tns_free(&tns);
  61. win_close(&win);
  62. }
  63. }
  64. void remove_file(int n, unsigned char silent) {
  65. if (n < 0 || n >= filecnt)
  66. return;
  67. if (filecnt == 1) {
  68. if (!silent)
  69. fprintf(stderr, "sxiv: no more files to display, aborting\n");
  70. cleanup();
  71. exit(!silent);
  72. }
  73. if (n + 1 < filecnt)
  74. memmove(filenames + n, filenames + n + 1, (filecnt - n - 1) *
  75. sizeof(const char*));
  76. if (n + 1 < tns.cnt) {
  77. memmove(tns.thumbs + n, tns.thumbs + n + 1, (tns.cnt - n - 1) *
  78. sizeof(thumb_t));
  79. memset(tns.thumbs + tns.cnt - 1, 0, sizeof(thumb_t));
  80. }
  81. --filecnt;
  82. if (n < tns.cnt)
  83. --tns.cnt;
  84. }
  85. int load_image(int new) {
  86. struct stat fstats;
  87. if (new >= 0 && new < filecnt) {
  88. win_set_cursor(&win, CURSOR_WATCH);
  89. img_close(&img, 0);
  90. while (!img_load(&img, filenames[new])) {
  91. remove_file(new, 0);
  92. if (new >= filecnt)
  93. new = filecnt - 1;
  94. }
  95. fileidx = new;
  96. if (!stat(filenames[new], &fstats))
  97. filesize = fstats.st_size;
  98. else
  99. filesize = 0;
  100. /* cursor is reset in redraw() */
  101. }
  102. return 1;
  103. }
  104. void update_title() {
  105. int n;
  106. float size;
  107. const char *unit;
  108. if (mode == MODE_THUMBS) {
  109. n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] %s",
  110. tns.cnt ? tns.sel + 1 : 0, tns.cnt,
  111. tns.cnt ? filenames[tns.sel] : "");
  112. } else {
  113. size = filesize;
  114. size_readable(&size, &unit);
  115. n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] <%d%%> (%.2f%s) %s",
  116. fileidx + 1, filecnt, (int) (img.zoom * 100.0), size, unit,
  117. filenames[fileidx]);
  118. }
  119. if (n >= TITLE_LEN) {
  120. win_title[TITLE_LEN - 2] = '.';
  121. win_title[TITLE_LEN - 3] = '.';
  122. win_title[TITLE_LEN - 4] = '.';
  123. }
  124. win_set_title(&win, win_title);
  125. }
  126. int check_append(const char *filename) {
  127. if (!filename)
  128. return 0;
  129. if (access(filename, R_OK)) {
  130. warn("could not open file: %s", filename);
  131. return 0;
  132. } else {
  133. if (fileidx == filecnt) {
  134. filecnt *= 2;
  135. filenames = (const char**) s_realloc(filenames,
  136. filecnt * sizeof(const char*));
  137. }
  138. filenames[fileidx++] = filename;
  139. return 1;
  140. }
  141. }
  142. int fncmp(const void *a, const void *b) {
  143. return strcoll(*((char* const*) a), *((char* const*) b));
  144. }
  145. int main(int argc, char **argv) {
  146. int i, start;
  147. const char *filename;
  148. struct stat fstats;
  149. r_dir_t dir;
  150. parse_options(argc, argv);
  151. if (options->clean_cache) {
  152. tns_init(&tns, 0);
  153. tns_clean_cache(&tns);
  154. exit(0);
  155. }
  156. if (!options->filecnt) {
  157. print_usage();
  158. exit(1);
  159. }
  160. if (options->recursive || options->from_stdin)
  161. filecnt = FNAME_CNT;
  162. else
  163. filecnt = options->filecnt;
  164. filenames = (const char**) s_malloc(filecnt * sizeof(const char*));
  165. fileidx = 0;
  166. if (options->from_stdin) {
  167. while ((filename = readline(stdin))) {
  168. if (!*filename || !check_append(filename))
  169. free((void*) filename);
  170. }
  171. } else {
  172. for (i = 0; i < options->filecnt; ++i) {
  173. filename = options->filenames[i];
  174. if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) {
  175. check_append(filename);
  176. } else {
  177. if (!options->recursive) {
  178. warn("ignoring directory: %s", filename);
  179. continue;
  180. }
  181. if (r_opendir(&dir, filename)) {
  182. warn("could not open directory: %s", filename);
  183. continue;
  184. }
  185. start = fileidx;
  186. while ((filename = r_readdir(&dir))) {
  187. if (!check_append(filename))
  188. free((void*) filename);
  189. }
  190. r_closedir(&dir);
  191. if (fileidx - start > 1)
  192. qsort(filenames + start, fileidx - start, sizeof(char*), fncmp);
  193. }
  194. }
  195. }
  196. filecnt = fileidx;
  197. fileidx = 0;
  198. if (!filecnt) {
  199. fprintf(stderr, "sxiv: no valid image file given, aborting\n");
  200. exit(1);
  201. }
  202. win_init(&win);
  203. img_init(&img, &win);
  204. if (options->thumbnails) {
  205. mode = MODE_THUMBS;
  206. tns_init(&tns, filecnt);
  207. while (!tns_load(&tns, 0, filenames[0], 0))
  208. remove_file(0, 0);
  209. tns.cnt = 1;
  210. } else {
  211. mode = MODE_NORMAL;
  212. tns.thumbs = NULL;
  213. load_image(fileidx);
  214. }
  215. win_open(&win);
  216. run();
  217. cleanup();
  218. return 0;
  219. }
  220. #if EXT_COMMANDS
  221. int run_command(const char *cline, Bool reload) {
  222. int fncnt, fnlen;
  223. char *cn, *cmdline;
  224. const char *co, *fname;
  225. pid_t pid;
  226. int ret, status;
  227. if (!cline || !*cline)
  228. return 0;
  229. fncnt = 0;
  230. co = cline - 1;
  231. while ((co = strchr(co + 1, '#')))
  232. ++fncnt;
  233. if (!fncnt)
  234. return 0;
  235. ret = 0;
  236. fname = filenames[mode == MODE_NORMAL ? fileidx : tns.sel];
  237. fnlen = strlen(fname);
  238. cn = cmdline = (char*) s_malloc((strlen(cline) + fncnt * (fnlen + 2)) *
  239. sizeof(char));
  240. /* replace all '#' with filename */
  241. for (co = cline; *co; ++co) {
  242. if (*co == '#') {
  243. *cn++ = '"';
  244. strcpy(cn, fname);
  245. cn += fnlen;
  246. *cn++ = '"';
  247. } else {
  248. *cn++ = *co;
  249. }
  250. }
  251. *cn = '\0';
  252. if ((pid = fork()) == 0) {
  253. execlp("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
  254. warn("could not exec: /bin/sh");
  255. exit(1);
  256. } else if (pid < 0) {
  257. warn("could not fork. command line was: %s", cmdline);
  258. } else if (reload) {
  259. waitpid(pid, &status, 0);
  260. if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
  261. ret = 1;
  262. else
  263. warn("child exited with non-zero return value: %d. command line was: %s",
  264. WEXITSTATUS(status), cmdline);
  265. }
  266. free(cmdline);
  267. return ret;
  268. }
  269. #endif /* EXT_COMMANDS */
  270. /* event handling */
  271. #define TO_WIN_RESIZE 75000
  272. #define TO_IMAGE_DRAG 1000
  273. #define TO_CURSOR_HIDE 1500000
  274. #define TO_THUMBS_LOAD 75000
  275. int timo_cursor;
  276. int timo_redraw;
  277. unsigned char drag;
  278. int mox, moy;
  279. void redraw() {
  280. if (mode == MODE_NORMAL) {
  281. img_render(&img, &win);
  282. if (timo_cursor)
  283. win_set_cursor(&win, CURSOR_ARROW);
  284. else if (!drag)
  285. win_set_cursor(&win, CURSOR_NONE);
  286. } else {
  287. tns_render(&tns, &win);
  288. }
  289. update_title();
  290. timo_redraw = 0;
  291. }
  292. void on_keypress(XKeyEvent *kev) {
  293. int x, y;
  294. unsigned int w, h;
  295. char key;
  296. KeySym ksym;
  297. int changed;
  298. if (!kev)
  299. return;
  300. XLookupString(kev, &key, 1, &ksym, NULL);
  301. changed = 0;
  302. #if EXT_COMMANDS
  303. /* external commands from commands.h */
  304. if (CLEANMASK(kev->state) & ControlMask) {
  305. for (x = 0; x < LEN(commands); ++x) {
  306. if (commands[x].ksym == ksym) {
  307. win_set_cursor(&win, CURSOR_WATCH);
  308. if (run_command(commands[x].cmdline, commands[x].reload)) {
  309. if (mode == MODE_NORMAL) {
  310. if (fileidx < tns.cnt)
  311. tns_load(&tns, fileidx, filenames[fileidx], 1);
  312. img_close(&img, 1);
  313. load_image(fileidx);
  314. } else {
  315. if (!tns_load(&tns, tns.sel, filenames[tns.sel], 0)) {
  316. remove_file(tns.sel, 0);
  317. tns.dirty = 1;
  318. if (tns.sel >= tns.cnt)
  319. tns.sel = tns.cnt - 1;
  320. }
  321. }
  322. redraw();
  323. }
  324. if (mode == MODE_THUMBS)
  325. win_set_cursor(&win, CURSOR_ARROW);
  326. else if (!timo_cursor)
  327. win_set_cursor(&win, CURSOR_NONE);
  328. return;
  329. }
  330. }
  331. }
  332. #endif
  333. if (mode == MODE_NORMAL) {
  334. switch (ksym) {
  335. /* navigate image list */
  336. case XK_n:
  337. case XK_space:
  338. if (fileidx + 1 < filecnt)
  339. changed = load_image(fileidx + 1);
  340. break;
  341. case XK_p:
  342. case XK_BackSpace:
  343. if (fileidx > 0)
  344. changed = load_image(fileidx - 1);
  345. break;
  346. case XK_bracketleft:
  347. if (fileidx != 0)
  348. changed = load_image(MAX(0, fileidx - 10));
  349. break;
  350. case XK_bracketright:
  351. if (fileidx != filecnt - 1)
  352. changed = load_image(MIN(fileidx + 10, filecnt - 1));
  353. break;
  354. case XK_g:
  355. if (fileidx != 0)
  356. changed = load_image(0);
  357. break;
  358. case XK_G:
  359. if (fileidx != filecnt - 1)
  360. changed = load_image(filecnt - 1);
  361. break;
  362. /* zooming */
  363. case XK_plus:
  364. case XK_equal:
  365. changed = img_zoom_in(&img, &win);
  366. break;
  367. case XK_minus:
  368. changed = img_zoom_out(&img, &win);
  369. break;
  370. case XK_0:
  371. changed = img_zoom(&img, &win, 1.0);
  372. break;
  373. case XK_w:
  374. if ((changed = img_fit_win(&img, &win)))
  375. img_center(&img, &win);
  376. break;
  377. /* panning */
  378. case XK_h:
  379. case XK_Left:
  380. changed = img_pan(&img, &win, PAN_LEFT);
  381. break;
  382. case XK_j:
  383. case XK_Down:
  384. changed = img_pan(&img, &win, PAN_DOWN);
  385. break;
  386. case XK_k:
  387. case XK_Up:
  388. changed = img_pan(&img, &win, PAN_UP);
  389. break;
  390. case XK_l:
  391. case XK_Right:
  392. changed = img_pan(&img, &win, PAN_RIGHT);
  393. break;
  394. /* rotation */
  395. case XK_less:
  396. img_rotate_left(&img, &win);
  397. changed = 1;
  398. break;
  399. case XK_greater:
  400. img_rotate_right(&img, &win);
  401. changed = 1;
  402. break;
  403. /* control window */
  404. case XK_W:
  405. x = MAX(0, win.x + img.x);
  406. y = MAX(0, win.y + img.y);
  407. w = img.w * img.zoom;
  408. h = img.h * img.zoom;
  409. if ((changed = win_moveresize(&win, x, y, w, h))) {
  410. img.x = x - win.x;
  411. img.y = y - win.y;
  412. }
  413. break;
  414. /* switch to thumbnail mode */
  415. case XK_Return:
  416. if (!tns.thumbs)
  417. tns_init(&tns, filecnt);
  418. img_close(&img, 0);
  419. mode = MODE_THUMBS;
  420. win_set_cursor(&win, CURSOR_ARROW);
  421. timo_cursor = 0;
  422. tns.sel = fileidx;
  423. changed = tns.dirty = 1;
  424. break;
  425. /* miscellaneous */
  426. case XK_a:
  427. img_toggle_antialias(&img);
  428. changed = 1;
  429. break;
  430. case XK_A:
  431. img.alpha ^= 1;
  432. changed = 1;
  433. break;
  434. case XK_D:
  435. remove_file(fileidx, 1);
  436. changed = load_image(fileidx >= filecnt ? filecnt - 1 : fileidx);
  437. break;
  438. case XK_r:
  439. changed = load_image(fileidx);
  440. break;
  441. }
  442. } else {
  443. /* thumbnail mode */
  444. switch (ksym) {
  445. /* open selected image */
  446. case XK_Return:
  447. load_image(tns.sel);
  448. mode = MODE_NORMAL;
  449. changed = 1;
  450. break;
  451. /* move selection */
  452. case XK_h:
  453. case XK_Left:
  454. changed = tns_move_selection(&tns, &win, TNS_LEFT);
  455. break;
  456. case XK_j:
  457. case XK_Down:
  458. changed = tns_move_selection(&tns, &win, TNS_DOWN);
  459. break;
  460. case XK_k:
  461. case XK_Up:
  462. changed = tns_move_selection(&tns, &win, TNS_UP);
  463. break;
  464. case XK_l:
  465. case XK_Right:
  466. changed = tns_move_selection(&tns, &win, TNS_RIGHT);
  467. break;
  468. case XK_g:
  469. if (tns.sel != 0) {
  470. tns.sel = 0;
  471. changed = tns.dirty = 1;
  472. }
  473. break;
  474. case XK_G:
  475. if (tns.sel != tns.cnt - 1) {
  476. tns.sel = tns.cnt - 1;
  477. changed = tns.dirty = 1;
  478. }
  479. break;
  480. /* miscellaneous */
  481. case XK_D:
  482. if (tns.sel < tns.cnt) {
  483. remove_file(tns.sel, 1);
  484. changed = tns.dirty = 1;
  485. if (tns.sel >= tns.cnt)
  486. tns.sel = tns.cnt - 1;
  487. }
  488. break;
  489. }
  490. }
  491. /* common key mappings */
  492. switch (ksym) {
  493. case XK_q:
  494. cleanup();
  495. exit(0);
  496. case XK_f:
  497. win_toggle_fullscreen(&win);
  498. if (mode == MODE_NORMAL)
  499. img.checkpan = 1;
  500. else
  501. tns.dirty = 1;
  502. timo_redraw = TO_WIN_RESIZE;
  503. break;
  504. }
  505. if (changed)
  506. redraw();
  507. }
  508. void on_buttonpress(XButtonEvent *bev) {
  509. int changed, sel;
  510. unsigned int mask;
  511. if (!bev)
  512. return;
  513. mask = CLEANMASK(bev->state);
  514. changed = 0;
  515. if (mode == MODE_NORMAL) {
  516. if (!drag) {
  517. win_set_cursor(&win, CURSOR_ARROW);
  518. timo_cursor = TO_CURSOR_HIDE;
  519. }
  520. switch (bev->button) {
  521. case Button1:
  522. if (fileidx + 1 < filecnt)
  523. changed = load_image(fileidx + 1);
  524. break;
  525. case Button2:
  526. mox = bev->x;
  527. moy = bev->y;
  528. win_set_cursor(&win, CURSOR_HAND);
  529. timo_cursor = 0;
  530. drag = 1;
  531. break;
  532. case Button3:
  533. if (fileidx > 0)
  534. changed = load_image(fileidx - 1);
  535. break;
  536. case Button4:
  537. if (mask == ControlMask)
  538. changed = img_zoom_in(&img, &win);
  539. else if (mask == ShiftMask)
  540. changed = img_pan(&img, &win, PAN_LEFT);
  541. else
  542. changed = img_pan(&img, &win, PAN_UP);
  543. break;
  544. case Button5:
  545. if (mask == ControlMask)
  546. changed = img_zoom_out(&img, &win);
  547. else if (mask == ShiftMask)
  548. changed = img_pan(&img, &win, PAN_RIGHT);
  549. else
  550. changed = img_pan(&img, &win, PAN_DOWN);
  551. break;
  552. case 6:
  553. changed = img_pan(&img, &win, PAN_LEFT);
  554. break;
  555. case 7:
  556. changed = img_pan(&img, &win, PAN_RIGHT);
  557. break;
  558. }
  559. } else {
  560. /* thumbnail mode */
  561. switch (bev->button) {
  562. case Button1:
  563. if ((sel = tns_translate(&tns, bev->x, bev->y)) >= 0) {
  564. if (sel == tns.sel) {
  565. load_image(tns.sel);
  566. mode = MODE_NORMAL;
  567. timo_cursor = TO_CURSOR_HIDE;
  568. } else {
  569. tns_highlight(&tns, &win, tns.sel, False);
  570. tns_highlight(&tns, &win, sel, True);
  571. tns.sel = sel;
  572. }
  573. changed = 1;
  574. break;
  575. }
  576. break;
  577. case Button4:
  578. changed = tns_scroll(&tns, TNS_UP);
  579. break;
  580. case Button5:
  581. changed = tns_scroll(&tns, TNS_DOWN);
  582. break;
  583. }
  584. }
  585. if (changed)
  586. redraw();
  587. }
  588. void on_motionnotify(XMotionEvent *mev) {
  589. if (!mev)
  590. return;
  591. if (mev->x >= 0 && mev->x <= win.w && mev->y >= 0 && mev->y <= win.h) {
  592. if (img_move(&img, &win, mev->x - mox, mev->y - moy))
  593. timo_redraw = TO_IMAGE_DRAG;
  594. mox = mev->x;
  595. moy = mev->y;
  596. }
  597. }
  598. void run() {
  599. int xfd, timeout;
  600. fd_set fds;
  601. struct timeval tt, t0, t1;
  602. XEvent ev;
  603. drag = 0;
  604. timo_cursor = mode == MODE_NORMAL ? TO_CURSOR_HIDE : 0;
  605. redraw();
  606. while (1) {
  607. if (mode == MODE_THUMBS && tns.cnt < filecnt) {
  608. win_set_cursor(&win, CURSOR_WATCH);
  609. gettimeofday(&t0, 0);
  610. while (tns.cnt < filecnt && !XPending(win.env.dpy)) {
  611. if (tns_load(&tns, tns.cnt, filenames[tns.cnt], 0))
  612. ++tns.cnt;
  613. else
  614. remove_file(tns.cnt, 0);
  615. gettimeofday(&t1, 0);
  616. if (TV_TO_DOUBLE(t1) - TV_TO_DOUBLE(t0) >= 0.25)
  617. break;
  618. }
  619. if (tns.cnt == filecnt)
  620. win_set_cursor(&win, CURSOR_ARROW);
  621. if (!XPending(win.env.dpy)) {
  622. redraw();
  623. continue;
  624. } else {
  625. timo_redraw = TO_THUMBS_LOAD;
  626. }
  627. } else if (timo_cursor || timo_redraw) {
  628. gettimeofday(&t0, 0);
  629. if (timo_cursor && timo_redraw)
  630. timeout = MIN(timo_cursor, timo_redraw);
  631. else if (timo_cursor)
  632. timeout = timo_cursor;
  633. else
  634. timeout = timo_redraw;
  635. tt.tv_sec = timeout / 1000000;
  636. tt.tv_usec = timeout % 1000000;
  637. xfd = ConnectionNumber(win.env.dpy);
  638. FD_ZERO(&fds);
  639. FD_SET(xfd, &fds);
  640. if (!XPending(win.env.dpy))
  641. select(xfd + 1, &fds, 0, 0, &tt);
  642. gettimeofday(&t1, 0);
  643. timeout = MIN((TV_TO_DOUBLE(t1) - TV_TO_DOUBLE(t0)) * 1000000, timeout);
  644. /* timeouts fired? */
  645. if (timo_cursor) {
  646. timo_cursor = MAX(0, timo_cursor - timeout);
  647. if (!timo_cursor)
  648. win_set_cursor(&win, CURSOR_NONE);
  649. }
  650. if (timo_redraw) {
  651. timo_redraw = MAX(0, timo_redraw - timeout);
  652. if (!timo_redraw)
  653. redraw();
  654. }
  655. if (!XPending(win.env.dpy) && (timo_cursor || timo_redraw))
  656. continue;
  657. }
  658. if (!XNextEvent(win.env.dpy, &ev)) {
  659. switch (ev.type) {
  660. case KeyPress:
  661. on_keypress(&ev.xkey);
  662. break;
  663. case ButtonPress:
  664. on_buttonpress(&ev.xbutton);
  665. break;
  666. case ButtonRelease:
  667. if (ev.xbutton.button == Button2) {
  668. drag = 0;
  669. if (mode == MODE_NORMAL) {
  670. win_set_cursor(&win, CURSOR_ARROW);
  671. timo_cursor = TO_CURSOR_HIDE;
  672. }
  673. }
  674. break;
  675. case MotionNotify:
  676. if (drag) {
  677. on_motionnotify(&ev.xmotion);
  678. } else if (mode == MODE_NORMAL) {
  679. if (!timo_cursor)
  680. win_set_cursor(&win, CURSOR_ARROW);
  681. timo_cursor = TO_CURSOR_HIDE;
  682. }
  683. break;
  684. case ConfigureNotify:
  685. if (win_configure(&win, &ev.xconfigure)) {
  686. timo_redraw = TO_WIN_RESIZE;
  687. if (mode == MODE_NORMAL)
  688. img.checkpan = 1;
  689. else
  690. tns.dirty = 1;
  691. }
  692. break;
  693. case ClientMessage:
  694. if ((Atom) ev.xclient.data.l[0] == wm_delete_win)
  695. return;
  696. break;
  697. }
  698. }
  699. }
  700. }