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.

преди 14 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 14 години
преди 14 години
преди 12 години
преди 14 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 14 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 12 години
преди 13 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 14 години
преди 14 години
преди 14 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 14 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 14 години
преди 14 години
преди 13 години
преди 14 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 14 години
преди 13 години
преди 14 години
преди 14 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /* sxiv: main.c
  2. * Copyright (c) 2012 Bert Muennich <be.muennich at googlemail.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the
  6. * Free Software Foundation; either version 2 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. */
  18. #define _POSIX_C_SOURCE 200112L
  19. #define _MAPPINGS_CONFIG
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include <sys/select.h>
  25. #include <sys/stat.h>
  26. #include <sys/time.h>
  27. #include <X11/Xutil.h>
  28. #include <X11/keysym.h>
  29. #include "types.h"
  30. #include "commands.h"
  31. #include "image.h"
  32. #include "options.h"
  33. #include "thumbs.h"
  34. #include "util.h"
  35. #include "window.h"
  36. #include "config.h"
  37. enum {
  38. BAR_L_LEN = 512,
  39. BAR_R_LEN = 64,
  40. FILENAME_CNT = 1024,
  41. TITLE_LEN = 256
  42. };
  43. typedef struct {
  44. struct timeval when;
  45. bool active;
  46. timeout_f handler;
  47. } timeout_t;
  48. /* timeout handler functions: */
  49. void redraw(void);
  50. void reset_cursor(void);
  51. void animate(void);
  52. void clear_resize(void);
  53. appmode_t mode;
  54. img_t img;
  55. tns_t tns;
  56. win_t win;
  57. fileinfo_t *files;
  58. int filecnt, fileidx;
  59. int alternate;
  60. int prefix;
  61. bool resized = false;
  62. const char * const INFO_SCRIPT = ".sxiv/exec/image-info";
  63. char *info_script;
  64. struct {
  65. char l[BAR_L_LEN];
  66. char r[BAR_R_LEN];
  67. } bar;
  68. timeout_t timeouts[] = {
  69. { { 0, 0 }, false, redraw },
  70. { { 0, 0 }, false, reset_cursor },
  71. { { 0, 0 }, false, animate },
  72. { { 0, 0 }, false, clear_resize },
  73. };
  74. void cleanup(void) {
  75. static bool in = false;
  76. if (!in) {
  77. in = true;
  78. img_close(&img, false);
  79. tns_free(&tns);
  80. win_close(&win);
  81. }
  82. }
  83. void check_add_file(char *filename) {
  84. const char *bn;
  85. if (filename == NULL || *filename == '\0')
  86. return;
  87. if (access(filename, R_OK) < 0) {
  88. warn("could not open file: %s", filename);
  89. return;
  90. }
  91. if (fileidx == filecnt) {
  92. filecnt *= 2;
  93. files = (fileinfo_t*) s_realloc(files, filecnt * sizeof(fileinfo_t));
  94. }
  95. if (*filename != '/') {
  96. files[fileidx].path = absolute_path(filename);
  97. if (files[fileidx].path == NULL) {
  98. warn("could not get absolute path of file: %s\n", filename);
  99. return;
  100. }
  101. }
  102. files[fileidx].loaded = false;
  103. files[fileidx].name = s_strdup(filename);
  104. if (*filename == '/')
  105. files[fileidx].path = files[fileidx].name;
  106. if ((bn = strrchr(files[fileidx].name , '/')) != NULL && bn[1] != '\0')
  107. files[fileidx].base = ++bn;
  108. else
  109. files[fileidx].base = files[fileidx].name;
  110. fileidx++;
  111. }
  112. void remove_file(int n, bool manual) {
  113. if (n < 0 || n >= filecnt)
  114. return;
  115. if (filecnt == 1) {
  116. if (!manual)
  117. fprintf(stderr, "sxiv: no more files to display, aborting\n");
  118. cleanup();
  119. exit(manual ? EXIT_SUCCESS : EXIT_FAILURE);
  120. }
  121. if (files[n].path != files[n].name)
  122. free((void*) files[n].path);
  123. free((void*) files[n].name);
  124. if (n + 1 < filecnt)
  125. memmove(files + n, files + n + 1, (filecnt - n - 1) * sizeof(fileinfo_t));
  126. if (n + 1 < tns.cnt) {
  127. memmove(tns.thumbs + n, tns.thumbs + n + 1, (tns.cnt - n - 1) *
  128. sizeof(thumb_t));
  129. memset(tns.thumbs + tns.cnt - 1, 0, sizeof(thumb_t));
  130. }
  131. filecnt--;
  132. if (n < tns.cnt)
  133. tns.cnt--;
  134. }
  135. void set_timeout(timeout_f handler, int time, bool overwrite) {
  136. int i;
  137. for (i = 0; i < ARRLEN(timeouts); i++) {
  138. if (timeouts[i].handler == handler) {
  139. if (!timeouts[i].active || overwrite) {
  140. gettimeofday(&timeouts[i].when, 0);
  141. TV_ADD_MSEC(&timeouts[i].when, time);
  142. timeouts[i].active = true;
  143. }
  144. return;
  145. }
  146. }
  147. }
  148. void reset_timeout(timeout_f handler) {
  149. int i;
  150. for (i = 0; i < ARRLEN(timeouts); i++) {
  151. if (timeouts[i].handler == handler) {
  152. timeouts[i].active = false;
  153. return;
  154. }
  155. }
  156. }
  157. bool check_timeouts(struct timeval *t) {
  158. int i = 0, tdiff, tmin = -1;
  159. struct timeval now;
  160. while (i < ARRLEN(timeouts)) {
  161. if (timeouts[i].active) {
  162. gettimeofday(&now, 0);
  163. tdiff = TV_DIFF(&timeouts[i].when, &now);
  164. if (tdiff <= 0) {
  165. timeouts[i].active = false;
  166. if (timeouts[i].handler != NULL)
  167. timeouts[i].handler();
  168. i = tmin = -1;
  169. } else if (tmin < 0 || tdiff < tmin) {
  170. tmin = tdiff;
  171. }
  172. }
  173. i++;
  174. }
  175. if (tmin > 0 && t != NULL)
  176. TV_SET_MSEC(t, tmin);
  177. return tmin > 0;
  178. }
  179. void read_info(void) {
  180. char cmd[4096];
  181. FILE *outp;
  182. int c, i = 0, n = sizeof(bar.l) - 1;
  183. bool lastsep = false;
  184. if (info_script != NULL) {
  185. snprintf(cmd, sizeof(cmd), "%s \"%s\"", info_script, files[fileidx].name);
  186. outp = popen(cmd, "r");
  187. if (outp == NULL)
  188. goto end;
  189. while (i < n && (c = fgetc(outp)) != EOF) {
  190. if (c == '\n') {
  191. if (!lastsep) {
  192. bar.l[i++] = ' ';
  193. lastsep = true;
  194. }
  195. } else {
  196. bar.l[i++] = c;
  197. lastsep = false;
  198. }
  199. }
  200. pclose(outp);
  201. }
  202. end:
  203. if (lastsep)
  204. i--;
  205. bar.l[i] = '\0';
  206. }
  207. void load_image(int new) {
  208. if (new < 0 || new >= filecnt)
  209. return;
  210. win_set_cursor(&win, CURSOR_WATCH);
  211. img_close(&img, false);
  212. while (!img_load(&img, &files[new])) {
  213. remove_file(new, false);
  214. if (new >= filecnt)
  215. new = filecnt - 1;
  216. }
  217. files[new].loaded = true;
  218. alternate = fileidx;
  219. fileidx = new;
  220. read_info();
  221. if (img.multi.cnt > 0 && img.multi.animate)
  222. set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
  223. else
  224. reset_timeout(animate);
  225. }
  226. void update_info(void) {
  227. unsigned int i, fn, fw, n, len = sizeof(bar.r);
  228. int sel;
  229. char *t = bar.r, title[TITLE_LEN];
  230. bool ow_info;
  231. for (fw = 0, i = filecnt; i > 0; fw++, i /= 10);
  232. sel = mode == MODE_IMAGE ? fileidx : tns.sel;
  233. if (mode == MODE_THUMB) {
  234. win_set_title(&win, "sxiv");
  235. if (tns.cnt == filecnt) {
  236. n = snprintf(t, len, "%0*d/%d", fw, sel + 1, filecnt);
  237. ow_info = true;
  238. } else {
  239. snprintf(bar.l, sizeof(bar.l), "Loading... %0*d/%d",
  240. fw, tns.cnt, filecnt);
  241. bar.r[0] = '\0';
  242. ow_info = false;
  243. }
  244. } else {
  245. snprintf(title, sizeof(title), "sxiv - %s", files[sel].name);
  246. win_set_title(&win, title);
  247. n = snprintf(t, len, "%3d%% ", (int) (img.zoom * 100.0));
  248. if (img.multi.cnt > 0) {
  249. for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10);
  250. n += snprintf(t + n, len - n, "(%0*d/%d) ",
  251. fn, img.multi.sel + 1, img.multi.cnt);
  252. }
  253. n += snprintf(t + n, len - n, "%0*d/%d", fw, sel + 1, filecnt);
  254. ow_info = bar.l[0] == '\0';
  255. }
  256. if (ow_info) {
  257. fn = strlen(files[sel].name);
  258. if (fn < sizeof(bar.l) &&
  259. win_textwidth(files[sel].name, fn, true) +
  260. win_textwidth(bar.r, n, true) < win.w)
  261. {
  262. strncpy(bar.l, files[sel].name, sizeof(bar.l));
  263. } else {
  264. strncpy(bar.l, files[sel].base, sizeof(bar.l));
  265. }
  266. }
  267. win_set_bar_info(&win, bar.l, bar.r);
  268. }
  269. void redraw(void) {
  270. if (mode == MODE_IMAGE)
  271. img_render(&img);
  272. else
  273. tns_render(&tns);
  274. update_info();
  275. win_draw(&win);
  276. reset_timeout(redraw);
  277. reset_cursor();
  278. }
  279. void reset_cursor(void) {
  280. int i;
  281. cursor_t cursor = CURSOR_NONE;
  282. if (mode == MODE_IMAGE) {
  283. for (i = 0; i < ARRLEN(timeouts); i++) {
  284. if (timeouts[i].handler == reset_cursor) {
  285. if (timeouts[i].active)
  286. cursor = CURSOR_ARROW;
  287. break;
  288. }
  289. }
  290. } else {
  291. if (tns.cnt != filecnt)
  292. cursor = CURSOR_WATCH;
  293. else
  294. cursor = CURSOR_ARROW;
  295. }
  296. win_set_cursor(&win, cursor);
  297. }
  298. void animate(void) {
  299. if (img_frame_animate(&img, false)) {
  300. redraw();
  301. set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
  302. }
  303. }
  304. void clear_resize(void) {
  305. resized = false;
  306. }
  307. bool keymask(const keymap_t *k, unsigned int state) {
  308. return (k->ctrl ? ControlMask : 0) == (state & ControlMask);
  309. }
  310. bool buttonmask(const button_t *b, unsigned int state) {
  311. return ((b->ctrl ? ControlMask : 0) | (b->shift ? ShiftMask : 0)) ==
  312. (state & (ControlMask | ShiftMask));
  313. }
  314. void on_keypress(XKeyEvent *kev) {
  315. int i;
  316. KeySym ksym;
  317. char key;
  318. if (kev == NULL)
  319. return;
  320. XLookupString(kev, &key, 1, &ksym, NULL);
  321. if ((ksym == XK_Escape || (key >= '0' && key <= '9')) &&
  322. (kev->state & ControlMask) == 0)
  323. {
  324. /* number prefix for commands */
  325. prefix = ksym == XK_Escape ? 0 : prefix * 10 + (int) (key - '0');
  326. return;
  327. }
  328. for (i = 0; i < ARRLEN(keys); i++) {
  329. if (keys[i].ksym == ksym && keymask(&keys[i], kev->state)) {
  330. if (keys[i].cmd != NULL && keys[i].cmd(keys[i].arg))
  331. redraw();
  332. prefix = 0;
  333. return;
  334. }
  335. }
  336. }
  337. void on_buttonpress(XButtonEvent *bev) {
  338. int i, sel;
  339. if (bev == NULL)
  340. return;
  341. if (mode == MODE_IMAGE) {
  342. win_set_cursor(&win, CURSOR_ARROW);
  343. set_timeout(reset_cursor, TO_CURSOR_HIDE, true);
  344. for (i = 0; i < ARRLEN(buttons); i++) {
  345. if (buttons[i].button == bev->button &&
  346. buttonmask(&buttons[i], bev->state))
  347. {
  348. if (buttons[i].cmd != NULL && buttons[i].cmd(buttons[i].arg))
  349. redraw();
  350. return;
  351. }
  352. }
  353. } else {
  354. /* thumbnail mode (hard-coded) */
  355. switch (bev->button) {
  356. case Button1:
  357. if ((sel = tns_translate(&tns, bev->x, bev->y)) >= 0) {
  358. if (sel == tns.sel) {
  359. mode = MODE_IMAGE;
  360. set_timeout(reset_cursor, TO_CURSOR_HIDE, true);
  361. load_image(tns.sel);
  362. } else {
  363. tns_highlight(&tns, tns.sel, false);
  364. tns_highlight(&tns, sel, true);
  365. tns.sel = sel;
  366. }
  367. redraw();
  368. break;
  369. }
  370. break;
  371. case Button4:
  372. case Button5:
  373. if (tns_scroll(&tns, bev->button == Button4 ? DIR_UP : DIR_DOWN,
  374. (bev->state & ControlMask) != 0))
  375. redraw();
  376. break;
  377. }
  378. }
  379. }
  380. void run(void) {
  381. int xfd;
  382. fd_set fds;
  383. struct timeval timeout;
  384. XEvent ev, nextev;
  385. bool discard;
  386. redraw();
  387. while (true) {
  388. while (mode == MODE_THUMB && tns.cnt < filecnt &&
  389. XPending(win.env.dpy) == 0)
  390. {
  391. /* load thumbnails */
  392. set_timeout(redraw, TO_REDRAW_THUMBS, false);
  393. if (tns_load(&tns, tns.cnt, &files[tns.cnt], false, false)) {
  394. tns.cnt++;
  395. } else {
  396. remove_file(tns.cnt, false);
  397. if (tns.sel >= tns.cnt)
  398. tns.sel--;
  399. }
  400. if (tns.cnt == filecnt)
  401. redraw();
  402. else
  403. check_timeouts(NULL);
  404. }
  405. while (XPending(win.env.dpy) == 0 && check_timeouts(&timeout)) {
  406. /* wait for timeouts */
  407. xfd = ConnectionNumber(win.env.dpy);
  408. FD_ZERO(&fds);
  409. FD_SET(xfd, &fds);
  410. select(xfd + 1, &fds, 0, 0, &timeout);
  411. }
  412. do {
  413. XNextEvent(win.env.dpy, &ev);
  414. discard = false;
  415. if (XEventsQueued(win.env.dpy, QueuedAlready) > 0) {
  416. XPeekEvent(win.env.dpy, &nextev);
  417. switch (ev.type) {
  418. case ConfigureNotify:
  419. discard = ev.type == nextev.type;
  420. break;
  421. case KeyPress:
  422. discard = (nextev.type == KeyPress || nextev.type == KeyRelease)
  423. && ev.xkey.keycode == nextev.xkey.keycode;
  424. break;
  425. }
  426. }
  427. } while (discard);
  428. switch (ev.type) {
  429. /* handle events */
  430. case ButtonPress:
  431. on_buttonpress(&ev.xbutton);
  432. break;
  433. case ClientMessage:
  434. if ((Atom) ev.xclient.data.l[0] == wm_delete_win)
  435. return;
  436. break;
  437. case ConfigureNotify:
  438. if (win_configure(&win, &ev.xconfigure)) {
  439. if (mode == MODE_IMAGE) {
  440. img.dirty = true;
  441. img.checkpan = true;
  442. } else {
  443. tns.dirty = true;
  444. }
  445. if (!resized || win.fullscreen) {
  446. redraw();
  447. set_timeout(clear_resize, TO_REDRAW_RESIZE, false);
  448. resized = true;
  449. } else {
  450. set_timeout(redraw, TO_REDRAW_RESIZE, false);
  451. }
  452. }
  453. break;
  454. case Expose:
  455. win_expose(&win, &ev.xexpose);
  456. break;
  457. case KeyPress:
  458. on_keypress(&ev.xkey);
  459. break;
  460. case MotionNotify:
  461. if (mode == MODE_IMAGE) {
  462. win_set_cursor(&win, CURSOR_ARROW);
  463. set_timeout(reset_cursor, TO_CURSOR_HIDE, true);
  464. }
  465. break;
  466. }
  467. }
  468. }
  469. int fncmp(const void *a, const void *b) {
  470. return strcoll(((fileinfo_t*) a)->name, ((fileinfo_t*) b)->name);
  471. }
  472. int main(int argc, char **argv) {
  473. int i, start;
  474. size_t n;
  475. ssize_t len;
  476. char *filename;
  477. const char *homedir;
  478. struct stat fstats;
  479. r_dir_t dir;
  480. parse_options(argc, argv);
  481. if (options->clean_cache) {
  482. tns_init(&tns, 0, NULL);
  483. tns_clean_cache(&tns);
  484. exit(EXIT_SUCCESS);
  485. }
  486. if (options->filecnt == 0) {
  487. print_usage();
  488. exit(EXIT_FAILURE);
  489. }
  490. if (options->recursive || options->from_stdin)
  491. filecnt = FILENAME_CNT;
  492. else
  493. filecnt = options->filecnt;
  494. files = (fileinfo_t*) s_malloc(filecnt * sizeof(fileinfo_t));
  495. fileidx = 0;
  496. /* build file list: */
  497. if (options->from_stdin) {
  498. filename = NULL;
  499. while ((len = get_line(&filename, &n, stdin)) > 0) {
  500. if (filename[len-1] == '\n')
  501. filename[len-1] = '\0';
  502. check_add_file(filename);
  503. }
  504. if (filename != NULL)
  505. free(filename);
  506. } else {
  507. for (i = 0; i < options->filecnt; i++) {
  508. filename = options->filenames[i];
  509. if (stat(filename, &fstats) < 0) {
  510. warn("could not stat file: %s", filename);
  511. continue;
  512. }
  513. if (!S_ISDIR(fstats.st_mode)) {
  514. check_add_file(filename);
  515. } else {
  516. if (!options->recursive) {
  517. warn("ignoring directory: %s", filename);
  518. continue;
  519. }
  520. if (r_opendir(&dir, filename) < 0) {
  521. warn("could not open directory: %s", filename);
  522. continue;
  523. }
  524. start = fileidx;
  525. while ((filename = r_readdir(&dir)) != NULL) {
  526. check_add_file(filename);
  527. free((void*) filename);
  528. }
  529. r_closedir(&dir);
  530. if (fileidx - start > 1)
  531. qsort(files + start, fileidx - start, sizeof(fileinfo_t), fncmp);
  532. }
  533. }
  534. }
  535. if (fileidx == 0) {
  536. fprintf(stderr, "sxiv: no valid image file given, aborting\n");
  537. exit(EXIT_FAILURE);
  538. }
  539. filecnt = fileidx;
  540. fileidx = options->startnum < filecnt ? options->startnum : 0;
  541. win_init(&win);
  542. img_init(&img, &win);
  543. if ((homedir = getenv("HOME")) == NULL) {
  544. warn("could not locate home directory");
  545. } else {
  546. len = strlen(homedir) + strlen(INFO_SCRIPT) + 2;
  547. info_script = (char*) s_malloc(len);
  548. snprintf(info_script, len, "%s/%s", homedir, INFO_SCRIPT);
  549. if (access(info_script, X_OK) != 0) {
  550. free(info_script);
  551. info_script = NULL;
  552. }
  553. }
  554. if (options->thumb_mode) {
  555. mode = MODE_THUMB;
  556. tns_init(&tns, filecnt, &win);
  557. while (!tns_load(&tns, 0, &files[0], false, false))
  558. remove_file(0, false);
  559. tns.cnt = 1;
  560. } else {
  561. mode = MODE_IMAGE;
  562. tns.thumbs = NULL;
  563. load_image(fileidx);
  564. }
  565. win_open(&win);
  566. run();
  567. cleanup();
  568. return 0;
  569. }