Krolyxon
Commit 2b7192b

Branches

Refs →

nsxivevents.c

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