Krolyxon
Commit 1d7849e

Branches

Refs →

nsxivevents.c

601 lines · 12.2 KB · C

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