spiv: Print backend help if only '-b help' was passed
[gfxprim/pasky.git] / demos / spiv / spiv.c
blob33b0971c291048286f9803625a58a9d95c429a3c
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 <errno.h>
30 #include <signal.h>
31 #include <string.h>
32 #include <pthread.h>
34 #include <GP.h>
36 #include "image_cache.h"
37 #include "image_list.h"
38 #include "image_loader.h"
39 #include "image_actions.h"
40 #include "spiv_help.h"
41 #include "spiv_config.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;
53 static int loader_running = 0;
55 struct loader_params {
56 /* current resize ratio */
57 float zoom_rat;
59 /* offset in pixels */
60 unsigned int zoom_x_offset;
61 unsigned int zoom_y_offset;
63 /* flag that is turned on when user changes zoom */
64 unsigned int zoom_manual;
66 long show_progress_once:2;
67 /* use nearest neighbour resampling first */
68 long show_nn_first:4;
69 /* use low pass before resampling */
70 long use_low_pass:6;
71 /* resampling method */
72 int resampling_method;
74 /* slideshow sleep */
75 int sleep_ms;
77 /* caches for loaded images */
78 struct image_cache *img_resized_cache;
81 static int image_loader_callback(GP_ProgressCallback *self)
83 static GP_Size size = 0;
84 GP_Context *c = backend->context;
86 if (abort_flag)
87 return 1;
89 if (!show_progress)
90 return 0;
92 char buf[100];
94 snprintf(buf, sizeof(buf), "%s ... %-3.1f%%",
95 (const char*)self->priv, self->percentage);
97 int align = GP_ALIGN_CENTER|GP_VALIGN_ABOVE;
99 size = GP_TextWidth(NULL, buf);
101 int start = c->w/2 - size/2 - 10;
102 int end = c->w/2 + size/2 + 10;
103 int middle = start + (end - start) * self->percentage / 100;
104 int top = c->h - GP_TextHeight(NULL) - 11;
106 GP_FillRectXYXY(c, start, c->h - 1, middle, top, gray_pixel);
107 GP_FillRectXYXY(c, middle, c->h - 1, end, top, black_pixel);
109 GP_Text(c, NULL, c->w/2, c->h - 5, align,
110 white_pixel, black_pixel, buf);
112 GP_BackendUpdateRect(backend, start, c->h - 1, end, top);
114 return 0;
117 static GP_Context *load_image(int elevate);
119 static const char *img_name(const char *img_path)
121 int i, len = strlen(img_path);
123 for (i = len - 1; i > 0; i--) {
124 if (img_path[i] == '/')
125 return &img_path[i+1];
128 return img_path;
131 static void set_caption(const char *path, float rat)
133 char buf[256];
135 snprintf(buf, sizeof(buf), "Spiv ~ %s 1:%3.3f", img_name(path), rat);
137 GP_BackendSetCaption(backend, buf);
141 * Loads image
143 static GP_Context *load_image(int elevate)
145 GP_Context *img;
146 GP_ProgressCallback callback = {.callback = image_loader_callback,
147 .priv = "Loading image"};
149 img = image_loader_get_image(&callback, elevate);
151 if (img)
152 return img;
154 GP_Context *ctx = backend->context;
156 GP_Fill(ctx, black_pixel);
157 GP_Print(ctx, NULL, ctx->w/2, ctx->h/2 - 10,
158 GP_ALIGN_CENTER|GP_VALIGN_CENTER, white_pixel, black_pixel,
159 "'%s'", image_loader_img_path());
160 GP_Print(ctx, NULL, ctx->w/2, ctx->h/2 + 10,
161 GP_ALIGN_CENTER|GP_VALIGN_CENTER, white_pixel, black_pixel,
162 "Failed to load image :( (%s)", strerror(errno));
163 GP_BackendFlip(backend);
165 return NULL;
169 * Fill context with chessboard-like pattern.
171 static void pattern_fill(GP_Context *ctx, unsigned int x0, unsigned int y0,
172 unsigned int w, unsigned int h)
174 unsigned int x, y;
176 GP_Pixel g1 = GP_RGBToContextPixel(0x64, 0x64, 0x64, ctx);
177 GP_Pixel g2 = GP_RGBToContextPixel(0x80, 0x80, 0x80, ctx);
179 unsigned int wm = w/10 < 10 ? 10 : w/10;
180 unsigned int hm = h/10 < 10 ? 10 : h/10;
181 unsigned int wt = w/20 < 5 ? 5 : w/20;
182 unsigned int ht = h/20 < 5 ? 5 : h/20;
184 for (y = 0; y < h; y++) {
185 for (x = 0; x < w; x++) {
186 GP_Pixel pix;
188 if ((x % wm < wt) ^ (y % hm < ht))
189 pix = g1;
190 else
191 pix = g2;
193 GP_PutPixel(ctx, x0 + x, y0 + y, pix);
199 static void info_printf(GP_Context *ctx, GP_Coord x, GP_Coord y,
200 const char *fmt, ...)
201 __attribute__ ((format (printf, 4, 5)));
203 static void info_printf(GP_Context *ctx, GP_Coord x, GP_Coord y,
204 const char *fmt, ...)
206 va_list va, vac;
208 va_start(va, fmt);
210 va_copy(vac, va);
211 GP_VPrint(ctx, NULL, x-1, y-1, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
212 black_pixel, white_pixel, fmt, vac);
213 va_end(vac);
215 GP_VPrint(ctx, NULL, x, y, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
216 white_pixel, black_pixel, fmt, va);
218 va_end(va);
221 static void show_info(struct loader_params *params, GP_Context *img,
222 GP_Context *orig_img)
224 GP_Context *context = backend->context;
225 const char *img_path = image_loader_img_path();
227 set_caption(img_path, params->zoom_rat);
229 if (!config.show_info)
230 return;
232 GP_Size th = GP_TextHeight(NULL), y = 10;
234 info_printf(context, 10, y, "%ux%u (%ux%u) 1:%3.3f %3.1f%%",
235 img->w, img->h, orig_img->w, orig_img->h, params->zoom_rat,
236 params->zoom_rat * 100);
237 y += th + 2;
239 info_printf(context, 10, y, "%s", img_name(img_path));
241 y += th + 2;
243 if (params->zoom_rat != 1.00) {
244 info_printf(context, 10, y, "%s%s",
245 params->use_low_pass && params->zoom_rat < 1 ? "Gaussian LP + " : "",
246 GP_InterpolationTypeName(params->resampling_method));
247 y += th + 2;
250 unsigned int count = image_loader_count();
251 unsigned int pos = image_loader_pos() + 1;
253 info_printf(context, 10, y, "%u of %u", pos, count);
254 y += th + 2;
256 if (!image_loader_is_in_dir())
257 return;
259 unsigned int dir_count = image_loader_dir_count();
260 unsigned int dir_pos = image_loader_dir_pos() + 1;
262 info_printf(context, 10, y,
263 "%u of %u in directory", dir_pos, dir_count);
266 static void update_display(struct loader_params *params, GP_Context *img,
267 GP_Context *orig_img)
269 GP_Context *context = backend->context;
270 struct cpu_timer timer;
271 GP_ProgressCallback callback = {.callback = image_loader_callback};
273 switch (config.orientation) {
274 case ROTATE_0:
275 break;
276 case ROTATE_90:
277 callback.priv = "Rotating image (90)";
278 img = GP_FilterRotate90Alloc(img, &callback);
279 break;
280 case ROTATE_180:
281 callback.priv = "Rotating image (180)";
282 img = GP_FilterRotate180Alloc(img, &callback);
283 break;
284 case ROTATE_270:
285 callback.priv = "Rotating image (270)";
286 img = GP_FilterRotate270Alloc(img, &callback);
287 break;
290 if (img == NULL)
291 return;
293 int cx = 0;
294 int cy = 0;
297 * Center the image, if window size is fixed and
298 * the image is smaller than window.
300 if (config.win_strategy == ZOOM_WIN_FIXED) {
302 if (img->w < context->w)
303 cx = (context->w - img->w)/2;
305 if (img->h < context->h)
306 cy = (context->h - img->h)/2;
309 if (params->zoom_manual) {
310 cx = params->zoom_x_offset;
311 cy = params->zoom_y_offset;
314 GP_Context sub_display;
316 cpu_timer_start(&timer, "Blitting");
318 if (config.floyd_steinberg) {
319 callback.priv = "Dithering";
320 GP_SubContext(context, &sub_display, cx, cy, img->w, img->h);
321 GP_FilterFloydSteinberg(img, &sub_display, NULL);
322 // GP_FilterHilbertPeano(img, &sub_display, NULL);
323 } else {
324 if (GP_PixelHasFlags(img->pixel_type, GP_PIXEL_HAS_ALPHA))
325 pattern_fill(context, cx, cy, img->w, img->h);
327 GP_Blit_Clipped(img, 0, 0, img->w, img->h, context, cx, cy);
330 cpu_timer_stop(&timer);
332 /* clean up the rest of the display */
333 GP_FillRectXYWH(context, 0, 0, cx, context->h, black_pixel);
334 GP_FillRectXYWH(context, 0, 0, context->w, cy, black_pixel);
337 int w = context->w - img->w - cx;
339 if (w > 0)
340 GP_FillRectXYWH(context, img->w + cx, 0, w, context->h, black_pixel);
342 int h = context->h - img->h - cy;
344 if (h > 0)
345 GP_FillRectXYWH(context, 0, img->h + cy, context->w, h, black_pixel);
347 show_info(params, img, orig_img);
349 if (config.orientation)
350 GP_ContextFree(img);
352 GP_BackendFlip(backend);
355 GP_Context *load_resized_image(struct loader_params *params, GP_Size w, GP_Size h)
357 GP_Context *img, *res = NULL;
358 struct cpu_timer timer;
359 GP_ProgressCallback callback = {.callback = image_loader_callback};
361 const char *img_path = image_loader_img_path();
363 /* Try to get resized cached image */
364 img = image_cache_get2(params->img_resized_cache, 1, "%s %ux%u r%i l%i",
365 img_path, w, h, params->resampling_method,
366 params->use_low_pass);
368 if (img != NULL)
369 return img;
371 /* Otherwise load image and resize it */
372 if ((img = load_image(1)) == NULL)
373 return NULL;
375 if (params->show_nn_first) {
376 /* Do simple interpolation and blit the result */
377 GP_Context *nn = GP_FilterResizeNNAlloc(img, w, h, NULL);
378 if (nn != NULL) {
379 update_display(params, nn, img);
380 GP_ContextFree(nn);
384 /* Do low pass filter */
385 if (params->use_low_pass && params->zoom_rat < 1) {
386 cpu_timer_start(&timer, "Blur");
387 callback.priv = "Blurring Image";
389 res = GP_FilterGaussianBlurAlloc(img, 0.4/params->zoom_rat,
390 0.4/params->zoom_rat, &callback);
392 if (res == NULL)
393 return NULL;
395 img = res;
397 cpu_timer_stop(&timer);
400 // img->gamma = GP_GammaAcquire(img->pixel_type, 0.45);
402 cpu_timer_start(&timer, "Resampling");
403 callback.priv = "Resampling Image";
404 GP_Context *i1 = GP_FilterResizeAlloc(img, w, h, params->resampling_method, &callback);
405 img = i1;
406 cpu_timer_stop(&timer);
409 if (params->zoom_rat > 1.5) {
410 cpu_timer_start(&timer, "Sharpening");
411 callback.priv = "Sharpening";
412 GP_FilterEdgeSharpening(i1, i1, 0.1, &callback);
413 cpu_timer_stop(&timer);
417 /* Free low passed context if needed */
418 GP_ContextFree(res);
420 if (img == NULL)
421 return NULL;
423 image_cache_put2(params->img_resized_cache, img, "%s %ux%u r%i l%i",
424 img_path, w, h, params->resampling_method,
425 params->use_low_pass);
427 return img;
430 static float calc_img_size(struct loader_params *params,
431 uint32_t img_w, uint32_t img_h,
432 uint32_t win_w, uint32_t win_h)
434 float w_rat, h_rat, rat;
435 unsigned int max_win_w = config.max_win_w;
436 unsigned int max_win_h = config.max_win_h;
438 switch (config.orientation) {
439 case ROTATE_90:
440 case ROTATE_270:
441 GP_SWAP(win_w, win_h);
442 GP_SWAP(max_win_w, max_win_h);
443 break;
444 default:
445 break;
448 if (params->zoom_manual) {
449 if (config.win_strategy == ZOOM_WIN_RESIZABLE) {
450 win_w = GP_MIN(max_win_w, img_w * params->zoom_rat + 0.5);
451 win_h = GP_MIN(max_win_h, img_h * params->zoom_rat + 0.5);
453 switch (config.orientation) {
454 case ROTATE_90:
455 case ROTATE_270:
456 GP_BackendResize(backend, win_h, win_w);
457 break;
458 default:
459 GP_BackendResize(backend, win_w, win_h);
462 return params->zoom_rat;
466 if (config.win_strategy == ZOOM_WIN_RESIZABLE) {
467 win_w = GP_MIN(max_win_w, img_w);
468 win_h = GP_MIN(max_win_h, img_h);
471 * Image is larger than screen and downscale is enabled ->
472 * resize window to match image ratio.
474 if ((win_w != img_w || win_h != img_h) &&
475 config.zoom_strategy & ZOOM_IMAGE_DOWNSCALE) {
477 w_rat = 1.00 * win_w / img_w;
478 h_rat = 1.00 * win_h / img_h;
480 if (w_rat > 1)
481 w_rat = 1;
483 if (h_rat > 1)
484 w_rat = 1;
486 rat = GP_MIN(h_rat, w_rat);
488 win_w = rat * img_w + 0.5;
489 win_h = rat * img_h + 0.5;
492 switch (config.orientation) {
493 case ROTATE_90:
494 case ROTATE_270:
495 GP_BackendResize(backend, win_h, win_w);
496 break;
497 default:
498 GP_BackendResize(backend, win_w, win_h);
502 if (img_w <= win_w && img_h <= win_h) {
503 if (!(config.zoom_strategy & ZOOM_IMAGE_UPSCALE))
504 return 1.00;
505 } else {
506 if (!(config.zoom_strategy & ZOOM_IMAGE_DOWNSCALE))
507 return 1.00;
511 w_rat = 1.00 * win_w / img_w;
512 h_rat = 1.00 * win_h / img_h;
514 return GP_MIN(w_rat, h_rat);
517 static void *image_loader(void *ptr)
519 struct loader_params *params = ptr;
520 struct cpu_timer sum_timer;
521 GP_Context *img, *orig_img, *context = backend->context;
523 cpu_timer_start(&sum_timer, "sum");
525 show_progress = config.show_progress || params->show_progress_once;
526 params->show_progress_once = 0;
528 if ((orig_img = load_image(0)) == NULL) {
529 loader_running = 0;
530 return NULL;
533 /* Figure zoom */
534 GP_Size w, h;
536 params->zoom_rat = calc_img_size(params, orig_img->w, orig_img->h,
537 context->w, context->h);
539 w = orig_img->w * params->zoom_rat + 0.5;
540 h = orig_img->h * params->zoom_rat + 0.5;
542 /* Special case => no need to resize */
543 if (w == orig_img->w && h == orig_img->h) {
544 img = orig_img;
545 goto update;
548 img = load_resized_image(params, w, h);
550 if (img == NULL) {
551 loader_running = 0;
552 return NULL;
555 update:
556 update_display(params, img, orig_img);
557 cpu_timer_stop(&sum_timer);
559 loader_running = 0;
561 return NULL;
564 static pthread_t loader_thread = (pthread_t)0;
566 static void stop_loader(void)
568 if (loader_thread) {
569 abort_flag = 1;
570 pthread_join(loader_thread, NULL);
571 loader_thread = (pthread_t)0;
572 abort_flag = 0;
576 static void show_image(struct loader_params *params)
578 int ret;
580 /* stop previous loader thread */
581 stop_loader();
583 loader_running = 1;
585 ret = pthread_create(&loader_thread, NULL, image_loader, (void*)params);
587 if (ret) {
588 fprintf(stderr, "Failed to start thread: %s\n", strerror(ret));
589 GP_BackendExit(backend);
590 exit(1);
594 static void image_seek(struct loader_params *params,
595 enum img_seek_offset offset, int whence)
598 * We need to stop loader first because image loader seek may free
599 * image we are currently resamling.
601 stop_loader();
602 params->zoom_manual = 0;
603 image_loader_seek(offset, whence);
604 show_image(params);
607 static void set_zoom_offset(struct loader_params *params, int dx, int dy)
609 params->zoom_manual = 1;
610 params->zoom_x_offset += dx;
611 params->zoom_y_offset += dy;
612 show_image(params);
615 static void zoom_mul(struct loader_params *params, float mul)
617 params->zoom_manual = 1;
618 params->zoom_rat *= mul;
619 show_image(params);
622 static void zoom_set(struct loader_params *params, float mul)
624 params->zoom_manual = 1;
625 params->zoom_rat = mul;
626 show_image(params);
629 static void sighandler(int signo)
631 if (backend != NULL)
632 GP_BackendExit(backend);
634 fprintf(stderr, "Got signal %i\n", signo);
636 _exit(1);
639 static void init_backend(const char *backend_opts)
641 backend = GP_BackendInit(backend_opts, "Spiv", stderr);
643 if (backend == NULL) {
644 fprintf(stderr, "Failed to initalize backend '%s'\n", backend_opts);
645 exit(1);
648 if (config.full_screen) {
649 if (GP_BackendIsX11(backend))
650 GP_BackendX11RequestFullscreen(backend, 2);
654 #define RESIZED_CACHE_MAX 400 * 1024
655 #define ORIG_CACHE_MAX 80 * 1024
658 * Figure out cache size depending on the size of RAM.
660 * Initialize cache, image loader.
662 static int init_loader(struct loader_params *params, const char **argv)
664 size_t size = image_cache_get_ram_size();
665 size_t resized_size = size/10;
666 size_t orig_size = size/50;
668 if (resized_size > RESIZED_CACHE_MAX)
669 resized_size = RESIZED_CACHE_MAX;
671 if (orig_size > ORIG_CACHE_MAX)
672 orig_size = ORIG_CACHE_MAX;
674 GP_DEBUG(1, "Resized cache size = %zukB", resized_size);
675 GP_DEBUG(1, "Orig cache size = %zukB", orig_size);
677 if (image_loader_init(argv, orig_size))
678 return 1;
680 params->img_resized_cache = image_cache_create(resized_size);
682 return 0;
685 static uint32_t timer_callback(GP_Timer *self)
687 struct loader_params *params = self->priv;
688 static int retries = 0;
691 * If loader is still running, reschedule after 20ms
693 * If more than two seconds has passed, try load next
695 if (loader_running && retries < 100) {
696 printf("Loader still running %ims\n", 20 * retries);
697 retries++;
698 return 20;
699 } else {
700 retries = 0;
704 * We need to stop loader first because image loader seek may free
705 * image we are currently resamling.
707 stop_loader();
708 image_loader_seek(IMG_CUR, 1);
709 show_image(params);
711 return params->sleep_ms;
714 int main(int argc, char *argv[])
716 GP_Context *context = NULL;
717 int shift_flag;
718 int opts;
720 struct loader_params params = {
721 .show_progress_once = 0,
722 .show_nn_first = 0,
723 .resampling_method = GP_INTERP_LINEAR_LF_INT,
725 .img_resized_cache = NULL,
727 .sleep_ms = 0,
730 GP_TIMER_DECLARE(timer, 0, 0, "Slideshow", timer_callback, &params);
732 if (access("/etc/spiv.conf", R_OK) == 0)
733 spiv_config_load("/etc/spiv.conf");
735 if (getenv("HOME")) {
736 char buf[256];
738 snprintf(buf, sizeof(buf), "%s/%s", getenv("HOME"), ".spiv");
740 if (access(buf, R_OK) == 0)
741 spiv_config_load(buf);
744 opts = spiv_config_parse_args(argc, argv);
746 if (opts < 0) {
747 print_help();
748 return 1;
751 cpu_timer_switch(config.timers);
752 params.sleep_ms = 1000 * config.slideshow_delay + 0.5;
754 if (opts >= argc) {
756 if (!strcmp(config.backend_init, "help")) {
757 init_backend(config.backend_init);
758 return 0;
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 if (init_loader(&params, (const char **)argv + opts))
772 return 1;
774 init_backend(config.backend_init);
776 if (config.emul_type != GP_PIXEL_UNKNOWN) {
777 backend = GP_BackendVirtualInit(backend, config.emul_type,
778 GP_BACKEND_CALL_EXIT);
781 context = backend->context;
783 black_pixel = GP_ColorToContextPixel(GP_COL_BLACK, context);
784 white_pixel = GP_ColorToContextPixel(GP_COL_WHITE, context);
785 gray_pixel = GP_RGBToContextPixel(0x33, 0x33, 0x33, context);
787 GP_Fill(context, black_pixel);
788 GP_BackendFlip(backend);
790 params.show_progress_once = 1;
791 show_image(&params);
793 if (params.sleep_ms) {
794 timer.expires = params.sleep_ms;
795 GP_BackendAddTimer(backend, &timer);
798 for (;;) {
799 GP_Event ev;
801 while (GP_BackendWaitEvent(backend, &ev)) {
803 shift_flag = GP_EventGetKey(&ev, GP_KEY_LEFT_SHIFT) ||
804 GP_EventGetKey(&ev, GP_KEY_RIGHT_SHIFT);
806 switch (ev.type) {
807 case GP_EV_REL:
808 switch (ev.code) {
809 case GP_EV_REL_WHEEL:
810 if (ev.val.val > 0)
811 goto next;
813 if (ev.val.val < 0)
814 goto prev;
815 break;
816 case GP_EV_REL_POS:
817 if (GP_EventGetKey(&ev, GP_BTN_LEFT))
818 set_zoom_offset(&params,
819 ev.val.rel.rx,
820 ev.val.rel.ry);
821 break;
823 break;
824 case GP_EV_KEY:
825 if (ev.code != GP_EV_KEY_DOWN)
826 continue;
828 switch (ev.val.key.key) {
829 case GP_KEY_H:
830 draw_help(backend);
831 show_image(&params);
832 break;
833 case GP_KEY_F:
834 if (GP_BackendIsX11(backend))
835 GP_BackendX11RequestFullscreen(backend, 2);
836 break;
837 case GP_KEY_I:
838 config.show_info = !config.show_info;
840 params.show_progress_once = 1;
841 show_image(&params);
842 break;
843 case GP_KEY_P:
844 config.show_progress = !config.show_progress;
845 break;
846 case GP_KEY_R:
847 config.orientation++;
849 if (config.orientation > ROTATE_270)
850 config.orientation = 0;
852 params.show_progress_once = 1;
853 show_image(&params);
854 break;
855 case GP_KEY_E:
856 if (config.orientation > 0)
857 config.orientation--;
858 else
859 config.orientation = ROTATE_270;
861 params.show_progress_once = 1;
862 show_image(&params);
863 break;
864 case GP_KEY_S:
865 if (params.sleep_ms) {
866 if (GP_BackendTimersInQueue(backend)) {
867 GP_BackendRemTimer(backend, &timer);
868 } else {
869 timer.expires = params.sleep_ms;
870 GP_BackendAddTimer(backend, &timer);
873 break;
874 case GP_KEY_RIGHT_BRACE:
875 params.resampling_method++;
877 if (params.resampling_method > GP_INTERP_MAX)
878 params.resampling_method = 0;
879 if (params.resampling_method == GP_INTERP_CUBIC)
880 params.resampling_method++;
881 if (params.resampling_method == GP_INTERP_LINEAR_LF_INT) {
882 params.use_low_pass = 0;
883 params.show_nn_first = 0;
884 } else {
885 params.use_low_pass = 1;
886 params.show_nn_first = 1;
889 params.show_progress_once = 1;
890 show_image(&params);
891 break;
892 case GP_KEY_LEFT_BRACE:
893 if (params.resampling_method == 0)
894 params.resampling_method = GP_INTERP_MAX;
895 else
896 params.resampling_method--;
897 if (params.resampling_method == GP_INTERP_CUBIC)
898 params.resampling_method--;
899 if (params.resampling_method == GP_INTERP_LINEAR_LF_INT) {
900 params.use_low_pass = 0;
901 params.show_nn_first = 0;
902 } else {
903 params.use_low_pass = 1;
904 params.show_nn_first = 1;
907 params.show_progress_once = 1;
908 show_image(&params);
909 break;
910 case GP_KEY_L:
911 params.use_low_pass = !params.use_low_pass;
913 params.show_progress_once = 1;
914 show_image(&params);
915 break;
916 case GP_KEY_C:
917 image_cache_drop(params.img_resized_cache);
918 image_loader_drop_cache();
919 break;
920 case GP_KEY_W:
921 config_win_toggle();
922 params.show_progress_once = 1;
923 show_image(&params);
924 break;
925 case GP_KEY_U:
926 config_upscale_toggle();
927 params.show_progress_once = 1;
928 show_image(&params);
929 break;
930 case GP_KEY_D:
931 config_downscale_toggle();
932 params.show_progress_once = 1;
933 show_image(&params);
934 break;
935 case GP_KEY_ESC:
936 case GP_KEY_ENTER:
937 case GP_KEY_Q:
938 stop_loader();
939 image_cache_destroy(params.img_resized_cache);
940 image_loader_destroy();
941 GP_BackendExit(backend);
942 return 0;
943 break;
944 case GP_KEY_PAGE_UP:
945 params.show_progress_once = 1;
946 image_seek(&params, IMG_DIR, -1);
947 break;
948 case GP_KEY_PAGE_DOWN:
949 params.show_progress_once = 1;
950 image_seek(&params, IMG_DIR, 1);
951 break;
952 case GP_KEY_HOME:
953 params.show_progress_once = 1;
954 image_seek(&params, IMG_FIRST, 0);
955 break;
956 case GP_KEY_END:
957 params.show_progress_once = 1;
958 image_seek(&params, IMG_LAST, 0);
959 break;
960 case GP_KEY_RIGHT:
961 if (shift_flag)
962 set_zoom_offset(&params, 1, 0);
963 else
964 set_zoom_offset(&params, 10, 0);
966 break;
967 case GP_KEY_LEFT:
968 if (shift_flag)
969 set_zoom_offset(&params, -1, 0);
970 else
971 set_zoom_offset(&params, -10, 0);
972 break;
973 case GP_KEY_UP:
974 if (shift_flag)
975 set_zoom_offset(&params, 0, -1);
976 else
977 set_zoom_offset(&params, 0, -10);
978 break;
979 case GP_KEY_DOWN:
980 if (shift_flag)
981 set_zoom_offset(&params, 0, 1);
982 else
983 set_zoom_offset(&params, 0, 10);
984 break;
985 next:
986 case GP_KEY_SPACE:
987 params.show_progress_once = 1;
988 if (shift_flag)
989 image_seek(&params, IMG_CUR, 10);
990 else
991 image_seek(&params, IMG_CUR, 1);
992 break;
993 prev:
994 case GP_KEY_BACKSPACE:
995 params.show_progress_once = 1;
996 if (shift_flag)
997 image_seek(&params, IMG_CUR, -10);
998 else
999 image_seek(&params, IMG_CUR, -1);
1000 break;
1001 case GP_KEY_1 ... GP_KEY_9: {
1002 float val = ev.val.key.key - GP_KEY_1 + 1;
1004 if (!shift_flag)
1005 val = 1/val;
1007 zoom_set(&params, val);
1008 } break;
1009 case GP_KEY_0:
1010 if (shift_flag)
1011 zoom_set(&params, 10);
1012 else
1013 zoom_set(&params, 0.1);
1014 break;
1015 case GP_KEY_KP_PLUS:
1016 case GP_KEY_DOT:
1017 params.show_progress_once = 1;
1018 if (shift_flag)
1019 zoom_mul(&params, 1.1);
1020 else
1021 zoom_mul(&params, 1.5);
1022 break;
1023 case GP_KEY_KP_MINUS:
1024 case GP_KEY_COMMA:
1025 params.show_progress_once = 1;
1026 if (shift_flag)
1027 zoom_mul(&params, 1/1.1);
1028 else
1029 zoom_mul(&params, 1/1.5);
1030 break;
1031 case GP_KEY_F1 ... GP_KEY_F10:
1032 image_action_run(ev.val.key.key - GP_KEY_F1 + 1,
1033 image_loader_img_path());
1034 break;
1036 break;
1037 case GP_EV_SYS:
1038 switch (ev.code) {
1039 case GP_EV_SYS_RESIZE:
1040 /* stop loader thread before resizing backend buffer */
1041 stop_loader();
1042 GP_BackendResizeAck(backend);
1043 GP_Fill(backend->context, 0);
1044 params.show_progress_once = 1;
1045 show_image(&params);
1046 break;
1047 case GP_EV_SYS_QUIT:
1048 GP_BackendExit(backend);
1049 return 0;
1050 break;
1052 break;
1057 return 0;