Krolyxon
Commit 8763f69

Branches

Refs →

nsxivevents.c

598 lines · 12.0 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_NORMAL) {
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 (keymask(&keys[i], kev->state) && ksym == keys[i].ksym) {
96 if (keys[i].handler && keys[i].handler(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_NORMAL) {
110 win_set_cursor(&win, CURSOR_ARROW);
111 timo_cursor = TO_CURSOR_HIDE;
112
113 for (i = 0; i < LEN(buttons); i++) {
114 if (buttonmask(&buttons[i], bev->state) &&
115 bev->button == buttons[i].button)
116 {
117 if (buttons[i].handler && buttons[i].handler(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_NORMAL;
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_NORMAL ? TO_CURSOR_HIDE : 0;
156
157 redraw();
158
159 while (1) {
160 if (mode == MODE_THUMBS && 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], 0))
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_NORMAL)
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/* handler functions for key and button mappings: */
245
246int quit(arg_t a) {
247 cleanup();
248 exit(0);
249}
250
251int reload(arg_t a) {
252 if (mode == MODE_NORMAL) {
253 load_image(fileidx);
254 return 1;
255 } else {
256 return 0;
257 }
258}
259
260int toggle_fullscreen(arg_t a) {
261 win_toggle_fullscreen(&win);
262 if (mode == MODE_NORMAL)
263 img.checkpan = 1;
264 else
265 tns.dirty = 1;
266 timo_redraw = TO_WIN_RESIZE;
267 return 0;
268}
269
270int toggle_antialias(arg_t a) {
271 if (mode == MODE_NORMAL) {
272 img_toggle_antialias(&img);
273 return 1;
274 } else {
275 return 0;
276 }
277}
278
279int toggle_alpha(arg_t a) {
280 if (mode == MODE_NORMAL) {
281 img.alpha ^= 1;
282 return 1;
283 } else {
284 return 0;
285 }
286}
287
288int switch_mode(arg_t a) {
289 if (mode == MODE_NORMAL) {
290 if (!tns.thumbs)
291 tns_init(&tns, filecnt);
292 img_close(&img, 0);
293 win_set_cursor(&win, CURSOR_ARROW);
294 timo_cursor = 0;
295 tns.sel = fileidx;
296 tns.dirty = 1;
297 mode = MODE_THUMBS;
298 } else {
299 timo_cursor = TO_CURSOR_HIDE;
300 load_image(tns.sel);
301 mode = MODE_NORMAL;
302 }
303 return 1;
304}
305
306int navigate(arg_t a) {
307 int n = (int) a;
308
309 if (mode == MODE_NORMAL) {
310 n += fileidx;
311 if (n < 0)
312 n = 0;
313 if (n >= filecnt)
314 n = filecnt - 1;
315
316 if (n != fileidx) {
317 load_image(n);
318 return 1;
319 }
320 }
321 return 0;
322}
323
324int first(arg_t a) {
325 if (mode == MODE_NORMAL && fileidx != 0) {
326 load_image(0);
327 return 1;
328 } else if (mode == MODE_THUMBS && tns.sel != 0) {
329 tns.sel = 0;
330 tns.dirty = 1;
331 return 1;
332 } else {
333 return 0;
334 }
335}
336
337int last(arg_t a) {
338 if (mode == MODE_NORMAL && fileidx != filecnt - 1) {
339 load_image(filecnt - 1);
340 return 1;
341 } else if (mode == MODE_THUMBS && tns.sel != tns.cnt - 1) {
342 tns.sel = tns.cnt - 1;
343 tns.dirty = 1;
344 return 1;
345 } else {
346 return 0;
347 }
348}
349
350int remove_image(arg_t a) {
351 if (mode == MODE_NORMAL) {
352 remove_file(fileidx, 1);
353 load_image(fileidx >= filecnt ? filecnt - 1 : fileidx);
354 return 1;
355 } else if (tns.sel < tns.cnt) {
356 remove_file(tns.sel, 1);
357 tns.dirty = 1;
358 if (tns.sel >= tns.cnt)
359 tns.sel = tns.cnt - 1;
360 return 1;
361 } else {
362 return 0;
363 }
364}
365
366int move(arg_t a) {
367 direction_t dir = (direction_t) a;
368
369 if (mode == MODE_NORMAL)
370 return img_pan(&img, &win, dir, 0);
371 else
372 return tns_move_selection(&tns, &win, dir);
373}
374
375int pan_screen(arg_t a) {
376 direction_t dir = (direction_t) a;
377
378 if (mode == MODE_NORMAL)
379 return img_pan(&img, &win, dir, 1);
380 else
381 return 0;
382}
383
384int pan_edge(arg_t a) {
385 direction_t dir = (direction_t) a;
386
387 if (mode == MODE_NORMAL)
388 return img_pan_edge(&img, &win, dir);
389 else
390 return 0;
391}
392
393/* Xlib helper function for drag() */
394Bool is_motionnotify(Display *d, XEvent *e, XPointer a) {
395 return e != NULL && e->type == MotionNotify;
396}
397
398int drag(arg_t a) {
399 int dx = 0, dy = 0, i, ox, oy, x, y;
400 unsigned int ui;
401 Bool dragging = True, next = False;
402 XEvent e;
403 Window w;
404
405 if (mode != MODE_NORMAL)
406 return 0;
407 if (!XQueryPointer(win.env.dpy, win.xwin, &w, &w, &i, &i, &ox, &oy, &ui))
408 return 0;
409
410 win_set_cursor(&win, CURSOR_HAND);
411
412 while (dragging) {
413 if (!next)
414 XMaskEvent(win.env.dpy,
415 ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e);
416 switch (e.type) {
417 case ButtonPress:
418 case ButtonRelease:
419 dragging = False;
420 break;
421 case MotionNotify:
422 x = e.xmotion.x;
423 y = e.xmotion.y;
424 if (x >= 0 && x <= win.w && y >= 0 && y <= win.h) {
425 dx += x - ox;
426 dy += y - oy;
427 }
428 ox = x;
429 oy = y;
430 break;
431 }
432 if (dragging)
433 next = XCheckIfEvent(win.env.dpy, &e, is_motionnotify, None);
434 if ((!dragging || !next) && (dx != 0 || dy != 0)) {
435 if (img_move(&img, &win, dx, dy))
436 img_render(&img, &win);
437 dx = dy = 0;
438 }
439 }
440
441 win_set_cursor(&win, CURSOR_ARROW);
442 timo_cursor = TO_CURSOR_HIDE;
443 timo_redraw = 0;
444
445 return 0;
446}
447
448int rotate(arg_t a) {
449 direction_t dir = (direction_t) a;
450
451 if (mode == MODE_NORMAL) {
452 if (dir == DIR_LEFT) {
453 img_rotate_left(&img, &win);
454 return 1;
455 } else if (dir == DIR_RIGHT) {
456 img_rotate_right(&img, &win);
457 return 1;
458 }
459 }
460 return 0;
461}
462
463int zoom(arg_t a) {
464 int scale = (int) a;
465
466 if (mode != MODE_NORMAL)
467 return 0;
468 if (scale > 0)
469 return img_zoom_in(&img, &win);
470 else if (scale < 0)
471 return img_zoom_out(&img, &win);
472 else
473 return img_zoom(&img, &win, 1.0);
474}
475
476int fit_to_win(arg_t a) {
477 int ret;
478
479 if (mode == MODE_NORMAL) {
480 if ((ret = img_fit_win(&img, &win)))
481 img_center(&img, &win);
482 return ret;
483 } else {
484 return 0;
485 }
486}
487
488int fit_to_img(arg_t a) {
489 int ret, x, y;
490 unsigned int w, h;
491
492 if (mode == MODE_NORMAL) {
493 x = MAX(0, win.x + img.x);
494 y = MAX(0, win.y + img.y);
495 w = img.w * img.zoom;
496 h = img.h * img.zoom;
497 if ((ret = win_moveresize(&win, x, y, w, h))) {
498 img.x = x - win.x;
499 img.y = y - win.y;
500 }
501 return ret;
502 } else {
503 return 0;
504 }
505}
506
507int open_with(arg_t a) {
508 const char *prog = (const char*) a;
509 pid_t pid;
510
511 if (!prog || !*prog)
512 return 0;
513
514 if((pid = fork()) == 0) {
515 execlp(prog, prog,
516 files[mode == MODE_NORMAL ? fileidx : tns.sel].path, NULL);
517 warn("could not exec: %s", prog);
518 exit(1);
519 } else if (pid < 0) {
520 warn("could not for. program was: %s", prog);
521 }
522
523 return 0;
524}
525
526int run_command(arg_t a) {
527 const char *cline = (const char*) a;
528 char *cn, *cmdline;
529 const char *co, *fpath;
530 int fpcnt, fplen, status;
531 pid_t pid;
532
533 if (!cline || !*cline)
534 return 0;
535
536 /* build command line: */
537 fpcnt = 0;
538 co = cline - 1;
539 while ((co = strchr(co + 1, '#')))
540 fpcnt++;
541 if (!fpcnt)
542 return 0;
543 fpath = files[mode == MODE_NORMAL ? fileidx : tns.sel].path;
544 fplen = strlen(fpath);
545 cn = cmdline = (char*) s_malloc((strlen(cline) + fpcnt * (fplen + 2)) *
546 sizeof(char));
547 /* replace all '#' with filename: */
548 for (co = cline; *co; co++) {
549 if (*co == '#') {
550 *cn++ = '"';
551 strcpy(cn, fpath);
552 cn += fplen;
553 *cn++ = '"';
554 } else {
555 *cn++ = *co;
556 }
557 }
558 *cn = '\0';
559
560 win_set_cursor(&win, CURSOR_WATCH);
561
562 if ((pid = fork()) == 0) {
563 execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL);
564 warn("could not exec: /bin/sh");
565 exit(1);
566 } else if (pid < 0) {
567 warn("could not fork. command line was: %s", cmdline);
568 goto end;
569 }
570
571 waitpid(pid, &status, 0);
572 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
573 warn("child exited with non-zero return value: %d. command line was: %s",
574 WEXITSTATUS(status), cmdline);
575
576 if (mode == MODE_NORMAL) {
577 if (fileidx < tns.cnt)
578 tns_load(&tns, fileidx, &files[fileidx], 1);
579 img_close(&img, 1);
580 load_image(fileidx);
581 } else {
582 if (!tns_load(&tns, tns.sel, &files[tns.sel], 0)) {
583 remove_file(tns.sel, 0);
584 tns.dirty = 1;
585 if (tns.sel >= tns.cnt)
586 tns.sel = tns.cnt - 1;
587 }
588 }
589
590end:
591 if (mode == MODE_THUMBS)
592 win_set_cursor(&win, CURSOR_ARROW);
593 /* else: cursor is reset in redraw() */
594
595 free(cmdline);
596
597 return 1;
598}