A Simple X Image Viewer
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

599 lignes
12 KiB

  1. /* sxiv: events.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. #define _GENERAL_CONFIG
  19. #define _MAPPINGS_CONFIG
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <sys/time.h>
  24. #include <sys/wait.h>
  25. #include <X11/keysym.h>
  26. #include <X11/Xutil.h>
  27. #include "events.h"
  28. #include "image.h"
  29. #include "thumbs.h"
  30. #include "types.h"
  31. #include "util.h"
  32. #include "window.h"
  33. #include "config.h"
  34. /* timeouts in milliseconds: */
  35. enum {
  36. TO_WIN_RESIZE = 75,
  37. TO_CURSOR_HIDE = 1500,
  38. TO_THUMBS_LOAD = 200
  39. };
  40. void cleanup();
  41. void remove_file(int, unsigned char);
  42. void load_image(int);
  43. void update_title();
  44. extern appmode_t mode;
  45. extern img_t img;
  46. extern tns_t tns;
  47. extern win_t win;
  48. extern char **filenames;
  49. extern int filecnt, fileidx;
  50. int timo_cursor;
  51. int timo_redraw;
  52. void redraw() {
  53. if (mode == MODE_NORMAL) {
  54. img_render(&img, &win);
  55. if (timo_cursor)
  56. win_set_cursor(&win, CURSOR_ARROW);
  57. else
  58. win_set_cursor(&win, CURSOR_NONE);
  59. } else {
  60. tns_render(&tns, &win);
  61. }
  62. update_title();
  63. timo_redraw = 0;
  64. }
  65. Bool keymask(const keymap_t *k, unsigned int state) {
  66. return (k->ctrl ? ControlMask : 0) == (state & ControlMask);
  67. }
  68. Bool buttonmask(const button_t *b, unsigned int state) {
  69. return ((b->ctrl ? ControlMask : 0) | (b->shift ? ShiftMask : 0)) ==
  70. (state & (ControlMask | ShiftMask));
  71. }
  72. void on_keypress(XKeyEvent *kev) {
  73. int i;
  74. KeySym ksym;
  75. char key;
  76. if (!kev)
  77. return;
  78. XLookupString(kev, &key, 1, &ksym, NULL);
  79. for (i = 0; i < LEN(keys); i++) {
  80. if (keymask(&keys[i], kev->state) && ksym == keys[i].ksym) {
  81. if (keys[i].handler && keys[i].handler(keys[i].arg))
  82. redraw();
  83. return;
  84. }
  85. }
  86. }
  87. void on_buttonpress(XButtonEvent *bev) {
  88. int i, sel;
  89. if (!bev)
  90. return;
  91. if (mode == MODE_NORMAL) {
  92. win_set_cursor(&win, CURSOR_ARROW);
  93. timo_cursor = TO_CURSOR_HIDE;
  94. for (i = 0; i < LEN(buttons); i++) {
  95. if (buttonmask(&buttons[i], bev->state) &&
  96. bev->button == buttons[i].button)
  97. {
  98. if (buttons[i].handler && buttons[i].handler(buttons[i].arg))
  99. redraw();
  100. return;
  101. }
  102. }
  103. } else {
  104. /* thumbnail mode (hard-coded) */
  105. switch (bev->button) {
  106. case Button1:
  107. if ((sel = tns_translate(&tns, bev->x, bev->y)) >= 0) {
  108. if (sel == tns.sel) {
  109. load_image(tns.sel);
  110. mode = MODE_NORMAL;
  111. timo_cursor = TO_CURSOR_HIDE;
  112. } else {
  113. tns_highlight(&tns, &win, tns.sel, False);
  114. tns_highlight(&tns, &win, sel, True);
  115. tns.sel = sel;
  116. }
  117. redraw();
  118. break;
  119. }
  120. break;
  121. case Button4:
  122. case Button5:
  123. if (tns_scroll(&tns, bev->button == Button4 ? DIR_UP : DIR_DOWN))
  124. redraw();
  125. break;
  126. }
  127. }
  128. }
  129. void run() {
  130. int xfd, timeout;
  131. fd_set fds;
  132. struct timeval tt, t0, t1;
  133. XEvent ev;
  134. timo_cursor = mode == MODE_NORMAL ? TO_CURSOR_HIDE : 0;
  135. redraw();
  136. while (1) {
  137. if (mode == MODE_THUMBS && tns.cnt < filecnt) {
  138. /* load thumbnails */
  139. win_set_cursor(&win, CURSOR_WATCH);
  140. gettimeofday(&t0, 0);
  141. while (tns.cnt < filecnt && !XPending(win.env.dpy)) {
  142. if (tns_load(&tns, tns.cnt, filenames[tns.cnt], 0))
  143. tns.cnt++;
  144. else
  145. remove_file(tns.cnt, 0);
  146. gettimeofday(&t1, 0);
  147. if (TIMEDIFF(&t1, &t0) >= TO_THUMBS_LOAD)
  148. break;
  149. }
  150. if (tns.cnt == filecnt)
  151. win_set_cursor(&win, CURSOR_ARROW);
  152. if (!XPending(win.env.dpy)) {
  153. redraw();
  154. continue;
  155. } else {
  156. timo_redraw = TO_THUMBS_LOAD;
  157. }
  158. } else if (timo_cursor || timo_redraw) {
  159. /* check active timeouts */
  160. gettimeofday(&t0, 0);
  161. timeout = MIN(timo_cursor + 1, timo_redraw + 1);
  162. MSEC_TO_TIMEVAL(timeout, &tt);
  163. xfd = ConnectionNumber(win.env.dpy);
  164. FD_ZERO(&fds);
  165. FD_SET(xfd, &fds);
  166. if (!XPending(win.env.dpy))
  167. select(xfd + 1, &fds, 0, 0, &tt);
  168. gettimeofday(&t1, 0);
  169. timeout = MIN(TIMEDIFF(&t1, &t0), timeout);
  170. /* timeouts fired? */
  171. if (timo_cursor) {
  172. timo_cursor = MAX(0, timo_cursor - timeout);
  173. if (!timo_cursor)
  174. win_set_cursor(&win, CURSOR_NONE);
  175. }
  176. if (timo_redraw) {
  177. timo_redraw = MAX(0, timo_redraw - timeout);
  178. if (!timo_redraw)
  179. redraw();
  180. }
  181. if ((timo_cursor || timo_redraw) && !XPending(win.env.dpy))
  182. continue;
  183. }
  184. if (!XNextEvent(win.env.dpy, &ev)) {
  185. /* handle events */
  186. switch (ev.type) {
  187. case ButtonPress:
  188. on_buttonpress(&ev.xbutton);
  189. break;
  190. case ClientMessage:
  191. if ((Atom) ev.xclient.data.l[0] == wm_delete_win)
  192. return;
  193. break;
  194. case ConfigureNotify:
  195. if (win_configure(&win, &ev.xconfigure)) {
  196. timo_redraw = TO_WIN_RESIZE;
  197. if (mode == MODE_NORMAL)
  198. img.checkpan = 1;
  199. else
  200. tns.dirty = 1;
  201. }
  202. break;
  203. case KeyPress:
  204. on_keypress(&ev.xkey);
  205. break;
  206. case MotionNotify:
  207. if (!timo_cursor)
  208. win_set_cursor(&win, CURSOR_ARROW);
  209. timo_cursor = TO_CURSOR_HIDE;
  210. break;
  211. }
  212. }
  213. }
  214. }
  215. /* handler functions for key and button mappings: */
  216. int quit(arg_t a) {
  217. cleanup();
  218. exit(0);
  219. }
  220. int reload(arg_t a) {
  221. if (mode == MODE_NORMAL) {
  222. load_image(fileidx);
  223. return 1;
  224. } else {
  225. return 0;
  226. }
  227. }
  228. int toggle_fullscreen(arg_t a) {
  229. win_toggle_fullscreen(&win);
  230. if (mode == MODE_NORMAL)
  231. img.checkpan = 1;
  232. else
  233. tns.dirty = 1;
  234. timo_redraw = TO_WIN_RESIZE;
  235. return 0;
  236. }
  237. int toggle_antialias(arg_t a) {
  238. if (mode == MODE_NORMAL) {
  239. img_toggle_antialias(&img);
  240. return 1;
  241. } else {
  242. return 0;
  243. }
  244. }
  245. int toggle_alpha(arg_t a) {
  246. if (mode == MODE_NORMAL) {
  247. img.alpha ^= 1;
  248. return 1;
  249. } else {
  250. return 0;
  251. }
  252. }
  253. int switch_mode(arg_t a) {
  254. if (mode == MODE_NORMAL) {
  255. if (!tns.thumbs)
  256. tns_init(&tns, filecnt);
  257. img_close(&img, 0);
  258. win_set_cursor(&win, CURSOR_ARROW);
  259. timo_cursor = 0;
  260. tns.sel = fileidx;
  261. tns.dirty = 1;
  262. mode = MODE_THUMBS;
  263. } else {
  264. timo_cursor = TO_CURSOR_HIDE;
  265. load_image(tns.sel);
  266. mode = MODE_NORMAL;
  267. }
  268. return 1;
  269. }
  270. int navigate(arg_t a) {
  271. int n = (int) a;
  272. if (mode == MODE_NORMAL) {
  273. n += fileidx;
  274. if (n < 0)
  275. n = 0;
  276. if (n >= filecnt)
  277. n = filecnt - 1;
  278. if (n != fileidx) {
  279. load_image(n);
  280. return 1;
  281. }
  282. }
  283. return 0;
  284. }
  285. int first(arg_t a) {
  286. if (mode == MODE_NORMAL && fileidx != 0) {
  287. load_image(0);
  288. return 1;
  289. } else if (mode == MODE_THUMBS && tns.sel != 0) {
  290. tns.sel = 0;
  291. tns.dirty = 1;
  292. return 1;
  293. } else {
  294. return 0;
  295. }
  296. }
  297. int last(arg_t a) {
  298. if (mode == MODE_NORMAL && fileidx != filecnt - 1) {
  299. load_image(filecnt - 1);
  300. return 1;
  301. } else if (mode == MODE_THUMBS && tns.sel != tns.cnt - 1) {
  302. tns.sel = tns.cnt - 1;
  303. tns.dirty = 1;
  304. return 1;
  305. } else {
  306. return 0;
  307. }
  308. }
  309. int remove_image(arg_t a) {
  310. if (mode == MODE_NORMAL) {
  311. remove_file(fileidx, 1);
  312. load_image(fileidx >= filecnt ? filecnt - 1 : fileidx);
  313. return 1;
  314. } else if (tns.sel < tns.cnt) {
  315. remove_file(tns.sel, 1);
  316. tns.dirty = 1;
  317. if (tns.sel >= tns.cnt)
  318. tns.sel = tns.cnt - 1;
  319. return 1;
  320. } else {
  321. return 0;
  322. }
  323. }
  324. int move(arg_t a) {
  325. direction_t dir = (direction_t) a;
  326. if (mode == MODE_NORMAL)
  327. return img_pan(&img, &win, dir, 0);
  328. else
  329. return tns_move_selection(&tns, &win, dir);
  330. }
  331. int pan_screen(arg_t a) {
  332. direction_t dir = (direction_t) a;
  333. if (mode == MODE_NORMAL)
  334. return img_pan(&img, &win, dir, 1);
  335. else
  336. return 0;
  337. }
  338. int pan_edge(arg_t a) {
  339. direction_t dir = (direction_t) a;
  340. if (mode == MODE_NORMAL)
  341. return img_pan_edge(&img, &win, dir);
  342. else
  343. return 0;
  344. }
  345. /* Xlib helper function for drag() */
  346. Bool is_motionnotify(Display *d, XEvent *e, XPointer a) {
  347. return e != NULL && e->type == MotionNotify;
  348. }
  349. int drag(arg_t a) {
  350. int dx = 0, dy = 0, i, ox, oy, x, y;
  351. unsigned int ui;
  352. Bool dragging = True, next = False;
  353. XEvent e;
  354. Window w;
  355. if (mode != MODE_NORMAL)
  356. return 0;
  357. if (!XQueryPointer(win.env.dpy, win.xwin, &w, &w, &i, &i, &ox, &oy, &ui))
  358. return 0;
  359. win_set_cursor(&win, CURSOR_HAND);
  360. while (dragging) {
  361. if (!next)
  362. XMaskEvent(win.env.dpy,
  363. ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e);
  364. switch (e.type) {
  365. case ButtonPress:
  366. case ButtonRelease:
  367. dragging = False;
  368. break;
  369. case MotionNotify:
  370. x = e.xmotion.x;
  371. y = e.xmotion.y;
  372. if (x >= 0 && x <= win.w && y >= 0 && y <= win.h) {
  373. dx += x - ox;
  374. dy += y - oy;
  375. }
  376. ox = x;
  377. oy = y;
  378. break;
  379. }
  380. if (dragging)
  381. next = XCheckIfEvent(win.env.dpy, &e, is_motionnotify, None);
  382. if ((!dragging || !next) && (dx != 0 || dy != 0)) {
  383. if (img_move(&img, &win, dx, dy))
  384. img_render(&img, &win);
  385. dx = dy = 0;
  386. }
  387. }
  388. win_set_cursor(&win, CURSOR_ARROW);
  389. timo_cursor = TO_CURSOR_HIDE;
  390. timo_redraw = 0;
  391. return 0;
  392. }
  393. int rotate(arg_t a) {
  394. direction_t dir = (direction_t) a;
  395. if (mode == MODE_NORMAL) {
  396. if (dir == DIR_LEFT) {
  397. img_rotate_left(&img, &win);
  398. return 1;
  399. } else if (dir == DIR_RIGHT) {
  400. img_rotate_right(&img, &win);
  401. return 1;
  402. }
  403. }
  404. return 0;
  405. }
  406. int zoom(arg_t a) {
  407. int scale = (int) a;
  408. if (mode != MODE_NORMAL)
  409. return 0;
  410. if (scale > 0)
  411. return img_zoom_in(&img, &win);
  412. else if (scale < 0)
  413. return img_zoom_out(&img, &win);
  414. else
  415. return img_zoom(&img, &win, 1.0);
  416. }
  417. int fit_to_win(arg_t a) {
  418. int ret;
  419. if (mode == MODE_NORMAL) {
  420. if ((ret = img_fit_win(&img, &win)))
  421. img_center(&img, &win);
  422. return ret;
  423. } else {
  424. return 0;
  425. }
  426. }
  427. int fit_to_img(arg_t a) {
  428. int ret, x, y;
  429. unsigned int w, h;
  430. if (mode == MODE_NORMAL) {
  431. x = MAX(0, win.x + img.x);
  432. y = MAX(0, win.y + img.y);
  433. w = img.w * img.zoom;
  434. h = img.h * img.zoom;
  435. if ((ret = win_moveresize(&win, x, y, w, h))) {
  436. img.x = x - win.x;
  437. img.y = y - win.y;
  438. }
  439. return ret;
  440. } else {
  441. return 0;
  442. }
  443. }
  444. int open_with(arg_t a) {
  445. const char *prog = (const char*) a;
  446. pid_t pid;
  447. if (!prog || !*prog)
  448. return 0;
  449. if((pid = fork()) == 0) {
  450. execlp(prog, prog,
  451. filenames[mode == MODE_NORMAL ? fileidx : tns.sel], NULL);
  452. warn("could not exec: %s", prog);
  453. exit(1);
  454. } else if (pid < 0) {
  455. warn("could not for. program was: %s", prog);
  456. }
  457. return 0;
  458. }
  459. int run_command(arg_t a) {
  460. const char *cline = (const char*) a;
  461. char *cn, *cmdline;
  462. const char *co, *fname;
  463. int fncnt, fnlen, status;
  464. pid_t pid;
  465. if (!cline || !*cline)
  466. return 0;
  467. /* build command line: */
  468. fncnt = 0;
  469. co = cline - 1;
  470. while ((co = strchr(co + 1, '#')))
  471. fncnt++;
  472. if (!fncnt)
  473. return 0;
  474. fname = filenames[mode == MODE_NORMAL ? fileidx : tns.sel];
  475. fnlen = strlen(fname);
  476. cn = cmdline = (char*) s_malloc((strlen(cline) + fncnt * (fnlen + 2)) *
  477. sizeof(char));
  478. /* replace all '#' with filename: */
  479. for (co = cline; *co; co++) {
  480. if (*co == '#') {
  481. *cn++ = '"';
  482. strcpy(cn, fname);
  483. cn += fnlen;
  484. *cn++ = '"';
  485. } else {
  486. *cn++ = *co;
  487. }
  488. }
  489. *cn = '\0';
  490. win_set_cursor(&win, CURSOR_WATCH);
  491. if ((pid = fork()) == 0) {
  492. execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
  493. warn("could not exec: /bin/sh");
  494. exit(1);
  495. } else if (pid < 0) {
  496. warn("could not fork. command line was: %s", cmdline);
  497. goto end;
  498. }
  499. waitpid(pid, &status, 0);
  500. if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
  501. warn("child exited with non-zero return value: %d. command line was: %s",
  502. WEXITSTATUS(status), cmdline);
  503. if (mode == MODE_NORMAL) {
  504. if (fileidx < tns.cnt)
  505. tns_load(&tns, fileidx, filenames[fileidx], 1);
  506. img_close(&img, 1);
  507. load_image(fileidx);
  508. } else {
  509. if (!tns_load(&tns, tns.sel, filenames[tns.sel], 0)) {
  510. remove_file(tns.sel, 0);
  511. tns.dirty = 1;
  512. if (tns.sel >= tns.cnt)
  513. tns.sel = tns.cnt - 1;
  514. }
  515. }
  516. end:
  517. if (mode == MODE_THUMBS)
  518. win_set_cursor(&win, CURSOR_ARROW);
  519. /* else: cursor is reset in redraw() */
  520. free(cmdline);
  521. return 1;
  522. }