From 6f7cb3039bf9bf549a6d5a08360484690af85a57 Mon Sep 17 00:00:00 2001 From: Mike Will Date: Sun, 2 Oct 2022 18:20:13 -0400 Subject: [PATCH 1/2] improve zooming feature with logarithmic scale Zooming now uses a logarithmic scale, and the events for zooming in/out gravitate to powers of 2, such as 50%, 100%, 200%, etc. The option `--zoom-step` has been changed to represent the number of zoom steps between powers of 2, with the default being 3 steps. --- man/feh.pre | 10 +++++----- src/events.c | 53 +++++++++++++++++++++++++++---------------------- src/keyevents.c | 23 +++++++++++++-------- src/options.c | 12 +++++------ src/options.h | 2 +- src/winwidget.c | 13 +++++++----- src/winwidget.h | 3 ++- 7 files changed, 66 insertions(+), 50 deletions(-) diff --git a/man/feh.pre b/man/feh.pre index af1def16..7b7324bb 100644 --- a/man/feh.pre +++ b/man/feh.pre @@ -1003,12 +1003,12 @@ zoom the image like the .Cm --bg-fill mode. . -.It Cm --zoom-step Ar percent +.It Cm --zoom-step Ar index . -Zoom images in and out by -.Ar percent -.Pq default: 25 -when using the zoom keys and buttons. +Zoom images in and out by a factor of the nth root +.Pq Ar index +of 2 when using the zoom keys and buttons. The default is the 3rd root, +which means that there will be 3 steps between powers of 2. . .El . diff --git a/src/events.c b/src/events.c index 524ccad7..650a88dd 100644 --- a/src/events.c +++ b/src/events.c @@ -40,6 +40,8 @@ fehkey *feh_str_to_kb(char *action); feh_event_handler *ev_handler[LASTEvent]; +static const double zoom_rate = M_LN2 / 128.0; + static void feh_event_handle_ButtonPress(XEvent * ev); static void feh_event_handle_ButtonRelease(XEvent * ev); static void feh_event_handle_LeaveNotify(XEvent * ev); @@ -242,29 +244,31 @@ static void feh_event_handle_ButtonPress(XEvent * ev) D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); winwid->click_offset_x = ev->xbutton.x; winwid->click_offset_y = ev->xbutton.y; - winwid->old_zoom = winwid->zoom; + winwid->old_step = round(log(winwid->zoom) / zoom_rate); /* required to adjust the image position in zoom mode */ winwid->im_click_offset_x = (winwid->click_offset_x - - winwid->im_x) / winwid->old_zoom; + - winwid->im_x) / winwid->zoom; winwid->im_click_offset_y = (winwid->click_offset_y - - winwid->im_y) / winwid->old_zoom; + - winwid->im_y) / winwid->zoom; } else if (feh_is_bb(EVENT_zoom_in, button, state)) { D(("Zoom_In Button Press event\n")); D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + if (winwid->zoom >= ZOOM_MAX || opt.step_rate <= 0) + return; + winwid->click_offset_x = ev->xbutton.x; winwid->click_offset_y = ev->xbutton.y; - winwid->old_zoom = winwid->zoom; /* required to adjust the image position in zoom mode */ winwid->im_click_offset_x = (winwid->click_offset_x - - winwid->im_x) / winwid->old_zoom; + - winwid->im_x) / winwid->zoom; winwid->im_click_offset_y = (winwid->click_offset_y - - winwid->im_y) / winwid->old_zoom; + - winwid->im_y) / winwid->zoom; /* copied from zoom_in, keyevents.c */ - winwid->zoom = winwid->zoom * opt.zoom_rate; + winwid->zoom = exp(++winwid->zoom_step * opt.step_rate); if (winwid->zoom > ZOOM_MAX) winwid->zoom = ZOOM_MAX; @@ -281,18 +285,20 @@ static void feh_event_handle_ButtonPress(XEvent * ev) } else if (feh_is_bb(EVENT_zoom_out, button, state)) { D(("Zoom_Out Button Press event\n")); D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + if (winwid->zoom <= ZOOM_MIN || opt.step_rate <= 0) + return; + winwid->click_offset_x = ev->xbutton.x; winwid->click_offset_y = ev->xbutton.y; - winwid->old_zoom = winwid->zoom; /* required to adjust the image position in zoom mode */ winwid->im_click_offset_x = (winwid->click_offset_x - - winwid->im_x) / winwid->old_zoom; + - winwid->im_x) / winwid->zoom; winwid->im_click_offset_y = (winwid->click_offset_y - - winwid->im_y) / winwid->old_zoom; + - winwid->im_y) / winwid->zoom; /* copied from zoom_out, keyevents.c */ - winwid->zoom = winwid->zoom / opt.zoom_rate; + winwid->zoom = exp(--winwid->zoom_step * opt.step_rate); if (winwid->zoom < ZOOM_MIN) winwid->zoom = ZOOM_MIN; @@ -400,11 +406,16 @@ static void feh_event_handle_ButtonRelease(XEvent * ev) opt.mode = MODE_NORMAL; winwid->mode = MODE_NORMAL; - if ((feh_is_bb(EVENT_zoom, button, state)) - && (ev->xbutton.x == winwid->click_offset_x) - && (ev->xbutton.y == winwid->click_offset_y)) { - winwid->zoom = 1.0; - winwidget_center_image(winwid); + if ((feh_is_bb(EVENT_zoom, button, state))) { + if ((ev->xbutton.x == winwid->click_offset_x) + && (ev->xbutton.y == winwid->click_offset_y)) { + winwid->zoom = 1.0; + winwid->zoom_step = 0; + winwidget_center_image(winwid); + } else { + winwid->zoom_step = round(log(winwid->zoom) / opt.step_rate); + winwidget_sanitise_offsets(winwid); + } } else winwidget_sanitise_offsets(winwid); @@ -543,14 +554,8 @@ static void feh_event_handle_MotionNotify(XEvent * ev) winwid = winwidget_get_from_window(ev->xmotion.window); if (winwid) { - if (ev->xmotion.x > winwid->click_offset_x) - winwid->zoom = winwid->old_zoom + ( - ((double) ev->xmotion.x - (double) winwid->click_offset_x) - / 128.0); - else - winwid->zoom = winwid->old_zoom - ( - ((double) winwid->click_offset_x - (double) ev->xmotion.x) - / 128.0); + int step = winwid->old_step + ev->xmotion.x - winwid->click_offset_x; + winwid->zoom = exp(step * zoom_rate); if (winwid->zoom < ZOOM_MIN) winwid->zoom = ZOOM_MIN; diff --git a/src/keyevents.c b/src/keyevents.c index ecb4c9d4..781f4f7f 100644 --- a/src/keyevents.c +++ b/src/keyevents.c @@ -602,35 +602,42 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy feh_event_invoke_action(winwid, 9); } else if (feh_is_kp(EVENT_zoom_in, state, keysym, button)) { - winwid->old_zoom = winwid->zoom; - winwid->zoom = winwid->zoom * opt.zoom_rate; + if (winwid->zoom >= ZOOM_MAX || opt.step_rate <= 0) + return; + + double old_zoom = winwid->zoom; + winwid->zoom = exp(++winwid->zoom_step * opt.step_rate); if (winwid->zoom > ZOOM_MAX) winwid->zoom = ZOOM_MAX; winwid->im_x = (winwid->w / 2) - (((winwid->w / 2) - winwid->im_x) / - winwid->old_zoom * winwid->zoom); + old_zoom * winwid->zoom); winwid->im_y = (winwid->h / 2) - (((winwid->h / 2) - winwid->im_y) / - winwid->old_zoom * winwid->zoom); + old_zoom * winwid->zoom); winwidget_sanitise_offsets(winwid); winwidget_render_image(winwid, 0, 0); } else if (feh_is_kp(EVENT_zoom_out, state, keysym, button)) { - winwid->old_zoom = winwid->zoom; - winwid->zoom = winwid->zoom / opt.zoom_rate; + if (winwid->zoom <= ZOOM_MIN || opt.step_rate <= 0) + return; + + double old_zoom = winwid->zoom; + winwid->zoom = exp(--winwid->zoom_step * opt.step_rate); if (winwid->zoom < ZOOM_MIN) winwid->zoom = ZOOM_MIN; winwid->im_x = (winwid->w / 2) - (((winwid->w / 2) - winwid->im_x) / - winwid->old_zoom * winwid->zoom); + old_zoom * winwid->zoom); winwid->im_y = (winwid->h / 2) - (((winwid->h / 2) - winwid->im_y) / - winwid->old_zoom * winwid->zoom); + old_zoom * winwid->zoom); winwidget_sanitise_offsets(winwid); winwidget_render_image(winwid, 0, 0); } else if (feh_is_kp(EVENT_zoom_default, state, keysym, button)) { winwid->zoom = 1.0; + winwid->zoom_step = 0; winwidget_center_image(winwid); winwidget_render_image(winwid, 0, 0); } diff --git a/src/options.c b/src/options.c index 80f13b66..5ef262fb 100644 --- a/src/options.c +++ b/src/options.c @@ -63,7 +63,7 @@ void init_parse_options(int argc, char **argv) opt.font = NULL; opt.max_height = opt.max_width = UINT_MAX; - opt.zoom_rate = 1.25; + opt.step_rate = M_LN2 / 3.0; opt.start_list_at = NULL; opt.jump_on_resort = 1; @@ -848,12 +848,12 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) opt.x11_windowid = strtol(optarg, NULL, 0); break; case OPTION_zoom_step: - opt.zoom_rate = atof(optarg); - if ((opt.zoom_rate <= 0)) { - weprintf("Zooming disabled due to --zoom-step=%f", opt.zoom_rate); - opt.zoom_rate = 1.0; + opt.step_rate = atof(optarg); + if ((opt.step_rate <= 0)) { + weprintf("Zooming disabled due to --zoom-step=%f", opt.step_rate); + opt.step_rate = 0.0; } else { - opt.zoom_rate = 1 + ((float)opt.zoom_rate / 100); + opt.step_rate = M_LN2 / opt.step_rate; } break; default: diff --git a/src/options.h b/src/options.h index 6441e8bc..a384c964 100644 --- a/src/options.h +++ b/src/options.h @@ -127,7 +127,7 @@ struct __fehoptions { int offset_y; int default_zoom; int zoom_mode; - double zoom_rate; + double step_rate; unsigned char adjust_reload; int xinerama_index; char *x11_class; diff --git a/src/winwidget.c b/src/winwidget.c index 132b57e9..5c110086 100644 --- a/src/winwidget.c +++ b/src/winwidget.c @@ -77,7 +77,8 @@ static winwidget winwidget_allocate(void) ret->im_x = 0; ret->im_y = 0; ret->zoom = 1.0; - ret->old_zoom = 1.0; + ret->zoom_step = 0; + ret->old_step = 0; ret->click_offset_x = 0; ret->click_offset_y = 0; @@ -494,6 +495,8 @@ void winwidget_render_image(winwidget winwid, int resize, int force_alias) && (!opt.default_zoom || required_zoom < winwid->zoom)) winwid->zoom = required_zoom; + winwid->zoom_step = round(log(winwid->zoom) / opt.step_rate); + if (opt.offset_flags & XValue) { if (opt.offset_flags & XNegative) { winwid->im_x = winwid->w - (winwid->im_w * winwid->zoom) - opt.offset_x; @@ -1065,12 +1068,12 @@ void feh_debug_print_winwid(winwidget w) "h = %d\n" "im_w = %d\n" "im_h = %d\n" "im_angle = %f\n" "type = %d\n" "had_resize = %d\n" "im = %p\n" "GC = %p\n" "pixmap = %ld\n" "name = %s\n" "file = %p\n" "mode = %d\n" - "im_x = %d\n" "im_y = %d\n" "zoom = %f\n" "old_zoom = %f\n" - "click_offset_x = %d\n" "click_offset_y = %d\n" + "im_x = %d\n" "im_y = %d\n" "zoom = %f\n" "zoom_step = %d\n" + "old_step = %d" "click_offset_x = %d\n" "click_offset_y = %d\n" "has_rotated = %d\n", (void *)w, w->win, w->w, w->h, w->im_w, w->im_h, w->im_angle, w->type, w->had_resize, w->im, (void *)w->gc, w->bg_pmap, w->name, (void *)w->file, w->mode, w->im_x, w->im_y, - w->zoom, w->old_zoom, w->click_offset_x, w->click_offset_y, + w->zoom, w->zoom_step, w->old_step, w->click_offset_x, w->click_offset_y, w->has_rotated); } @@ -1078,7 +1081,7 @@ void winwidget_reset_image(winwidget winwid) { if (!opt.keep_zoom_vp) { winwid->zoom = 1.0; - winwid->old_zoom = 1.0; + winwid->zoom_step = 0; winwid->im_x = 0; winwid->im_y = 0; } diff --git a/src/winwidget.h b/src/winwidget.h index 9d742864..f03287fe 100644 --- a/src/winwidget.h +++ b/src/winwidget.h @@ -107,7 +107,8 @@ struct __winwidget { * all the way up to INT_MAX (eww) */ double zoom; - double old_zoom; + int zoom_step; + int old_step; int click_offset_x; int click_offset_y; From 264d477e269a1e0e3d07ad29a04a7e5e2eb8a1ee Mon Sep 17 00:00:00 2001 From: Mike Will Date: Tue, 28 Mar 2023 10:43:35 -0400 Subject: [PATCH 2/2] add an option to change the rate of zoom mode While `--zoom-steps` is the number of button or key presses between powers of 2, `--zoom-rate` represents the number of pixels between powers of 2 that the user clicks and drags between, so if you added the arg `--zoom-rate=1024`, it would result in a very slow zoom mode. The default is 128 pixels. --- man/feh.pre | 7 +++++++ src/events.c | 8 +++----- src/options.c | 13 ++++++++++++- src/options.h | 2 ++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/man/feh.pre b/man/feh.pre index 7b7324bb..6a9db0e6 100644 --- a/man/feh.pre +++ b/man/feh.pre @@ -1010,6 +1010,13 @@ Zoom images in and out by a factor of the nth root of 2 when using the zoom keys and buttons. The default is the 3rd root, which means that there will be 3 steps between powers of 2. . +.It Cm --zoom-rate Ar index +. +Zoom images in and out by a factor of the nth root +.Pq Ar index +of 2 upon entering zoom mode. The default is the 128th root, +which means that there will be 128 pixels between powers of 2. +. .El . .Sh MONTAGE MODE OPTIONS diff --git a/src/events.c b/src/events.c index 650a88dd..49515901 100644 --- a/src/events.c +++ b/src/events.c @@ -40,8 +40,6 @@ fehkey *feh_str_to_kb(char *action); feh_event_handler *ev_handler[LASTEvent]; -static const double zoom_rate = M_LN2 / 128.0; - static void feh_event_handle_ButtonPress(XEvent * ev); static void feh_event_handle_ButtonRelease(XEvent * ev); static void feh_event_handle_LeaveNotify(XEvent * ev); @@ -244,7 +242,7 @@ static void feh_event_handle_ButtonPress(XEvent * ev) D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); winwid->click_offset_x = ev->xbutton.x; winwid->click_offset_y = ev->xbutton.y; - winwid->old_step = round(log(winwid->zoom) / zoom_rate); + winwid->old_step = round(log(winwid->zoom) / opt.zoom_rate); /* required to adjust the image position in zoom mode */ winwid->im_click_offset_x = (winwid->click_offset_x @@ -553,9 +551,9 @@ static void feh_event_handle_MotionNotify(XEvent * ev) while (XCheckTypedWindowEvent(disp, ev->xmotion.window, MotionNotify, ev)); winwid = winwidget_get_from_window(ev->xmotion.window); - if (winwid) { + if (winwid && opt.zoom_rate > 0) { int step = winwid->old_step + ev->xmotion.x - winwid->click_offset_x; - winwid->zoom = exp(step * zoom_rate); + winwid->zoom = exp(step * opt.zoom_rate); if (winwid->zoom < ZOOM_MIN) winwid->zoom = ZOOM_MIN; diff --git a/src/options.c b/src/options.c index 5ef262fb..b00d7cf0 100644 --- a/src/options.c +++ b/src/options.c @@ -63,6 +63,7 @@ void init_parse_options(int argc, char **argv) opt.font = NULL; opt.max_height = opt.max_width = UINT_MAX; + opt.zoom_rate = M_LN2 / 128.0; opt.step_rate = M_LN2 / 3.0; opt.start_list_at = NULL; @@ -395,6 +396,7 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) {"bg-center" , 0, 0, OPTION_bg_center}, {"bg-scale" , 0, 0, OPTION_bg_scale}, {"zoom" , 1, 0, OPTION_zoom}, + {"zoom-rate" , 1, 0, OPTION_zoom_rate}, {"zoom-step" , 1, 0, OPTION_zoom_step}, {"no-screen-clip", 0, 0, OPTION_no_screen_clip}, {"index-info" , 1, 0, OPTION_index_info}, @@ -847,10 +849,19 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun) case OPTION_window_id: opt.x11_windowid = strtol(optarg, NULL, 0); break; + case OPTION_zoom_rate: + opt.zoom_rate = atof(optarg); + if ((opt.zoom_rate <= 0)) { + weprintf("Zoom mode disabled due to --zoom-rate=%f", opt.zoom_rate); + opt.zoom_rate = 0.0; + } else { + opt.zoom_rate = M_LN2 / opt.zoom_rate; + } + break; case OPTION_zoom_step: opt.step_rate = atof(optarg); if ((opt.step_rate <= 0)) { - weprintf("Zooming disabled due to --zoom-step=%f", opt.step_rate); + weprintf("Zoom steps disabled due to --zoom-step=%f", opt.step_rate); opt.step_rate = 0.0; } else { opt.step_rate = M_LN2 / opt.step_rate; diff --git a/src/options.h b/src/options.h index a384c964..e1599d3a 100644 --- a/src/options.h +++ b/src/options.h @@ -127,6 +127,7 @@ struct __fehoptions { int offset_y; int default_zoom; int zoom_mode; + double zoom_rate; double step_rate; unsigned char adjust_reload; int xinerama_index; @@ -216,6 +217,7 @@ OPTION_bg_scale, OPTION_bg_fill, OPTION_bg_max, OPTION_zoom, +OPTION_zoom_rate, OPTION_zoom_step, OPTION_zoom_in_rate, OPTION_zoom_out_rate,