A Simple X Image Viewer
 
 
 
 
 
 

403 lines
7.7 KiB

  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 <sys/select.h>
  21. #include <sys/stat.h>
  22. #include <X11/Xlib.h>
  23. #include <X11/Xutil.h>
  24. #include <X11/keysym.h>
  25. #include "sxiv.h"
  26. #include "image.h"
  27. #include "window.h"
  28. void* s_malloc(size_t);
  29. void* s_realloc(void*, size_t);
  30. void on_keypress(XEvent*);
  31. void on_buttonpress(XEvent*);
  32. void on_buttonrelease(XEvent*);
  33. void on_motionnotify(XEvent*);
  34. void on_configurenotify(XEvent*);
  35. void update_title();
  36. void read_dir(const char*);
  37. static void (*handler[LASTEvent])(XEvent*) = {
  38. [KeyPress] = on_keypress,
  39. [ButtonPress] = on_buttonpress,
  40. [ButtonRelease] = on_buttonrelease,
  41. [MotionNotify] = on_motionnotify,
  42. [ConfigureNotify] = on_configurenotify
  43. };
  44. img_t img;
  45. win_t win;
  46. #define DNAME_CNT 512
  47. #define FNAME_CNT 4096
  48. const char **filenames;
  49. int filecnt, fileidx;
  50. unsigned char timeout;
  51. int mox;
  52. int moy;
  53. #define TITLE_LEN 256
  54. char win_title[TITLE_LEN];
  55. void run() {
  56. int xfd;
  57. fd_set fds;
  58. struct timeval t;
  59. XEvent ev;
  60. timeout = 0;
  61. while (1) {
  62. if (timeout) {
  63. t.tv_sec = 0;
  64. t.tv_usec = 250;
  65. xfd = ConnectionNumber(win.env.dpy);
  66. FD_ZERO(&fds);
  67. FD_SET(xfd, &fds);
  68. if (!XPending(win.env.dpy) && !select(xfd + 1, &fds, 0, 0, &t)) {
  69. img_render(&img, &win);
  70. timeout = 0;
  71. }
  72. }
  73. if (!XNextEvent(win.env.dpy, &ev) && handler[ev.type])
  74. handler[ev.type](&ev);
  75. }
  76. }
  77. int main(int argc, char **argv) {
  78. int i;
  79. const char *filename;
  80. struct stat fstats;
  81. parse_options(argc, argv);
  82. if (!options->filecnt) {
  83. print_usage();
  84. exit(1);
  85. }
  86. if (options->recursive)
  87. filecnt = FNAME_CNT;
  88. else
  89. filecnt = options->filecnt;
  90. filenames = (const char**) s_malloc(filecnt * sizeof(const char*));
  91. fileidx = 0;
  92. for (i = 0; i < options->filecnt; ++i) {
  93. filename = options->filenames[i];
  94. if (stat(filename, &fstats)) {
  95. WARN("could not stat file: %s", filename);
  96. } else if (S_ISDIR(fstats.st_mode)) {
  97. if (options->recursive)
  98. read_dir(filename);
  99. else
  100. WARN("ignoring directory: %s", filename);
  101. } else if (img_check(filename)) {
  102. if (fileidx == filecnt) {
  103. filecnt *= 2;
  104. filenames = (const char**) s_realloc(filenames,
  105. filecnt * sizeof(const char*));
  106. }
  107. filenames[fileidx++] = filename;
  108. }
  109. }
  110. filecnt = fileidx;
  111. fileidx = 0;
  112. if (!filecnt) {
  113. fprintf(stderr, "sxiv: no valid image filename given, aborting\n");
  114. exit(1);
  115. }
  116. win_open(&win);
  117. img_init(&img, &win);
  118. img_load(&img, filenames[fileidx]);
  119. img_render(&img, &win);
  120. update_title();
  121. run();
  122. cleanup();
  123. return 0;
  124. }
  125. void cleanup() {
  126. static int in = 0;
  127. if (!in++) {
  128. img_free(&img);
  129. win_close(&win);
  130. }
  131. }
  132. void on_keypress(XEvent *ev) {
  133. char key;
  134. KeySym ksym;
  135. int changed;
  136. if (!ev)
  137. return;
  138. XLookupString(&ev->xkey, &key, 1, &ksym, NULL);
  139. changed = 0;
  140. switch (ksym) {
  141. case XK_Escape:
  142. cleanup();
  143. exit(2);
  144. case XK_q:
  145. cleanup();
  146. exit(0);
  147. /* navigate image list */
  148. case XK_n:
  149. case XK_space:
  150. if (fileidx + 1 < filecnt) {
  151. changed = img_load(&img, filenames[++fileidx]);
  152. }
  153. break;
  154. case XK_p:
  155. case XK_BackSpace:
  156. if (fileidx > 0) {
  157. changed = img_load(&img, filenames[--fileidx]);
  158. }
  159. break;
  160. case XK_bracketleft:
  161. if (fileidx != 0) {
  162. fileidx = MAX(0, fileidx - 10);
  163. changed = img_load(&img, filenames[fileidx]);
  164. }
  165. break;
  166. case XK_bracketright:
  167. if (fileidx != filecnt - 1) {
  168. fileidx = MIN(fileidx + 10, filecnt - 1);
  169. changed = img_load(&img, filenames[fileidx]);
  170. }
  171. break;
  172. case XK_g:
  173. if (fileidx != 0) {
  174. fileidx = 0;
  175. changed = img_load(&img, filenames[fileidx]);
  176. }
  177. break;
  178. case XK_G:
  179. if (fileidx != filecnt - 1) {
  180. fileidx = filecnt - 1;
  181. changed = img_load(&img, filenames[fileidx]);
  182. }
  183. break;
  184. /* zooming */
  185. case XK_plus:
  186. case XK_equal:
  187. changed = img_zoom_in(&img);
  188. break;
  189. case XK_minus:
  190. changed = img_zoom_out(&img);
  191. break;
  192. /* panning */
  193. case XK_h:
  194. case XK_Left:
  195. changed = img_pan(&img, &win, PAN_LEFT);
  196. break;
  197. case XK_j:
  198. case XK_Down:
  199. changed = img_pan(&img, &win, PAN_DOWN);
  200. break;
  201. case XK_k:
  202. case XK_Up:
  203. changed = img_pan(&img, &win, PAN_UP);
  204. break;
  205. case XK_l:
  206. case XK_Right:
  207. changed = img_pan(&img, &win, PAN_RIGHT);
  208. break;
  209. /* rotation */
  210. case XK_less:
  211. changed = img_rotate_left(&img, &win);
  212. break;
  213. case XK_greater:
  214. changed = img_rotate_right(&img, &win);
  215. break;
  216. /* control window */
  217. case XK_f:
  218. win_toggle_fullscreen(&win);
  219. break;
  220. /* miscellaneous */
  221. case XK_a:
  222. changed = img_toggle_antialias(&img);
  223. break;
  224. case XK_r:
  225. changed = img_load(&img, filenames[fileidx]);
  226. break;
  227. }
  228. if (changed) {
  229. img_render(&img, &win);
  230. update_title();
  231. timeout = 0;
  232. }
  233. }
  234. void on_buttonpress(XEvent *ev) {
  235. int changed;
  236. unsigned int mask;
  237. if (!ev)
  238. return;
  239. mask = CLEANMASK(ev->xbutton.state);
  240. changed = 0;
  241. switch (ev->xbutton.button) {
  242. case Button1:
  243. if (fileidx + 1 < filecnt) {
  244. img_load(&img, filenames[++fileidx]);
  245. changed = 1;
  246. }
  247. break;
  248. case Button2:
  249. mox = ev->xbutton.x;
  250. moy = ev->xbutton.y;
  251. win_set_cursor(&win, CURSOR_HAND);
  252. break;
  253. case Button3:
  254. if (fileidx > 0) {
  255. img_load(&img, filenames[--fileidx]);
  256. changed = 1;
  257. }
  258. break;
  259. case Button4:
  260. if (mask == ControlMask)
  261. changed = img_zoom_in(&img);
  262. else if (mask == ShiftMask)
  263. changed = img_pan(&img, &win, PAN_LEFT);
  264. else
  265. changed = img_pan(&img, &win, PAN_UP);
  266. break;
  267. case Button5:
  268. if (mask == ControlMask)
  269. changed = img_zoom_out(&img);
  270. else if (mask == ShiftMask)
  271. changed = img_pan(&img, &win, PAN_RIGHT);
  272. else
  273. changed = img_pan(&img, &win, PAN_DOWN);
  274. break;
  275. case 6:
  276. changed = img_pan(&img, &win, PAN_LEFT);
  277. break;
  278. case 7:
  279. changed = img_pan(&img, &win, PAN_RIGHT);
  280. break;
  281. }
  282. if (changed) {
  283. img_render(&img, &win);
  284. update_title();
  285. timeout = 0;
  286. }
  287. }
  288. void on_buttonrelease(XEvent *ev) {
  289. if (!ev)
  290. return;
  291. if (ev->xbutton.button == Button2)
  292. win_set_cursor(&win, CURSOR_ARROW);
  293. }
  294. void on_motionnotify(XEvent *ev) {
  295. XMotionEvent *m;
  296. if (!ev)
  297. return;
  298. m = &ev->xmotion;
  299. if (m->x >= 0 && m->x <= win.w && m->y >= 0 && m->y <= win.h) {
  300. if (img_move(&img, &win, m->x - mox, m->y - moy))
  301. timeout = 1;
  302. mox = m->x;
  303. moy = m->y;
  304. }
  305. }
  306. void on_configurenotify(XEvent *ev) {
  307. if (!ev)
  308. return;
  309. if (win_configure(&win, &ev->xconfigure)) {
  310. img.checkpan = 1;
  311. timeout = 1;
  312. }
  313. }
  314. void update_title() {
  315. int n;
  316. n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] <%d%%> %s", fileidx + 1,
  317. filecnt, (int) (img.zoom * 100.0), filenames[fileidx]);
  318. if (n >= TITLE_LEN) {
  319. win_title[TITLE_LEN - 2] = '.';
  320. win_title[TITLE_LEN - 3] = '.';
  321. win_title[TITLE_LEN - 4] = '.';
  322. }
  323. win_set_title(&win, win_title);
  324. }
  325. void read_dir(const char *dir) {
  326. }
  327. void* s_malloc(size_t size) {
  328. void *ptr;
  329. if (!(ptr = malloc(size)))
  330. DIE("could not allocate memory");
  331. return ptr;
  332. }
  333. void* s_realloc(void *ptr, size_t size) {
  334. if (!(ptr = realloc(ptr, size)))
  335. DIE("could not allocate memory");
  336. return ptr;
  337. }