A Simple X Image Viewer
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

602 řádky
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
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 2
  7. * of the License, or (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., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, 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 fileinfo_t *files;
  49. extern int filecnt, fileidx;
  50. int timo_cursor;
  51. int timo_redraw;
  52. void redraw() {
  53. if (mode == MODE_IMAGE) {
  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 (keys[i].ksym == ksym && keymask(&keys[i], kev->state)) {
  81. if (keys[i].cmd && keys[i].cmd(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_IMAGE) {
  92. win_set_cursor(&win, CURSOR_ARROW);
  93. timo_cursor = TO_CURSOR_HIDE;
  94. for (i = 0; i < LEN(buttons); i++) {
  95. if (buttons[i].button == bev->button &&
  96. buttonmask(&buttons[i], bev->state))
  97. {
  98. if (buttons[i].cmd && buttons[i].cmd(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_IMAGE;
  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_IMAGE ? TO_CURSOR_HIDE : 0;
  135. redraw();
  136. while (1) {
  137. if (mode == MODE_THUMB && 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, &files[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_IMAGE)
  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. /* command functions for key and button mappings: */
  216. int it_quit(arg_t a) {
  217. cleanup();
  218. exit(0);
  219. }
  220. int it_switch_mode(arg_t a) {
  221. if (mode == MODE_IMAGE) {
  222. if (!tns.thumbs)
  223. tns_init(&tns, filecnt);
  224. img_close(&img, 0);
  225. win_set_cursor(&win, CURSOR_ARROW);
  226. timo_cursor = 0;
  227. tns.sel = fileidx;
  228. tns.dirty = 1;
  229. mode = MODE_THUMB;
  230. } else {
  231. timo_cursor = TO_CURSOR_HIDE;
  232. load_image(tns.sel);
  233. mode = MODE_IMAGE;
  234. }
  235. return 1;
  236. }
  237. int it_toggle_fullscreen(arg_t a) {
  238. win_toggle_fullscreen(&win);
  239. if (mode == MODE_IMAGE)
  240. img.checkpan = 1;
  241. else
  242. tns.dirty = 1;
  243. timo_redraw = TO_WIN_RESIZE;
  244. return 0;
  245. }
  246. int it_reload_image(arg_t a) {
  247. if (mode == MODE_IMAGE) {
  248. load_image(fileidx);
  249. } else if (!tns_load(&tns, tns.sel, &files[tns.sel], 0)) {
  250. remove_file(tns.sel, 0);
  251. tns.dirty = 1;
  252. if (tns.sel >= tns.cnt)
  253. tns.sel = tns.cnt - 1;
  254. }
  255. return 1;
  256. }
  257. int it_remove_image(arg_t a) {
  258. if (mode == MODE_IMAGE) {
  259. remove_file(fileidx, 1);
  260. load_image(fileidx >= filecnt ? filecnt - 1 : fileidx);
  261. return 1;
  262. } else if (tns.sel < tns.cnt) {
  263. remove_file(tns.sel, 1);
  264. tns.dirty = 1;
  265. if (tns.sel >= tns.cnt)
  266. tns.sel = tns.cnt - 1;
  267. return 1;
  268. } else {
  269. return 0;
  270. }
  271. }
  272. int i_navigate(arg_t a) {
  273. int n = (int) a;
  274. if (mode == MODE_IMAGE) {
  275. n += fileidx;
  276. if (n < 0)
  277. n = 0;
  278. if (n >= filecnt)
  279. n = filecnt - 1;
  280. if (n != fileidx) {
  281. load_image(n);
  282. return 1;
  283. }
  284. }
  285. return 0;
  286. }
  287. int it_first(arg_t a) {
  288. if (mode == MODE_IMAGE && fileidx != 0) {
  289. load_image(0);
  290. return 1;
  291. } else if (mode == MODE_THUMB && tns.sel != 0) {
  292. tns.sel = 0;
  293. tns.dirty = 1;
  294. return 1;
  295. } else {
  296. return 0;
  297. }
  298. }
  299. int it_last(arg_t a) {
  300. if (mode == MODE_IMAGE && fileidx != filecnt - 1) {
  301. load_image(filecnt - 1);
  302. return 1;
  303. } else if (mode == MODE_THUMB && tns.sel != tns.cnt - 1) {
  304. tns.sel = tns.cnt - 1;
  305. tns.dirty = 1;
  306. return 1;
  307. } else {
  308. return 0;
  309. }
  310. }
  311. int it_move(arg_t a) {
  312. direction_t dir = (direction_t) a;
  313. if (mode == MODE_IMAGE)
  314. return img_pan(&img, &win, dir, 0);
  315. else
  316. return tns_move_selection(&tns, &win, dir);
  317. }
  318. int i_pan_screen(arg_t a) {
  319. direction_t dir = (direction_t) a;
  320. if (mode == MODE_IMAGE)
  321. return img_pan(&img, &win, dir, 1);
  322. else
  323. return 0;
  324. }
  325. int i_pan_edge(arg_t a) {
  326. direction_t dir = (direction_t) a;
  327. if (mode == MODE_IMAGE)
  328. return img_pan_edge(&img, &win, dir);
  329. else
  330. return 0;
  331. }
  332. /* Xlib helper function for i_drag() */
  333. Bool is_motionnotify(Display *d, XEvent *e, XPointer a) {
  334. return e != NULL && e->type == MotionNotify;
  335. }
  336. int i_drag(arg_t a) {
  337. int dx = 0, dy = 0, i, ox, oy, x, y;
  338. unsigned int ui;
  339. Bool dragging = True, next = False;
  340. XEvent e;
  341. Window w;
  342. if (mode != MODE_IMAGE)
  343. return 0;
  344. if (!XQueryPointer(win.env.dpy, win.xwin, &w, &w, &i, &i, &ox, &oy, &ui))
  345. return 0;
  346. win_set_cursor(&win, CURSOR_HAND);
  347. while (dragging) {
  348. if (!next)
  349. XMaskEvent(win.env.dpy,
  350. ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e);
  351. switch (e.type) {
  352. case ButtonPress:
  353. case ButtonRelease:
  354. dragging = False;
  355. break;
  356. case MotionNotify:
  357. x = e.xmotion.x;
  358. y = e.xmotion.y;
  359. if (x >= 0 && x <= win.w && y >= 0 && y <= win.h) {
  360. dx += x - ox;
  361. dy += y - oy;
  362. }
  363. ox = x;
  364. oy = y;
  365. break;
  366. }
  367. if (dragging)
  368. next = XCheckIfEvent(win.env.dpy, &e, is_motionnotify, None);
  369. if ((!dragging || !next) && (dx != 0 || dy != 0)) {
  370. if (img_move(&img, &win, dx, dy))
  371. img_render(&img, &win);
  372. dx = dy = 0;
  373. }
  374. }
  375. win_set_cursor(&win, CURSOR_ARROW);
  376. timo_cursor = TO_CURSOR_HIDE;
  377. timo_redraw = 0;
  378. return 0;
  379. }
  380. int i_zoom(arg_t a) {
  381. int scale = (int) a;
  382. if (mode != MODE_IMAGE)
  383. return 0;
  384. if (scale > 0)
  385. return img_zoom_in(&img, &win);
  386. else if (scale < 0)
  387. return img_zoom_out(&img, &win);
  388. else
  389. return img_zoom(&img, &win, 1.0);
  390. }
  391. int i_fit_to_win(arg_t a) {
  392. int ret;
  393. if (mode == MODE_IMAGE) {
  394. if ((ret = img_fit_win(&img, &win)))
  395. img_center(&img, &win);
  396. return ret;
  397. } else {
  398. return 0;
  399. }
  400. }
  401. int i_fit_to_img(arg_t a) {
  402. int ret, x, y;
  403. unsigned int w, h;
  404. if (mode == MODE_IMAGE) {
  405. x = MAX(0, win.x + img.x);
  406. y = MAX(0, win.y + img.y);
  407. w = img.w * img.zoom;
  408. h = img.h * img.zoom;
  409. if ((ret = win_moveresize(&win, x, y, w, h))) {
  410. img.x = x - win.x;
  411. img.y = y - win.y;
  412. }
  413. return ret;
  414. } else {
  415. return 0;
  416. }
  417. }
  418. int i_rotate(arg_t a) {
  419. direction_t dir = (direction_t) a;
  420. if (mode == MODE_IMAGE) {
  421. if (dir == DIR_LEFT) {
  422. img_rotate_left(&img, &win);
  423. return 1;
  424. } else if (dir == DIR_RIGHT) {
  425. img_rotate_right(&img, &win);
  426. return 1;
  427. }
  428. }
  429. return 0;
  430. }
  431. int i_toggle_antialias(arg_t a) {
  432. if (mode == MODE_IMAGE) {
  433. img_toggle_antialias(&img);
  434. return 1;
  435. } else {
  436. return 0;
  437. }
  438. }
  439. int i_toggle_alpha(arg_t a) {
  440. if (mode == MODE_IMAGE) {
  441. img.alpha ^= 1;
  442. return 1;
  443. } else {
  444. return 0;
  445. }
  446. }
  447. int it_open_with(arg_t a) {
  448. const char *prog = (const char*) a;
  449. pid_t pid;
  450. if (!prog || !*prog)
  451. return 0;
  452. if((pid = fork()) == 0) {
  453. execlp(prog, prog,
  454. files[mode == MODE_IMAGE ? fileidx : tns.sel].path, NULL);
  455. warn("could not exec: %s", prog);
  456. exit(1);
  457. } else if (pid < 0) {
  458. warn("could not for. program was: %s", prog);
  459. }
  460. return 0;
  461. }
  462. int it_shell_cmd(arg_t a) {
  463. const char *cline = (const char*) a;
  464. char *cn, *cmdline;
  465. const char *co, *fpath;
  466. int fpcnt, fplen, status;
  467. pid_t pid;
  468. if (!cline || !*cline)
  469. return 0;
  470. /* build command line: */
  471. fpcnt = 0;
  472. co = cline - 1;
  473. while ((co = strchr(co + 1, '#')))
  474. fpcnt++;
  475. if (!fpcnt)
  476. return 0;
  477. fpath = files[mode == MODE_IMAGE ? fileidx : tns.sel].path;
  478. fplen = strlen(fpath);
  479. cn = cmdline = (char*) s_malloc((strlen(cline) + fpcnt * (fplen + 2)) *
  480. sizeof(char));
  481. /* replace all '#' with filename: */
  482. for (co = cline; *co; co++) {
  483. if (*co == '#') {
  484. *cn++ = '"';
  485. strcpy(cn, fpath);
  486. cn += fplen;
  487. *cn++ = '"';
  488. } else {
  489. *cn++ = *co;
  490. }
  491. }
  492. *cn = '\0';
  493. win_set_cursor(&win, CURSOR_WATCH);
  494. if ((pid = fork()) == 0) {
  495. execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
  496. warn("could not exec: /bin/sh");
  497. exit(1);
  498. } else if (pid < 0) {
  499. warn("could not fork. command line was: %s", cmdline);
  500. goto end;
  501. }
  502. waitpid(pid, &status, 0);
  503. if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
  504. warn("child exited with non-zero return value: %d. command line was: %s",
  505. WEXITSTATUS(status), cmdline);
  506. if (mode == MODE_IMAGE) {
  507. if (fileidx < tns.cnt)
  508. tns_load(&tns, fileidx, &files[fileidx], 1);
  509. img_close(&img, 1);
  510. load_image(fileidx);
  511. } else {
  512. if (!tns_load(&tns, tns.sel, &files[tns.sel], 0)) {
  513. remove_file(tns.sel, 0);
  514. tns.dirty = 1;
  515. if (tns.sel >= tns.cnt)
  516. tns.sel = tns.cnt - 1;
  517. }
  518. }
  519. end:
  520. if (mode == MODE_THUMB)
  521. win_set_cursor(&win, CURSOR_ARROW);
  522. /* else: cursor is reset in redraw() */
  523. free(cmdline);
  524. return 1;
  525. }