A Simple X Image Viewer
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
13 anos atrás
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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 <X11/Xlib.h>
  25. #include <X11/Xutil.h>
  26. #include <X11/keysym.h>
  27. #include "image.h"
  28. #include "options.h"
  29. #include "thumbs.h"
  30. #include "util.h"
  31. #include "window.h"
  32. typedef enum appmode_e {
  33. MODE_NORMAL = 0,
  34. MODE_THUMBS
  35. } appmode_t;
  36. void update_title();
  37. int check_append(const char*);
  38. void read_dir_rec(const char*);
  39. void run();
  40. appmode_t mode;
  41. img_t img;
  42. tns_t tns;
  43. win_t win;
  44. #define DNAME_CNT 512
  45. #define FNAME_CNT 1024
  46. const char **filenames;
  47. int filecnt, fileidx;
  48. size_t filesize;
  49. int tns_loaded;
  50. #define TITLE_LEN 256
  51. char win_title[TITLE_LEN];
  52. void cleanup() {
  53. static int in = 0;
  54. if (!in++) {
  55. img_free(&img);
  56. tns_free(&tns, &win);
  57. win_close(&win);
  58. }
  59. }
  60. int load_image() {
  61. struct stat fstats;
  62. if (!stat(filenames[fileidx], &fstats))
  63. filesize = fstats.st_size;
  64. else
  65. filesize = 0;
  66. return img_load(&img, filenames[fileidx]);
  67. }
  68. int main(int argc, char **argv) {
  69. int i;
  70. const char *filename;
  71. struct stat fstats;
  72. parse_options(argc, argv);
  73. if (!options->filecnt) {
  74. print_usage();
  75. exit(1);
  76. }
  77. if (options->recursive || options->from_stdin)
  78. filecnt = FNAME_CNT;
  79. else
  80. filecnt = options->filecnt;
  81. filenames = (const char**) s_malloc(filecnt * sizeof(const char*));
  82. fileidx = 0;
  83. if (options->from_stdin) {
  84. while ((filename = readline(stdin))) {
  85. if (!*filename || !check_append(filename))
  86. free((void*) filename);
  87. }
  88. } else {
  89. for (i = 0; i < options->filecnt; ++i) {
  90. filename = options->filenames[i];
  91. if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
  92. if (options->recursive)
  93. read_dir_rec(filename);
  94. else
  95. warn("ignoring directory: %s", filename);
  96. } else {
  97. check_append(filename);
  98. }
  99. }
  100. }
  101. filecnt = fileidx;
  102. fileidx = 0;
  103. if (!filecnt) {
  104. fprintf(stderr, "sxiv: no valid image filename given, aborting\n");
  105. exit(1);
  106. }
  107. win_open(&win);
  108. img_init(&img, &win);
  109. if (options->thumbnails) {
  110. tns_loaded = 0;
  111. tns_init(&tns, filecnt);
  112. }
  113. if (options->thumbnails == 2) {
  114. mode = MODE_THUMBS;
  115. } else {
  116. mode = MODE_NORMAL;
  117. load_image();
  118. img_render(&img, &win);
  119. }
  120. update_title();
  121. run();
  122. cleanup();
  123. return 0;
  124. }
  125. void update_title() {
  126. int n;
  127. float size;
  128. const char *unit;
  129. if (img.valid) {
  130. size = filesize;
  131. size_readable(&size, &unit);
  132. n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] <%d%%> (%.2f%s) %s",
  133. fileidx + 1, filecnt, (int) (img.zoom * 100.0), size, unit,
  134. filenames[fileidx]);
  135. } else {
  136. n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] broken: %s",
  137. fileidx + 1, filecnt, filenames[fileidx]);
  138. }
  139. if (n >= TITLE_LEN) {
  140. win_title[TITLE_LEN - 2] = '.';
  141. win_title[TITLE_LEN - 3] = '.';
  142. win_title[TITLE_LEN - 4] = '.';
  143. }
  144. win_set_title(&win, win_title);
  145. }
  146. int check_append(const char *filename) {
  147. if (!filename)
  148. return 0;
  149. if (img_check(filename)) {
  150. if (fileidx == filecnt) {
  151. filecnt *= 2;
  152. filenames = (const char**) s_realloc(filenames,
  153. filecnt * sizeof(const char*));
  154. }
  155. filenames[fileidx++] = filename;
  156. return 1;
  157. } else {
  158. return 0;
  159. }
  160. }
  161. void read_dir_rec(const char *dirname) {
  162. char *filename;
  163. const char **dirnames;
  164. int dircnt, diridx;
  165. unsigned char first;
  166. size_t len;
  167. DIR *dir;
  168. struct dirent *dentry;
  169. struct stat fstats;
  170. if (!dirname)
  171. return;
  172. dircnt = DNAME_CNT;
  173. diridx = first = 1;
  174. dirnames = (const char**) s_malloc(dircnt * sizeof(const char*));
  175. dirnames[0] = dirname;
  176. while (diridx > 0) {
  177. dirname = dirnames[--diridx];
  178. if (!(dir = opendir(dirname))) {
  179. warn("could not open directory: %s", dirname);
  180. } else {
  181. while ((dentry = readdir(dir))) {
  182. if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
  183. continue;
  184. len = strlen(dirname) + strlen(dentry->d_name) + 2;
  185. filename = (char*) s_malloc(len * sizeof(char));
  186. snprintf(filename, len, "%s/%s", dirname, dentry->d_name);
  187. if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
  188. if (diridx == dircnt) {
  189. dircnt *= 2;
  190. dirnames = (const char**) s_realloc(dirnames,
  191. dircnt * sizeof(const char*));
  192. }
  193. dirnames[diridx++] = filename;
  194. } else {
  195. if (!check_append(filename))
  196. free(filename);
  197. }
  198. }
  199. closedir(dir);
  200. }
  201. if (!first)
  202. free((void*) dirname);
  203. else
  204. first = 0;
  205. }
  206. free(dirnames);
  207. }
  208. /* event handling */
  209. unsigned char timeout;
  210. int mox, moy;
  211. void redraw() {
  212. if (mode == MODE_NORMAL)
  213. img_render(&img, &win);
  214. else
  215. tns_render(&tns, &win);
  216. update_title();
  217. timeout = 0;
  218. }
  219. void on_keypress(XKeyEvent *kev) {
  220. int x, y;
  221. unsigned int w, h;
  222. char key;
  223. KeySym ksym;
  224. int changed;
  225. if (!kev)
  226. return;
  227. XLookupString(kev, &key, 1, &ksym, NULL);
  228. changed = 0;
  229. switch (ksym) {
  230. case XK_Escape:
  231. cleanup();
  232. exit(2);
  233. case XK_q:
  234. cleanup();
  235. exit(0);
  236. /* navigate image list */
  237. case XK_n:
  238. case XK_space:
  239. if (fileidx + 1 < filecnt) {
  240. ++fileidx;
  241. changed = load_image();
  242. }
  243. break;
  244. case XK_p:
  245. case XK_BackSpace:
  246. if (fileidx > 0) {
  247. --fileidx;
  248. changed = load_image();
  249. }
  250. break;
  251. case XK_bracketleft:
  252. if (fileidx != 0) {
  253. fileidx = MAX(0, fileidx - 10);
  254. changed = load_image();
  255. }
  256. break;
  257. case XK_bracketright:
  258. if (fileidx != filecnt - 1) {
  259. fileidx = MIN(fileidx + 10, filecnt - 1);
  260. changed = load_image();
  261. }
  262. break;
  263. case XK_g:
  264. if (fileidx != 0) {
  265. fileidx = 0;
  266. changed = load_image();
  267. }
  268. break;
  269. case XK_G:
  270. if (fileidx != filecnt - 1) {
  271. fileidx = filecnt - 1;
  272. changed = load_image();
  273. }
  274. break;
  275. /* zooming */
  276. case XK_plus:
  277. case XK_equal:
  278. changed = img_zoom_in(&img);
  279. break;
  280. case XK_minus:
  281. changed = img_zoom_out(&img);
  282. break;
  283. case XK_0:
  284. changed = img_zoom(&img, 1.0);
  285. break;
  286. case XK_w:
  287. if ((changed = img_fit_win(&img, &win)))
  288. img_center(&img, &win);
  289. break;
  290. /* panning */
  291. case XK_h:
  292. case XK_Left:
  293. changed = img_pan(&img, &win, PAN_LEFT);
  294. break;
  295. case XK_j:
  296. case XK_Down:
  297. changed = img_pan(&img, &win, PAN_DOWN);
  298. break;
  299. case XK_k:
  300. case XK_Up:
  301. changed = img_pan(&img, &win, PAN_UP);
  302. break;
  303. case XK_l:
  304. case XK_Right:
  305. changed = img_pan(&img, &win, PAN_RIGHT);
  306. break;
  307. /* rotation */
  308. case XK_less:
  309. img_rotate_left(&img, &win);
  310. changed = 1;
  311. break;
  312. case XK_greater:
  313. img_rotate_right(&img, &win);
  314. changed = 1;
  315. break;
  316. /* control window */
  317. case XK_f:
  318. win_toggle_fullscreen(&win);
  319. /* render on next configurenotify */
  320. break;
  321. case XK_W:
  322. x = win.x + img.x;
  323. y = win.y + img.y;
  324. w = img.w * img.zoom;
  325. h = img.h * img.zoom;
  326. if ((changed = win_moveresize(&win, x, y, w, h))) {
  327. img.x = x - win.x;
  328. img.y = y - win.y;
  329. }
  330. break;
  331. /* miscellaneous */
  332. case XK_a:
  333. img_toggle_antialias(&img);
  334. changed = 1;
  335. break;
  336. case XK_r:
  337. changed = load_image();
  338. break;
  339. }
  340. if (changed)
  341. redraw();
  342. }
  343. void on_buttonpress(XButtonEvent *bev) {
  344. int changed;
  345. unsigned int mask;
  346. if (!bev)
  347. return;
  348. mask = CLEANMASK(bev->state);
  349. changed = 0;
  350. switch (bev->button) {
  351. case Button1:
  352. if (fileidx + 1 < filecnt) {
  353. ++fileidx;
  354. changed = load_image();
  355. }
  356. break;
  357. case Button2:
  358. mox = bev->x;
  359. moy = bev->y;
  360. win_set_cursor(&win, CURSOR_HAND);
  361. break;
  362. case Button3:
  363. if (fileidx > 0) {
  364. --fileidx;
  365. changed = load_image();
  366. }
  367. break;
  368. case Button4:
  369. if (mask == ControlMask)
  370. changed = img_zoom_in(&img);
  371. else if (mask == ShiftMask)
  372. changed = img_pan(&img, &win, PAN_LEFT);
  373. else
  374. changed = img_pan(&img, &win, PAN_UP);
  375. break;
  376. case Button5:
  377. if (mask == ControlMask)
  378. changed = img_zoom_out(&img);
  379. else if (mask == ShiftMask)
  380. changed = img_pan(&img, &win, PAN_RIGHT);
  381. else
  382. changed = img_pan(&img, &win, PAN_DOWN);
  383. break;
  384. case 6:
  385. changed = img_pan(&img, &win, PAN_LEFT);
  386. break;
  387. case 7:
  388. changed = img_pan(&img, &win, PAN_RIGHT);
  389. break;
  390. }
  391. if (changed)
  392. redraw();
  393. }
  394. void on_motionnotify(XMotionEvent *mev) {
  395. if (!mev)
  396. return;
  397. if (mev->x >= 0 && mev->x <= win.w && mev->y >= 0 && mev->y <= win.h) {
  398. if (img_move(&img, &win, mev->x - mox, mev->y - moy))
  399. timeout = 1;
  400. mox = mev->x;
  401. moy = mev->y;
  402. }
  403. }
  404. void run() {
  405. int xfd;
  406. fd_set fds;
  407. struct timeval t;
  408. XEvent ev;
  409. timeout = 0;
  410. while (1) {
  411. if (mode == MODE_THUMBS && tns_loaded < filecnt) {
  412. tns_load(&tns, &win, filenames[tns_loaded++]);
  413. tns_render(&tns, &win);
  414. if (!XPending(win.env.dpy))
  415. continue;
  416. } else if (timeout) {
  417. t.tv_sec = 0;
  418. t.tv_usec = 250;
  419. xfd = ConnectionNumber(win.env.dpy);
  420. FD_ZERO(&fds);
  421. FD_SET(xfd, &fds);
  422. if (!XPending(win.env.dpy) && !select(xfd + 1, &fds, 0, 0, &t)) {
  423. timeout = 0;
  424. if (mode == MODE_NORMAL)
  425. img_render(&img, &win);
  426. else
  427. tns_render(&tns, &win);
  428. }
  429. }
  430. if (!XNextEvent(win.env.dpy, &ev)) {
  431. switch (ev.type) {
  432. case KeyPress:
  433. on_keypress(&ev.xkey);
  434. break;
  435. case ButtonPress:
  436. on_buttonpress(&ev.xbutton);
  437. break;
  438. case ButtonRelease:
  439. if (ev.xbutton.button == Button2)
  440. win_set_cursor(&win, CURSOR_ARROW);
  441. break;
  442. case MotionNotify:
  443. on_motionnotify(&ev.xmotion);
  444. break;
  445. case ConfigureNotify:
  446. if (win_configure(&win, &ev.xconfigure)) {
  447. timeout = 1;
  448. if (mode == MODE_NORMAL)
  449. img.checkpan = 1;
  450. }
  451. break;
  452. case ClientMessage:
  453. if ((Atom) ev.xclient.data.l[0] == wm_delete_win)
  454. return;
  455. break;
  456. }
  457. }
  458. }
  459. }