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.
 
 
 
 
 
 

501 lines
9.4 KiB

  1. /* Copyright 2011, 2012 Bert Muennich
  2. *
  3. * This file is part of sxiv.
  4. *
  5. * sxiv is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published
  7. * by the Free Software Foundation; either version 2 of the License,
  8. * or (at your option) any later version.
  9. *
  10. * sxiv is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with sxiv. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #define _POSIX_C_SOURCE 200112L
  19. #define _IMAGE_CONFIG
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <sys/wait.h>
  24. #include "commands.h"
  25. #include "image.h"
  26. #include "thumbs.h"
  27. #include "util.h"
  28. #include "config.h"
  29. void cleanup(void);
  30. void remove_file(int, bool);
  31. void load_image(int);
  32. void redraw(void);
  33. void reset_cursor(void);
  34. void animate(void);
  35. void set_timeout(timeout_f, int, bool);
  36. void reset_timeout(timeout_f);
  37. extern appmode_t mode;
  38. extern img_t img;
  39. extern tns_t tns;
  40. extern win_t win;
  41. extern fileinfo_t *files;
  42. extern int filecnt, fileidx;
  43. extern int alternate;
  44. extern int prefix;
  45. const int ss_delays[] = {
  46. 1, 2, 3, 5, 10, 15, 20, 30, 60, 120, 180, 300, 600
  47. };
  48. bool it_quit(arg_t a)
  49. {
  50. cleanup();
  51. exit(EXIT_SUCCESS);
  52. }
  53. bool it_switch_mode(arg_t a)
  54. {
  55. if (mode == MODE_IMAGE) {
  56. if (tns.thumbs == NULL)
  57. tns_init(&tns, filecnt, &win);
  58. img_close(&img, false);
  59. reset_timeout(reset_cursor);
  60. tns.sel = fileidx;
  61. tns.dirty = true;
  62. mode = MODE_THUMB;
  63. } else {
  64. load_image(tns.sel);
  65. mode = MODE_IMAGE;
  66. }
  67. return true;
  68. }
  69. bool it_toggle_fullscreen(arg_t a)
  70. {
  71. win_toggle_fullscreen(&win);
  72. /* redraw after next ConfigureNotify event */
  73. set_timeout(redraw, TO_REDRAW_RESIZE, false);
  74. if (mode == MODE_IMAGE)
  75. img.checkpan = img.dirty = true;
  76. else
  77. tns.dirty = true;
  78. return false;
  79. }
  80. bool it_toggle_bar(arg_t a)
  81. {
  82. win_toggle_bar(&win);
  83. if (mode == MODE_IMAGE)
  84. img.checkpan = img.dirty = true;
  85. else
  86. tns.dirty = true;
  87. return true;
  88. }
  89. bool t_reload_all(arg_t a)
  90. {
  91. if (mode == MODE_THUMB) {
  92. tns_free(&tns);
  93. tns_init(&tns, filecnt, &win);
  94. return true;
  95. } else {
  96. return false;
  97. }
  98. }
  99. bool it_reload_image(arg_t a)
  100. {
  101. if (mode == MODE_IMAGE) {
  102. load_image(fileidx);
  103. } else {
  104. win_set_cursor(&win, CURSOR_WATCH);
  105. if (!tns_load(&tns, tns.sel, &files[tns.sel], true, false)) {
  106. remove_file(tns.sel, false);
  107. tns.dirty = true;
  108. if (tns.sel >= tns.cnt)
  109. tns.sel = tns.cnt - 1;
  110. }
  111. }
  112. return true;
  113. }
  114. bool it_remove_image(arg_t a)
  115. {
  116. if (mode == MODE_IMAGE) {
  117. remove_file(fileidx, true);
  118. load_image(fileidx >= filecnt ? filecnt - 1 : fileidx);
  119. return true;
  120. } else if (tns.sel < tns.cnt) {
  121. remove_file(tns.sel, true);
  122. tns.dirty = true;
  123. if (tns.sel >= tns.cnt)
  124. tns.sel = tns.cnt - 1;
  125. return true;
  126. } else {
  127. return false;
  128. }
  129. }
  130. bool i_navigate(arg_t a)
  131. {
  132. long n = (long) a;
  133. if (mode == MODE_IMAGE) {
  134. if (prefix > 0)
  135. n *= prefix;
  136. n += fileidx;
  137. if (n < 0)
  138. n = 0;
  139. if (n >= filecnt)
  140. n = filecnt - 1;
  141. if (n != fileidx) {
  142. load_image(n);
  143. return true;
  144. }
  145. }
  146. return false;
  147. }
  148. bool i_alternate(arg_t a)
  149. {
  150. if (mode == MODE_IMAGE) {
  151. load_image(alternate);
  152. return true;
  153. } else {
  154. return false;
  155. }
  156. }
  157. bool it_first(arg_t a)
  158. {
  159. if (mode == MODE_IMAGE && fileidx != 0) {
  160. load_image(0);
  161. return true;
  162. } else if (mode == MODE_THUMB && tns.sel != 0) {
  163. tns.sel = 0;
  164. tns.dirty = true;
  165. return true;
  166. } else {
  167. return false;
  168. }
  169. }
  170. bool it_n_or_last(arg_t a)
  171. {
  172. int n = prefix != 0 && prefix - 1 < filecnt ? prefix - 1 : filecnt - 1;
  173. if (mode == MODE_IMAGE && fileidx != n) {
  174. load_image(n);
  175. return true;
  176. } else if (mode == MODE_THUMB && tns.sel != n) {
  177. tns.sel = n;
  178. tns.dirty = true;
  179. return true;
  180. } else {
  181. return false;
  182. }
  183. }
  184. bool i_navigate_frame(arg_t a)
  185. {
  186. if (mode == MODE_IMAGE && !img.multi.animate)
  187. return img_frame_navigate(&img, (long) a);
  188. else
  189. return false;
  190. }
  191. bool i_toggle_animation(arg_t a)
  192. {
  193. if (mode != MODE_IMAGE)
  194. return false;
  195. if (img.multi.animate) {
  196. reset_timeout(animate);
  197. img.multi.animate = false;
  198. } else if (img_frame_animate(&img, true)) {
  199. set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
  200. }
  201. return true;
  202. }
  203. bool it_scroll_move(arg_t a)
  204. {
  205. direction_t dir = (direction_t) a;
  206. if (mode == MODE_IMAGE)
  207. return img_pan(&img, dir, prefix);
  208. else
  209. return tns_move_selection(&tns, dir, prefix);
  210. }
  211. bool it_scroll_screen(arg_t a)
  212. {
  213. direction_t dir = (direction_t) a;
  214. if (mode == MODE_IMAGE)
  215. return img_pan(&img, dir, -1);
  216. else
  217. return tns_scroll(&tns, dir, true);
  218. }
  219. bool i_scroll_to_edge(arg_t a)
  220. {
  221. direction_t dir = (direction_t) a;
  222. if (mode == MODE_IMAGE)
  223. return img_pan_edge(&img, dir);
  224. else
  225. return false;
  226. }
  227. /* Xlib helper function for i_drag() */
  228. Bool is_motionnotify(Display *d, XEvent *e, XPointer a)
  229. {
  230. return e != NULL && e->type == MotionNotify;
  231. }
  232. #define WARP(x,y) \
  233. XWarpPointer(win.env.dpy, None, win.xwin, 0, 0, 0, 0, x, y); \
  234. ox = x, oy = y; \
  235. break
  236. bool i_drag(arg_t a)
  237. {
  238. int dx = 0, dy = 0, i, ox, oy, x, y;
  239. unsigned int ui;
  240. bool dragging = true, next = false;
  241. XEvent e;
  242. Window w;
  243. if (mode != MODE_IMAGE)
  244. return false;
  245. if (!XQueryPointer(win.env.dpy, win.xwin, &w, &w, &i, &i, &ox, &oy, &ui))
  246. return false;
  247. win_set_cursor(&win, CURSOR_HAND);
  248. while (dragging) {
  249. if (!next)
  250. XMaskEvent(win.env.dpy,
  251. ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e);
  252. switch (e.type) {
  253. case ButtonPress:
  254. case ButtonRelease:
  255. dragging = false;
  256. break;
  257. case MotionNotify:
  258. x = e.xmotion.x;
  259. y = e.xmotion.y;
  260. /* wrap the mouse around */
  261. if (x < 0) {
  262. WARP(win.w, y);
  263. } else if (x > win.w) {
  264. WARP(0, y);
  265. } else if (y < 0) {
  266. WARP(x, win.h);
  267. } else if (y > win.h) {
  268. WARP(x, 0);
  269. }
  270. dx += x - ox;
  271. dy += y - oy;
  272. ox = x;
  273. oy = y;
  274. break;
  275. }
  276. if (dragging)
  277. next = XCheckIfEvent(win.env.dpy, &e, is_motionnotify, None);
  278. if ((!dragging || !next) && (dx != 0 || dy != 0)) {
  279. if (img_move(&img, dx, dy)) {
  280. img_render(&img);
  281. win_draw(&win);
  282. }
  283. dx = dy = 0;
  284. }
  285. }
  286. win_set_cursor(&win, CURSOR_ARROW);
  287. set_timeout(reset_cursor, TO_CURSOR_HIDE, true);
  288. reset_timeout(redraw);
  289. return false;
  290. }
  291. bool i_zoom(arg_t a)
  292. {
  293. long scale = (long) a;
  294. if (mode != MODE_IMAGE)
  295. return false;
  296. if (scale > 0)
  297. return img_zoom_in(&img);
  298. else if (scale < 0)
  299. return img_zoom_out(&img);
  300. else
  301. return false;
  302. }
  303. bool i_set_zoom(arg_t a)
  304. {
  305. if (mode == MODE_IMAGE)
  306. return img_zoom(&img, (prefix ? prefix : (long) a) / 100.0);
  307. else
  308. return false;
  309. }
  310. bool i_fit_to_win(arg_t a)
  311. {
  312. bool ret = false;
  313. scalemode_t sm = (scalemode_t) a;
  314. if (mode == MODE_IMAGE) {
  315. if ((ret = img_fit_win(&img, sm)))
  316. img_center(&img);
  317. }
  318. return ret;
  319. }
  320. bool i_fit_to_img(arg_t a)
  321. {
  322. int x, y;
  323. unsigned int w, h;
  324. bool ret = false;
  325. if (mode == MODE_IMAGE) {
  326. x = MAX(0, win.x + img.x);
  327. y = MAX(0, win.y + img.y);
  328. w = img.w * img.zoom;
  329. h = img.h * img.zoom;
  330. if ((ret = win_moveresize(&win, x, y, w, h))) {
  331. img.x = x - win.x;
  332. img.y = y - win.y;
  333. img.dirty = true;
  334. }
  335. }
  336. return ret;
  337. }
  338. bool i_rotate(arg_t a)
  339. {
  340. direction_t dir = (direction_t) a;
  341. if (mode == MODE_IMAGE) {
  342. if (dir == DIR_LEFT) {
  343. img_rotate_left(&img);
  344. return true;
  345. } else if (dir == DIR_RIGHT) {
  346. img_rotate_right(&img);
  347. return true;
  348. }
  349. }
  350. return false;
  351. }
  352. bool i_flip(arg_t a)
  353. {
  354. flipdir_t dir = (flipdir_t) a;
  355. if (mode == MODE_IMAGE) {
  356. img_flip(&img, dir);
  357. return true;
  358. } else {
  359. return false;
  360. }
  361. }
  362. bool i_toggle_antialias(arg_t a)
  363. {
  364. if (mode == MODE_IMAGE) {
  365. img_toggle_antialias(&img);
  366. return true;
  367. } else {
  368. return false;
  369. }
  370. }
  371. bool it_toggle_alpha(arg_t a)
  372. {
  373. img.alpha = tns.alpha = !img.alpha;
  374. if (mode == MODE_IMAGE)
  375. img.dirty = true;
  376. else
  377. tns.dirty = true;
  378. return true;
  379. }
  380. bool it_open_with(arg_t a)
  381. {
  382. const char *prog = (const char*) a;
  383. pid_t pid;
  384. if (prog == NULL || *prog == '\0')
  385. return false;
  386. if ((pid = fork()) == 0) {
  387. execlp(prog, prog,
  388. files[mode == MODE_IMAGE ? fileidx : tns.sel].path, NULL);
  389. warn("could not exec: %s", prog);
  390. exit(EXIT_FAILURE);
  391. } else if (pid < 0) {
  392. warn("could not fork. program was: %s", prog);
  393. }
  394. return false;
  395. }
  396. bool it_shell_cmd(arg_t a)
  397. {
  398. int n, status;
  399. const char *cmdline = (const char*) a;
  400. pid_t pid;
  401. if (cmdline == NULL || *cmdline == '\0')
  402. return false;
  403. n = mode == MODE_IMAGE ? fileidx : tns.sel;
  404. if (setenv("SXIV_IMG", files[n].path, 1) < 0) {
  405. warn("could not set env.-variable: SXIV_IMG. command line was: %s",
  406. cmdline);
  407. return false;
  408. }
  409. if ((pid = fork()) == 0) {
  410. execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
  411. warn("could not exec: /bin/sh. command line was: %s", cmdline);
  412. exit(EXIT_FAILURE);
  413. } else if (pid < 0) {
  414. warn("could not fork. command line was: %s", cmdline);
  415. return false;
  416. }
  417. win_set_cursor(&win, CURSOR_WATCH);
  418. waitpid(pid, &status, 0);
  419. if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0)
  420. warn("child exited with non-zero return value: %d. command line was: %s",
  421. WEXITSTATUS(status), cmdline);
  422. if (mode == MODE_IMAGE) {
  423. img_close(&img, true);
  424. load_image(fileidx);
  425. }
  426. if (!tns_load(&tns, n, &files[n], true, mode == MODE_IMAGE) &&
  427. mode == MODE_THUMB)
  428. {
  429. remove_file(tns.sel, false);
  430. tns.dirty = true;
  431. if (tns.sel >= tns.cnt)
  432. tns.sel = tns.cnt - 1;
  433. }
  434. return true;
  435. }