A Simple X Image Viewer
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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