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.
 
 
 
 
 
 

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