A Simple X Image Viewer
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

506 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. bool i_drag(arg_t a)
  233. {
  234. int dx = 0, dy = 0, i, ox, oy, x, y;
  235. unsigned int ui;
  236. bool dragging = true, next = false;
  237. XEvent e;
  238. Window w;
  239. if (mode != MODE_IMAGE)
  240. return false;
  241. if (!XQueryPointer(win.env.dpy, win.xwin, &w, &w, &i, &i, &ox, &oy, &ui))
  242. return false;
  243. win_set_cursor(&win, CURSOR_HAND);
  244. while (dragging) {
  245. if (!next)
  246. XMaskEvent(win.env.dpy,
  247. ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e);
  248. switch (e.type) {
  249. case ButtonPress:
  250. case ButtonRelease:
  251. dragging = false;
  252. break;
  253. case MotionNotify:
  254. x = e.xmotion.x;
  255. y = e.xmotion.y;
  256. #define WARP(x, y) \
  257. XWarpPointer(win.env.dpy, \
  258. None, win.xwin, \
  259. 0, 0, 0, 0, \
  260. x, y); \
  261. ox = x, oy = y; \
  262. break
  263. /* wrap the mouse around */
  264. if(x < 0){
  265. WARP(win.w, y);
  266. }else if(x > win.w){
  267. WARP(0, y);
  268. }else if(y < 0){
  269. WARP(x, win.h);
  270. }else if(y > win.h){
  271. WARP(x, 0);
  272. }
  273. dx += x - ox;
  274. dy += y - oy;
  275. ox = x;
  276. oy = y;
  277. break;
  278. }
  279. if (dragging)
  280. next = XCheckIfEvent(win.env.dpy, &e, is_motionnotify, None);
  281. if ((!dragging || !next) && (dx != 0 || dy != 0)) {
  282. if (img_move(&img, dx, dy)) {
  283. img_render(&img);
  284. win_draw(&win);
  285. }
  286. dx = dy = 0;
  287. }
  288. }
  289. win_set_cursor(&win, CURSOR_ARROW);
  290. set_timeout(reset_cursor, TO_CURSOR_HIDE, true);
  291. reset_timeout(redraw);
  292. return false;
  293. }
  294. bool i_zoom(arg_t a)
  295. {
  296. long scale = (long) a;
  297. if (mode != MODE_IMAGE)
  298. return false;
  299. if (scale > 0)
  300. return img_zoom_in(&img);
  301. else if (scale < 0)
  302. return img_zoom_out(&img);
  303. else
  304. return false;
  305. }
  306. bool i_set_zoom(arg_t a)
  307. {
  308. if (mode == MODE_IMAGE)
  309. return img_zoom(&img, (prefix ? prefix : (long) a) / 100.0);
  310. else
  311. return false;
  312. }
  313. bool i_fit_to_win(arg_t a)
  314. {
  315. bool ret = false;
  316. scalemode_t sm = (scalemode_t) a;
  317. if (mode == MODE_IMAGE) {
  318. if ((ret = img_fit_win(&img, sm)))
  319. img_center(&img);
  320. }
  321. return ret;
  322. }
  323. bool i_fit_to_img(arg_t a)
  324. {
  325. int x, y;
  326. unsigned int w, h;
  327. bool ret = false;
  328. if (mode == MODE_IMAGE) {
  329. x = MAX(0, win.x + img.x);
  330. y = MAX(0, win.y + img.y);
  331. w = img.w * img.zoom;
  332. h = img.h * img.zoom;
  333. if ((ret = win_moveresize(&win, x, y, w, h))) {
  334. img.x = x - win.x;
  335. img.y = y - win.y;
  336. img.dirty = true;
  337. }
  338. }
  339. return ret;
  340. }
  341. bool i_rotate(arg_t a)
  342. {
  343. direction_t dir = (direction_t) a;
  344. if (mode == MODE_IMAGE) {
  345. if (dir == DIR_LEFT) {
  346. img_rotate_left(&img);
  347. return true;
  348. } else if (dir == DIR_RIGHT) {
  349. img_rotate_right(&img);
  350. return true;
  351. }
  352. }
  353. return false;
  354. }
  355. bool i_flip(arg_t a)
  356. {
  357. flipdir_t dir = (flipdir_t) a;
  358. if (mode == MODE_IMAGE) {
  359. img_flip(&img, dir);
  360. return true;
  361. } else {
  362. return false;
  363. }
  364. }
  365. bool i_toggle_antialias(arg_t a)
  366. {
  367. if (mode == MODE_IMAGE) {
  368. img_toggle_antialias(&img);
  369. return true;
  370. } else {
  371. return false;
  372. }
  373. }
  374. bool it_toggle_alpha(arg_t a)
  375. {
  376. img.alpha = tns.alpha = !img.alpha;
  377. if (mode == MODE_IMAGE)
  378. img.dirty = true;
  379. else
  380. tns.dirty = true;
  381. return true;
  382. }
  383. bool it_open_with(arg_t a)
  384. {
  385. const char *prog = (const char*) a;
  386. pid_t pid;
  387. if (prog == NULL || *prog == '\0')
  388. return false;
  389. if ((pid = fork()) == 0) {
  390. execlp(prog, prog,
  391. files[mode == MODE_IMAGE ? fileidx : tns.sel].path, NULL);
  392. warn("could not exec: %s", prog);
  393. exit(EXIT_FAILURE);
  394. } else if (pid < 0) {
  395. warn("could not fork. program was: %s", prog);
  396. }
  397. return false;
  398. }
  399. bool it_shell_cmd(arg_t a)
  400. {
  401. int n, status;
  402. const char *cmdline = (const char*) a;
  403. pid_t pid;
  404. if (cmdline == NULL || *cmdline == '\0')
  405. return false;
  406. n = mode == MODE_IMAGE ? fileidx : tns.sel;
  407. if (setenv("SXIV_IMG", files[n].path, 1) < 0) {
  408. warn("could not set env.-variable: SXIV_IMG. command line was: %s",
  409. cmdline);
  410. return false;
  411. }
  412. if ((pid = fork()) == 0) {
  413. execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
  414. warn("could not exec: /bin/sh. command line was: %s", cmdline);
  415. exit(EXIT_FAILURE);
  416. } else if (pid < 0) {
  417. warn("could not fork. command line was: %s", cmdline);
  418. return false;
  419. }
  420. win_set_cursor(&win, CURSOR_WATCH);
  421. waitpid(pid, &status, 0);
  422. if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0)
  423. warn("child exited with non-zero return value: %d. command line was: %s",
  424. WEXITSTATUS(status), cmdline);
  425. if (mode == MODE_IMAGE) {
  426. img_close(&img, true);
  427. load_image(fileidx);
  428. }
  429. if (!tns_load(&tns, n, &files[n], true, mode == MODE_IMAGE) &&
  430. mode == MODE_THUMB)
  431. {
  432. remove_file(tns.sel, false);
  433. tns.dirty = true;
  434. if (tns.sel >= tns.cnt)
  435. tns.sel = tns.cnt - 1;
  436. }
  437. return true;
  438. }