A Simple X Image Viewer
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

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