input: timers: Implement remove + docs.
[gfxprim/pasky.git] / demos / spiv / spiv.c
blob9b34c35b04a9ba552d3d86b56505bd1877fbb56d
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim 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 GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 SPIV -- Simple yet Powerful Image Viewer.
29 #include <unistd.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <string.h>
33 #include <pthread.h>
35 #include <GP.h>
36 #include <input/GP_InputDriverLinux.h>
38 #include "image_cache.h"
39 #include "image_list.h"
40 #include "image_actions.h"
41 #include "spiv_help.h"
42 #include "cpu_timer.h"
44 static GP_Pixel black_pixel;
45 static GP_Pixel white_pixel;
46 static GP_Pixel gray_pixel;
48 static GP_Backend *backend = NULL;
50 /* image loader thread */
51 static int abort_flag = 0;
52 static int show_progress = 0;
54 enum zoom_type {
56 * Resize image to fit current size of the window.
58 ZOOM_FIT,
61 * Use zoom set in zoom float and zoom offsets.
63 ZOOM_FIXED,
66 * Fixed zoom but spiv tries to change
67 * the window size to fit the image size
69 ZOOM_FIXED_WIN,
72 * Do not upscale images but downscale them
73 * if they are too big.
75 ZOOM_FIT_DOWNSCALE,
78 struct loader_params {
79 /* current image path */
80 const char *img_path;
81 /* current resize ratio */
82 float rat;
84 /* show loader progress */
85 long show_progress:1;
86 long show_progress_once:2;
87 /* show image info in topleft corner */
88 long show_info:3;
89 /* use nearest neighbour resampling first */
90 long show_nn_first:4;
91 /* use dithering when blitting to display */
92 long use_dithering:5;
93 /* use low pass before resampling */
94 long use_low_pass:6;
95 /* image orientation 0, 90, 180, 270 */
96 int rotate;
97 /* resampling method */
98 int resampling_method;
100 /* offset in pixels */
101 unsigned int zoom_x_offset;
102 unsigned int zoom_y_offset;
104 /* zoom */
105 enum zoom_type zoom_type;
106 float zoom;
108 /* caches for loaded images */
109 struct image_cache *img_resized_cache;
110 struct image_cache *img_orig_cache;
112 /* image list structure */
113 struct image_list *img_list;
116 static int image_loader_callback(GP_ProgressCallback *self)
118 static GP_Size size = 0;
119 GP_Context *c = backend->context;
121 if (abort_flag)
122 return 1;
124 if (!show_progress)
125 return 0;
127 char buf[100];
129 snprintf(buf, sizeof(buf), "%s ... %-3.1f%%",
130 (const char*)self->priv, self->percentage);
132 int align = GP_ALIGN_CENTER|GP_VALIGN_ABOVE;
134 size = GP_TextWidth(NULL, buf);
136 int start = c->w/2 - size/2 - 10;
137 int end = c->w/2 + size/2 + 10;
138 int middle = start + (end - start) * self->percentage / 100;
139 int top = c->h - GP_TextHeight(NULL) - 11;
141 GP_FillRectXYXY(c, start, c->h - 1, middle, top, gray_pixel);
142 GP_FillRectXYXY(c, middle, c->h - 1, end, top, black_pixel);
144 GP_Text(c, NULL, c->w/2, c->h - 5, align,
145 white_pixel, black_pixel, buf);
147 GP_BackendUpdateRect(backend, start, c->h - 1, end, top);
149 return 0;
152 static GP_Context *load_image(struct loader_params *params, int elevate);
155 * Ask backend to resize window may not be implemented or authorized. If
156 * backend (window) is resized we will get SYS_RESIZE event, see the main event
157 * loop.
159 static void resize_backend(struct loader_params *params,
160 float ratio, int shift_flag)
162 GP_Context *img = load_image(params, 1);
164 if (!shift_flag)
165 ratio = 1.00 / ratio;
167 unsigned int w = img->w * ratio + 0.5;
168 unsigned int h = img->h * ratio + 0.5;
170 GP_BackendResize(backend, w, h);
174 static float calc_img_size(struct loader_params *params,
175 uint32_t img_w, uint32_t img_h,
176 uint32_t src_w, uint32_t src_h)
178 float w_rat;
179 float h_rat;
181 switch (params->zoom_type) {
182 case ZOOM_FIT_DOWNSCALE:
183 if (img_w <= src_w && img_h <= src_h)
184 return 1.00;
185 case ZOOM_FIT:
186 w_rat = 1.00 * src_w / img_w;
187 h_rat = 1.00 * src_h / img_h;
188 return GP_MIN(w_rat, h_rat);
189 case ZOOM_FIXED:
190 return params->zoom;
191 case ZOOM_FIXED_WIN:
192 resize_backend(params, params->zoom, 0);
193 return params->zoom;
196 return 1.00;
199 static const char *img_name(const char *img_path)
201 int i, len = strlen(img_path);
203 for (i = len - 1; i > 0; i--) {
204 if (img_path[i] == '/')
205 return &img_path[i+1];
208 return img_path;
211 static void set_caption(const char *path, float rat)
213 char buf[256];
215 snprintf(buf, sizeof(buf), "Spiv ~ %s 1:%3.3f", img_name(path), rat);
217 GP_BackendSetCaption(backend, buf);
221 * Loads image
223 static GP_Context *load_image(struct loader_params *params, int elevate)
225 struct cpu_timer timer;
226 GP_Context *img, *context = backend->context;
228 GP_ProgressCallback callback = {.callback = image_loader_callback,
229 .priv = "Loading image"};
231 img = image_cache_get(params->img_orig_cache, params->img_path, 0, 0, elevate);
233 /* Image not cached, load it */
234 if (img == NULL) {
235 fprintf(stderr, "Loading '%s'\n", params->img_path);
237 cpu_timer_start(&timer, "Loading");
238 if ((img = GP_LoadImage(params->img_path, &callback)) == NULL) {
240 if (errno == ECANCELED)
241 return NULL;
243 GP_Fill(context, black_pixel);
244 GP_Print(context, NULL, context->w/2, context->h/2 - 10,
245 GP_ALIGN_CENTER|GP_VALIGN_CENTER, white_pixel, black_pixel,
246 "'%s'", params->img_path);
247 GP_Print(context, NULL, context->w/2, context->h/2 + 10,
248 GP_ALIGN_CENTER|GP_VALIGN_CENTER, white_pixel, black_pixel,
249 "Failed to load image :( (%s)", strerror(errno));
250 GP_BackendFlip(backend);
251 return NULL;
254 /* Workaround */
255 // if (img->pixel_type != GP_PIXEL_RGB888) {
256 // GP_Context *tmp;
257 // tmp = GP_ContextConvertAlloc(img, GP_PIXEL_RGB888);
258 // GP_ContextFree(img);
259 // img = tmp;
260 // }
262 image_cache_put(params->img_orig_cache, img, params->img_path, 0, 0);
264 cpu_timer_stop(&timer);
267 return img;
271 * Fill context with chessboard-like pattern.
273 static void pattern_fill(GP_Context *ctx, unsigned int x0, unsigned int y0, unsigned int w, unsigned int h)
275 unsigned int x, y;
277 GP_Pixel g1 = GP_RGBToContextPixel(0x64, 0x64, 0x64, ctx);
278 GP_Pixel g2 = GP_RGBToContextPixel(0x80, 0x80, 0x80, ctx);
280 unsigned int wm = w/10 < 10 ? 10 : w/10;
281 unsigned int hm = h/10 < 10 ? 10 : h/10;
282 unsigned int wt = w/20 < 5 ? 5 : w/20;
283 unsigned int ht = h/20 < 5 ? 5 : h/20;
285 for (y = 0; y < h; y++) {
286 for (x = 0; x < w; x++) {
287 GP_Pixel pix;
289 if ((x % wm < wt) ^ (y % hm < ht))
290 pix = g1;
291 else
292 pix = g2;
294 GP_PutPixel(ctx, x0 + x, y0 + y, pix);
300 * Updates display.
302 static void update_display(struct loader_params *params, GP_Context *img)
304 GP_Context *context = backend->context;
305 struct cpu_timer timer;
306 GP_ProgressCallback callback = {.callback = image_loader_callback};
308 switch (params->rotate) {
309 case 0:
310 break;
311 case 90:
312 callback.priv = "Rotating image (90)";
313 img = GP_FilterRotate90Alloc(img, &callback);
314 break;
315 case 180:
316 callback.priv = "Rotating image (180)";
317 img = GP_FilterRotate180Alloc(img, &callback);
318 break;
319 case 270:
320 callback.priv = "Rotating image (270)";
321 img = GP_FilterRotate270Alloc(img, &callback);
322 break;
325 if (img == NULL)
326 return;
328 int cx = 0;
329 int cy = 0;
331 switch (params->zoom_type) {
332 case ZOOM_FIT_DOWNSCALE:
333 case ZOOM_FIT:
334 cx = (context->w - img->w)/2;
335 cy = (context->h - img->h)/2;
336 break;
337 case ZOOM_FIXED:
338 case ZOOM_FIXED_WIN:
339 cx = params->zoom_x_offset;
340 cy = params->zoom_y_offset;
341 break;
344 GP_Context sub_display;
346 cpu_timer_start(&timer, "Blitting");
348 if (params->use_dithering) {
349 callback.priv = "Dithering";
350 GP_SubContext(context, &sub_display, cx, cy, img->w, img->h);
351 GP_FilterFloydSteinberg_RGB888(img, &sub_display, NULL);
352 // GP_FilterHilbertPeano_RGB888(img, &sub_display, NULL);
353 } else {
354 if (GP_PixelHasFlags(img->pixel_type, GP_PIXEL_HAS_ALPHA))
355 pattern_fill(context, cx, cy, img->w, img->h);
357 GP_Blit_Clipped(img, 0, 0, img->w, img->h, context, cx, cy);
360 cpu_timer_stop(&timer);
362 if (params->rotate)
363 GP_ContextFree(img);
365 /* clean up the rest of the display */
366 GP_FillRectXYWH(context, 0, 0, cx, context->h, black_pixel);
367 GP_FillRectXYWH(context, 0, 0, context->w, cy, black_pixel);
369 int w = context->w - img->w - cx;
371 if (w > 0)
372 GP_FillRectXYWH(context, img->w + cx, 0, w, context->h, black_pixel);
374 int h = context->h - img->h - cy;
376 if (h > 0)
377 GP_FillRectXYWH(context, 0, img->h + cy, context->w, h, black_pixel);
379 set_caption(params->img_path, params->rat);
381 if (!params->show_info) {
382 GP_BackendFlip(backend);
383 return;
386 GP_Size th = GP_TextHeight(NULL);
388 GP_Print(context, NULL, 11, 11, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
389 black_pixel, white_pixel, "%ux%u", img->w, img->h);
391 GP_Print(context, NULL, 10, 10, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
392 white_pixel, black_pixel, "%ux%u", img->w, img->h);
394 GP_Print(context, NULL, 11, 13 + th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
395 black_pixel, white_pixel, "1:%3.3f", params->rat);
397 GP_Print(context, NULL, 10, 12 + th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
398 white_pixel, black_pixel, "1:%3.3f", params->rat);
400 GP_Print(context, NULL, 11, 15 + 2 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
401 black_pixel, white_pixel, "%s", img_name(params->img_path));
403 GP_Print(context, NULL, 10, 14 + 2 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
404 white_pixel, black_pixel, "%s", img_name(params->img_path));
406 GP_Print(context, NULL, 11, 17 + 3 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
407 black_pixel, white_pixel, "%s%s",
408 params->use_low_pass && params->rat < 1 ? "Gaussian LP + " : "",
409 GP_InterpolationTypeName(params->resampling_method));
411 GP_Print(context, NULL, 10, 16 + 3 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
412 white_pixel, black_pixel, "%s%s",
413 params->use_low_pass && params->rat < 1 ? "Gaussian LP + " : "",
414 GP_InterpolationTypeName(params->resampling_method));
416 unsigned int count = image_list_count(params->img_list);
417 unsigned int pos = image_list_pos(params->img_list) + 1;
419 GP_Print(context, NULL, 11, 19 + 4 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
420 black_pixel, white_pixel, "%u of %u", pos, count);
422 GP_Print(context, NULL, 10, 18 + 4 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
423 white_pixel, black_pixel, "%u of %u", pos, count);
425 unsigned int dir_count = image_list_dir_count(params->img_list);
426 unsigned int dir_pos = image_list_dir_pos(params->img_list) + 1;
428 if (dir_count > 1 && dir_count != count) {
429 GP_Print(context, NULL, 11, 21 + 5 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
430 black_pixel, white_pixel, "%u of %u in directory", dir_pos, dir_count);
432 GP_Print(context, NULL, 10, 20 + 5 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
433 white_pixel, black_pixel, "%u of %u in directory", dir_pos, dir_count);
436 GP_BackendFlip(backend);
439 GP_Context *load_resized_image(struct loader_params *params, GP_Size w, GP_Size h)
441 long cookie = (w & 0xffff) | (h & 0xffff)<<16;
442 GP_Context *img, *res = NULL;
443 struct cpu_timer timer;
444 GP_ProgressCallback callback = {.callback = image_loader_callback};
446 int key = (params->resampling_method<<1) | !!(params->use_low_pass);
448 /* Try to get resized cached image */
449 img = image_cache_get(params->img_resized_cache, params->img_path, cookie, key, 1);
451 if (img != NULL)
452 return img;
454 /* Otherwise load image and resize it */
455 if ((img = load_image(params, 1)) == NULL)
456 return NULL;
458 if (params->show_nn_first) {
459 /* Do simple interpolation and blit the result */
460 GP_Context *nn = GP_FilterResizeNNAlloc(img, w, h, NULL);
461 if (nn != NULL) {
462 update_display(params, nn);
463 GP_ContextFree(nn);
467 /* Do low pass filter */
468 if (params->use_low_pass && params->rat < 1) {
469 cpu_timer_start(&timer, "Blur");
470 callback.priv = "Blurring Image";
472 res = GP_FilterGaussianBlurAlloc(img, 0.3/params->rat,
473 0.3/params->rat, &callback);
475 if (res == NULL)
476 return NULL;
478 img = res;
480 cpu_timer_stop(&timer);
483 // img->gamma = GP_GammaAcquire(img->pixel_type, 0.45);
485 cpu_timer_start(&timer, "Resampling");
486 callback.priv = "Resampling Image";
487 GP_Context *i1 = GP_FilterResize(img, NULL, params->resampling_method, w, h, &callback);
488 // img->gamma = NULL;
489 // GP_Context *i2 = GP_FilterResize(img, NULL, params->resampling_method, w, h, &callback);
490 // img = GP_FilterDifferenceAlloc(i2, i1, NULL);
491 // img = GP_FilterInvert(img, NULL, NULL);
492 img = i1;
493 // if (params->resampling_method == GP_INTERP_CUBIC_INT)
494 // GP_FilterEdgeSharpening(img, img, 0.2, NULL);
495 cpu_timer_stop(&timer);
498 if (params->rat > 1.5) {
499 cpu_timer_start(&timer, "Sharpening");
500 callback.priv = "Sharpening";
501 GP_FilterEdgeSharpening(i1, i1, 0.1, &callback);
502 cpu_timer_stop(&timer);
506 /* Free low passed context if needed */
507 GP_ContextFree(res);
509 if (img == NULL)
510 return NULL;
512 image_cache_put(params->img_resized_cache, img, params->img_path, cookie, key);
514 return img;
517 static void *image_loader(void *ptr)
519 struct loader_params *params = ptr;
520 struct cpu_timer sum_timer;
521 GP_Context *img, *context = backend->context;
523 cpu_timer_start(&sum_timer, "sum");
525 show_progress = params->show_progress || params->show_progress_once;
526 params->show_progress_once = 0;
528 /* Figure out rotation */
529 GP_Size w, h;
531 switch (params->rotate) {
532 case 0:
533 case 180:
534 default:
535 w = context->w;
536 h = context->h;
537 break;
538 case 90:
539 case 270:
540 w = context->h;
541 h = context->w;
542 break;
545 if ((img = load_image(params, 0)) == NULL)
546 return NULL;
548 params->rat = calc_img_size(params, img->w, img->h, w, h);
550 /* Special case => no need to resize */
551 if (params->rat == 1.0)
552 goto update;
554 w = img->w;
555 h = img->h;
557 img = load_resized_image(params, w * params->rat + 0.5, h * params->rat + 0.5);
559 if (img == NULL)
560 return NULL;
562 image_cache_print(params->img_resized_cache);
563 image_cache_print(params->img_orig_cache);
565 update:
566 update_display(params, img);
567 cpu_timer_stop(&sum_timer);
569 return NULL;
572 static pthread_t loader_thread = (pthread_t)0;
574 static void stop_loader(void)
576 if (loader_thread) {
577 abort_flag = 1;
578 pthread_join(loader_thread, NULL);
579 loader_thread = (pthread_t)0;
580 abort_flag = 0;
584 static void show_image(struct loader_params *params, const char *path)
586 int ret;
588 if (path != NULL)
589 params->img_path = path;
591 /* stop previous loader thread */
592 stop_loader();
594 ret = pthread_create(&loader_thread, NULL, image_loader, (void*)params);
596 if (ret) {
597 fprintf(stderr, "Failed to start thread: %s\n", strerror(ret));
598 GP_BackendExit(backend);
599 exit(1);
603 static void set_zoom_offset(struct loader_params *params, int dx, int dy)
605 params->zoom_x_offset += dx;
606 params->zoom_y_offset += dy;
607 show_image(params, NULL);
610 static void zoom_mul(struct loader_params *params, float mul)
612 params->zoom *= mul;
613 show_image(params, NULL);
616 static void sighandler(int signo)
618 if (backend != NULL)
619 GP_BackendExit(backend);
621 fprintf(stderr, "Got signal %i\n", signo);
623 _exit(1);
626 static void init_backend(const char *backend_opts)
628 backend = GP_BackendInit(backend_opts, "Spiv", stderr);
630 if (backend == NULL) {
631 fprintf(stderr, "Failed to initalize backend '%s'\n", backend_opts);
632 exit(1);
637 static void init_caches(struct loader_params *params)
639 size_t size = image_cache_get_ram_size();
640 unsigned int resized_size = (1024 * size)/10;
641 unsigned int orig_size = (1024 * size)/50;
643 if (resized_size > 100 * 1024 * 1024)
644 resized_size = 100 * 1024 * 1024;
646 if (orig_size > 40 * 1024 * 1024)
647 orig_size = 40 * 1024 * 1024;
649 GP_DEBUG(1, "Cache sizes original = %u, resized = %u",
650 orig_size, resized_size);
652 params->img_resized_cache = image_cache_create(resized_size);
653 params->img_orig_cache = image_cache_create(orig_size);
655 // params->img_resized_cache = NULL;
656 // params->img_orig_cache = NULL;
659 static uint32_t timer_callback(GP_Timer *self)
661 struct loader_params *params = self->priv;
663 show_image(params, image_list_move(params->img_list, 1));
665 return 0;
668 int main(int argc, char *argv[])
670 GP_Context *context = NULL;
671 const char *backend_opts = "X11";
672 int sleep_ms = -1;
673 int opt;
674 int shift_flag;
675 GP_PixelType emul_type = GP_PIXEL_UNKNOWN;
677 struct loader_params params = {
678 .img_path = NULL,
680 .show_progress = 0,
681 .show_progress_once = 0,
682 .show_info = 0,
683 .show_nn_first = 0,
684 .use_dithering = 0,
685 .rotate = 0,
686 .resampling_method = GP_INTERP_LINEAR_LF_INT,
688 .zoom_type = ZOOM_FIT_DOWNSCALE,
689 .zoom = 1,
691 .img_resized_cache = NULL,
692 .img_orig_cache = NULL,
695 GP_TIMER_DECLARE(timer, 0, 0, "Slideshow", timer_callback, &params);
697 while ((opt = getopt(argc, argv, "b:ce:fhIPs:r:z:0:1:2:3:4:5:6:7:8:9:")) != -1) {
698 switch (opt) {
699 case 'I':
700 params.show_info = 1;
701 break;
702 case 'P':
703 params.show_progress = 1;
704 break;
705 case 'f':
706 params.use_dithering = 1;
707 break;
708 case 's':
709 sleep_ms = atoi(optarg);
710 break;
711 case 'c':
712 params.resampling_method = GP_INTERP_CUBIC_INT;
713 /* Cubic resampling needs low pass */
714 params.use_low_pass = 1;
715 /* Cubic resampling is slow, show nn first */
716 params.show_nn_first = 1;
717 break;
718 case 'e':
719 emul_type = GP_PixelTypeByName(optarg);
721 if (emul_type == GP_PIXEL_UNKNOWN) {
722 fprintf(stderr, "Invalid pixel type '%s'\n", optarg);
723 return 1;
725 break;
726 case 'r':
727 if (!strcmp(optarg, "90"))
728 params.rotate = 90;
729 else if (!strcmp(optarg, "180"))
730 params.rotate = 180;
731 else if (!strcmp(optarg, "270"))
732 params.rotate = 270;
733 case 'b':
734 backend_opts = optarg;
735 break;
736 case 'h':
737 print_help();
738 exit(0);
739 break;
740 case 'z':
741 switch (optarg[0]) {
742 case 'f':
743 params.zoom_type = ZOOM_FIXED;
744 break;
745 case 'w':
746 params.zoom_type = ZOOM_FIXED_WIN;
747 break;
749 break;
750 case '0' ... '9':
751 image_action_set(opt - '0', optarg);
752 break;
753 default:
754 fprintf(stderr, "Invalid paramter '%c'\n", opt);
755 print_help();
756 return 1;
760 if (optind >= argc) {
761 fprintf(stderr, "Requires path to at least one image\n\n");
762 print_help();
763 return 1;
766 signal(SIGINT, sighandler);
767 signal(SIGSEGV, sighandler);
768 signal(SIGBUS, sighandler);
769 signal(SIGABRT, sighandler);
771 init_caches(&params);
773 init_backend(backend_opts);
775 if (emul_type != GP_PIXEL_UNKNOWN)
776 backend = GP_BackendVirtualInit(backend, emul_type, GP_BACKEND_CALL_EXIT);
778 context = backend->context;
780 black_pixel = GP_ColorToContextPixel(GP_COL_BLACK, context);
781 white_pixel = GP_ColorToContextPixel(GP_COL_WHITE, context);
782 gray_pixel = GP_RGBToContextPixel(0x33, 0x33, 0x33, context);
784 GP_Fill(context, black_pixel);
785 GP_BackendFlip(backend);
787 struct image_list *list = image_list_create((const char**)argv + optind);
788 params.img_list = list;
790 params.show_progress_once = 1;
791 show_image(&params, image_list_img_path(list));
793 if (sleep_ms) {
794 timer.period = sleep_ms;
795 GP_BackendAddTimer(backend, &timer);
798 for (;;) {
799 GP_BackendWait(backend);
801 GP_Event ev;
803 while (GP_BackendGetEvent(backend, &ev)) {
805 shift_flag = GP_EventGetKey(&ev, GP_KEY_LEFT_SHIFT) ||
806 GP_EventGetKey(&ev, GP_KEY_RIGHT_SHIFT);
808 switch (ev.type) {
809 case GP_EV_REL:
810 switch (ev.code) {
811 case GP_EV_REL_WHEEL:
812 if (ev.val.val > 0)
813 goto next;
815 if (ev.val.val < 0)
816 goto prev;
817 break;
818 case GP_EV_REL_POS:
819 if (GP_EventGetKey(&ev, GP_BTN_LEFT))
820 set_zoom_offset(&params,
821 ev.val.rel.rx,
822 ev.val.rel.ry);
823 break;
825 break;
826 case GP_EV_KEY:
827 if (ev.code != GP_EV_KEY_DOWN)
828 continue;
830 switch (ev.val.key.key) {
831 case GP_KEY_H:
832 draw_help(backend);
833 show_image(&params, NULL);
834 break;
835 case GP_KEY_F:
836 if (GP_BackendIsX11(backend))
837 GP_BackendX11RequestFullscreen(backend, 2);
838 break;
839 case GP_KEY_I:
840 params.show_info = !params.show_info;
842 params.show_progress_once = 1;
843 show_image(&params, NULL);
844 break;
845 case GP_KEY_P:
846 params.show_progress = !params.show_progress;
847 break;
848 case GP_KEY_R:
849 params.rotate += 90;
850 if (params.rotate > 270)
851 params.rotate = 0;
853 params.show_progress_once = 1;
854 show_image(&params, NULL);
855 break;
856 case GP_KEY_RIGHT_BRACE:
857 params.resampling_method++;
859 if (params.resampling_method > GP_INTERP_MAX)
860 params.resampling_method = 0;
862 if (params.resampling_method == GP_INTERP_LINEAR_LF_INT) {
863 params.use_low_pass = 0;
864 params.show_nn_first = 0;
865 } else {
866 params.use_low_pass = 1;
867 params.show_nn_first = 1;
870 params.show_progress_once = 1;
871 show_image(&params, image_list_img_path(list));
872 break;
873 case GP_KEY_LEFT_BRACE:
874 if (params.resampling_method == 0)
875 params.resampling_method = GP_INTERP_MAX;
876 else
877 params.resampling_method--;
879 if (params.resampling_method == GP_INTERP_LINEAR_LF_INT) {
880 params.use_low_pass = 0;
881 params.show_nn_first = 0;
882 } else {
883 params.use_low_pass = 1;
884 params.show_nn_first = 1;
887 params.show_progress_once = 1;
888 show_image(&params, image_list_img_path(list));
889 break;
890 case GP_KEY_L:
891 params.use_low_pass = !params.use_low_pass;
893 params.show_progress_once = 1;
894 show_image(&params, image_list_img_path(list));
895 break;
896 case GP_KEY_D:
897 image_cache_drop(params.img_resized_cache);
898 image_cache_drop(params.img_orig_cache);
899 break;
900 case GP_KEY_ESC:
901 case GP_KEY_ENTER:
902 case GP_KEY_Q:
903 image_cache_drop(params.img_resized_cache);
904 image_cache_drop(params.img_orig_cache);
905 GP_BackendExit(backend);
906 return 0;
907 break;
908 case GP_KEY_PAGE_UP:
909 params.show_progress_once = 1;
910 show_image(&params, image_list_dir_move(list, -1));
911 break;
912 case GP_KEY_PAGE_DOWN:
913 params.show_progress_once = 1;
914 show_image(&params, image_list_dir_move(list, 1));
915 break;
916 case GP_KEY_HOME:
917 params.show_progress_once = 1;
918 show_image(&params, image_list_first(list));
919 break;
920 case GP_KEY_END:
921 params.show_progress_once = 1;
922 show_image(&params, image_list_last(list));
923 break;
924 case GP_KEY_RIGHT:
925 if (shift_flag)
926 set_zoom_offset(&params, 1, 0);
927 else
928 set_zoom_offset(&params, 10, 0);
930 break;
931 case GP_KEY_LEFT:
932 if (shift_flag)
933 set_zoom_offset(&params, -1, 0);
934 else
935 set_zoom_offset(&params, -10, 0);
936 break;
937 case GP_KEY_UP:
938 if (shift_flag)
939 set_zoom_offset(&params, 0, -1);
940 else
941 set_zoom_offset(&params, 0, -10);
942 break;
943 case GP_KEY_DOWN:
944 if (shift_flag)
945 set_zoom_offset(&params, 0, 1);
946 else
947 set_zoom_offset(&params, 0, 10);
948 break;
949 next:
950 case GP_KEY_SPACE:
951 params.show_progress_once = 1;
952 if (shift_flag)
953 show_image(&params, image_list_move(list, 10));
954 else
955 show_image(&params, image_list_move(list, 1));
956 break;
957 prev:
958 case GP_KEY_BACKSPACE:
959 params.show_progress_once = 1;
960 if (shift_flag)
961 show_image(&params, image_list_move(list, -10));
962 else
963 show_image(&params, image_list_move(list, -1));
964 break;
965 case GP_KEY_1:
966 resize_backend(&params, 1, shift_flag);
967 break;
968 case GP_KEY_2:
969 resize_backend(&params, 2, shift_flag);
970 break;
971 case GP_KEY_3:
972 resize_backend(&params, 3, shift_flag);
973 break;
974 case GP_KEY_4:
975 resize_backend(&params, 4, shift_flag);
976 break;
977 case GP_KEY_5:
978 resize_backend(&params, 5, shift_flag);
979 break;
980 case GP_KEY_6:
981 resize_backend(&params, 6, shift_flag);
982 break;
983 case GP_KEY_7:
984 resize_backend(&params, 7, shift_flag);
985 break;
986 case GP_KEY_8:
987 resize_backend(&params, 8, shift_flag);
988 break;
989 case GP_KEY_9:
990 resize_backend(&params, 9, shift_flag);
991 break;
992 case GP_KEY_KP_PLUS:
993 case GP_KEY_DOT:
994 params.show_progress_once = 1;
995 zoom_mul(&params, 1.5);
996 break;
997 case GP_KEY_KP_MINUS:
998 case GP_KEY_COMMA:
999 params.show_progress_once = 1;
1000 zoom_mul(&params, 1/1.5);
1001 break;
1002 case GP_KEY_F1 ... GP_KEY_F10:
1003 image_action_run(ev.val.key.key - GP_KEY_F1,
1004 image_list_img_path(list));
1005 break;
1007 break;
1008 case GP_EV_SYS:
1009 switch (ev.code) {
1010 case GP_EV_SYS_RESIZE:
1011 /* stop loader thread before resizing backend buffer */
1012 stop_loader();
1013 GP_BackendResizeAck(backend);
1014 GP_Fill(backend->context, 0);
1015 params.show_progress_once = 1;
1016 show_image(&params, NULL);
1017 break;
1018 case GP_EV_SYS_QUIT:
1019 GP_BackendExit(backend);
1020 return 0;
1021 break;
1023 break;
1028 GP_BackendExit(backend);
1030 return 0;