diff options
author | Harry Jeffery <harry@exec64.co.uk> | 2019-06-15 14:28:29 +0100 |
---|---|---|
committer | Harry Jeffery <harry@exec64.co.uk> | 2019-07-03 20:50:19 +0100 |
commit | 7c7dc660e587eac1aa3c8b3405eba95ba558e682 (patch) | |
tree | 81d12d560b60d397be23c7d132e32a5de30e409a /src/imv.c | |
parent | 20e9d23b82f55a751c3cf1166cb59ef26775ee00 (diff) | |
download | imv-7c7dc660e587eac1aa3c8b3405eba95ba558e682.tar.gz |
Big glfw refactor
I did a lot of this in a very ad-hoc fashion with no proper commit
history. As such, the kindest thing to do seemed to be to just squash it
into this one commit.
Diffstat (limited to 'src/imv.c')
-rw-r--r-- | src/imv.c | 1094 |
1 files changed, 588 insertions, 506 deletions
@@ -10,19 +10,23 @@ #include <unistd.h> #include <wordexp.h> -#include <SDL2/SDL.h> -#include <SDL2/SDL_ttf.h> +#define GLFW_EXPOSE_NATIVE_X11 +#define GLFW_EXPOSE_NATIVE_WAYLAND +#include <GLFW/glfw3.h> +#include <GLFW/glfw3native.h> #include "backend.h" #include "binds.h" +#include "canvas.h" #include "commands.h" +#include "console.h" #include "image.h" #include "ini.h" +#include "keyboard.h" #include "list.h" #include "log.h" #include "navigator.h" #include "source.h" -#include "util.h" #include "viewport.h" /* Some systems like GNU/Hurd don't define PATH_MAX */ @@ -37,12 +41,6 @@ enum scaling_mode { SCALING_MODE_COUNT }; -enum upscaling_method { - UPSCALING_LINEAR, - UPSCALING_NEAREST_NEIGHBOUR, - UPSCALING_METHOD_COUNT, -}; - static const char *scaling_label[] = { "actual size", "shrink to fit", @@ -67,6 +65,29 @@ struct backend_chain { struct backend_chain *next; }; +enum internal_event_type { + NEW_IMAGE, + BAD_IMAGE, + NEW_PATH +}; + +struct internal_event { + enum internal_event_type type; + union { + struct { + struct imv_image *image; + int frametime; + bool is_new_image; + } new_image; + struct { + char *error; + } bad_image; + struct { + char *path; + } new_path; + } data; +}; + struct imv { /* set to true to trigger clean exit */ bool quit; @@ -113,26 +134,40 @@ struct imv { /* scale up / down images to match window, or actual size */ enum scaling_mode scaling_mode; - /* show a solid background colour, or chequerboard pattern */ - enum background_type background_type; - /* the aforementioned background colour */ - struct { unsigned char r, g, b; } background_color; + struct { + /* show a solid background colour, or chequerboard pattern */ + enum background_type type; + /* the aforementioned background colour */ + struct { unsigned char r, g, b; } color; + } background; /* slideshow state tracking */ - unsigned long slideshow_image_duration; - unsigned long slideshow_time_elapsed; + struct { + double duration; + double elapsed; + } slideshow; - /* for animated images, the GetTicks() time to display the next frame */ - unsigned int next_frame_due; - /* how long the next frame to be put onscreen should be displayed for */ - int next_frame_duration; - /* the next frame of an animated image, pre-fetched */ - struct imv_bitmap *next_frame; + struct { + /* for animated images, the getTime() time to display the next frame */ + double due; + /* how long the next frame to be put onscreen should be displayed for */ + double duration; + /* the next frame of an animated image, pre-fetched */ + struct imv_image *image; + } next_frame; - /* overlay font name */ - char *font_name; - /* buffer for storing input commands, NULL when not in command mode */ - char *input_buffer; + struct imv_image *current_image; + + struct { + double x; + double y; + } last_cursor_position; + + /* overlay font */ + struct { + char *name; + int size; + } font; /* if specified by user, the path of the first image to display */ char *starting_path; @@ -141,43 +176,26 @@ struct imv { char *title_text; char *overlay_text; - /* when true, imv will ignore all window events until it encounters a - * ENABLE_INPUT user-event. This is required to overcome a bug where - * SDL will send input events to us from before we gained focus - */ - bool ignore_window_events; - /* imv subsystems */ struct imv_binds *binds; struct imv_navigator *navigator; struct backend_chain *backends; - struct imv_source *source; + struct imv_source *current_source; struct imv_source *last_source; struct imv_commands *commands; - struct imv_image *image; + struct imv_console *console; struct imv_viewport *view; + struct imv_keyboard *keyboard; + struct imv_canvas *canvas; /* if reading an image from stdin, this is the buffer for it */ void *stdin_image_data; size_t stdin_image_data_len; - /* SDL subsystems */ - SDL_Window *window; - SDL_Renderer *renderer; - TTF_Font *font; - SDL_Texture *background_image; - bool sdl_init; - bool ttf_init; - struct { - unsigned int NEW_IMAGE; - unsigned int BAD_IMAGE; - unsigned int NEW_PATH; - unsigned int ENABLE_INPUT; - } events; - struct { - int width; - int height; - } current_image; + GLFWwindow *window; + bool glfw_init; + struct list *internal_events; + pthread_mutex_t internal_events_mutex; }; void command_quit(struct list *args, const char *argstr, void *data); @@ -198,11 +216,11 @@ void command_set_scaling_mode(struct list *args, const char *argstr, void *data) void command_set_slideshow_duration(struct list *args, const char *argstr, void *data); static bool setup_window(struct imv *imv); -static void handle_event(struct imv *imv, SDL_Event *event); +static void consume_internal_event(struct imv *imv, struct internal_event *event); static void render_window(struct imv *imv); static void update_env_vars(struct imv *imv); static size_t generate_env_text(struct imv *imv, char *buf, size_t len, const char *format); - +static size_t read_from_stdin(void **buffer); /* Finds the next split between commands in a string (';'). Provides a pointer * to the next character after the delimiter as out, or a pointer to '\0' if @@ -244,7 +262,7 @@ static void split_commands(const char *start, const char **out, size_t *len) static bool add_bind(struct imv *imv, const char *keys, const char *commands) { struct list *list = imv_bind_parse_keys(keys); - if(!list) { + if (!list) { imv_log(IMV_ERROR, "Invalid key combination"); return false; } @@ -291,77 +309,196 @@ static bool add_bind(struct imv *imv, const char *keys, const char *commands) return success; } -static int async_free_source_thread(void *raw) +static void *async_free_source_thread(void *raw) { struct imv_source *src = raw; src->free(src); - return 0; + return NULL; } static void async_free_source(struct imv_source *src) { - SDL_Thread *thread = SDL_CreateThread(async_free_source_thread, - "async_free_source", src); - SDL_DetachThread(thread); + typedef void *(*thread_func)(void*); + pthread_t thread; + pthread_create(&thread, NULL, (thread_func)async_free_source_thread, src); + pthread_detach(thread); } static void async_load_first_frame(struct imv_source *src) { - typedef int (*thread_func)(void*); - SDL_Thread *thread = SDL_CreateThread((thread_func)src->load_first_frame, - "async_load_first_frame", - src); - SDL_DetachThread(thread); + typedef void *(*thread_func)(void*); + pthread_t thread; + pthread_create(&thread, NULL, (thread_func)src->load_first_frame, src); + pthread_detach(thread); } static void async_load_next_frame(struct imv_source *src) { - typedef int (*thread_func)(void*); - SDL_Thread *thread = SDL_CreateThread((thread_func)src->load_next_frame, - "async_load_next_frame", - src); - SDL_DetachThread(thread); + typedef void *(*thread_func)(void*); + pthread_t thread; + pthread_create(&thread, NULL, (thread_func)src->load_next_frame, src); + pthread_detach(thread); } static void source_callback(struct imv_source_message *msg) { struct imv *imv = msg->user_data; - if (msg->source != imv->source) { + if (msg->source != imv->current_source) { /* We received a message from an old source, tidy up contents * as required, but ignore it. */ - if (msg->bitmap) { - imv_bitmap_free(msg->bitmap); + if (msg->image) { + imv_image_free(msg->image); } return; } - SDL_Event event; - SDL_zero(event); - - if (msg->bitmap) { - event.type = imv->events.NEW_IMAGE; - event.user.data1 = msg->bitmap; - event.user.code = msg->frametime; + struct internal_event *event = calloc(1, sizeof *event); + if (msg->image) { + event->type = NEW_IMAGE; + event->data.new_image.image = msg->image; + event->data.new_image.frametime = msg->frametime; - /* Keep track of the last source to send us a bitmap in order to detect + /* Keep track of the last source to send us an image in order to detect * when we're getting a new image, as opposed to a new frame from the * same image. */ - uintptr_t is_new_image = msg->source != imv->last_source; - event.user.data2 = (void*)is_new_image; + event->data.new_image.is_new_image = msg->source != imv->last_source; imv->last_source = msg->source; } else { - event.type = imv->events.BAD_IMAGE; + event->type = BAD_IMAGE; /* TODO: Something more elegant with error messages */ - /* event.user.data1 = strdup(msg->error); */ + event->data.bad_image.error = strdup(msg->error); + } + + pthread_mutex_lock(&imv->internal_events_mutex); + list_append(imv->internal_events, event); + pthread_mutex_unlock(&imv->internal_events_mutex); + + glfwPostEmptyEvent(); +} + +static void command_callback(const char *text, void *data) +{ + struct imv *imv = data; + struct list *commands = list_create(); + list_append(commands, strdup(text)); + imv_command_exec_list(imv->commands, commands, imv); + list_deep_free(commands); + imv->need_redraw = true; +} + +static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + (void)key; + (void)mods; + struct imv *imv = glfwGetWindowUserPointer(window); + + imv_keyboard_update_key(imv->keyboard, scancode, action == GLFW_PRESS); + + if (action != GLFW_PRESS) { + return; + } + + char keyname[128] = {0}; + imv_keyboard_keyname(imv->keyboard, scancode, keyname, sizeof keyname); + + if (imv_console_is_active(imv->console)) { + + if (imv_console_key(imv->console, keyname)) { + imv->need_redraw = true; + return; + } + + char text[128]; + size_t len = imv_keyboard_get_text(imv->keyboard, scancode, text, sizeof text); + if (len >= sizeof text) { + imv_log(IMV_WARNING, "Keyboard input too large for input buffer. Discarding.\n"); + } else { + imv_console_input(imv->console, text); + } + + } else { + /* In regular mode see if we should enter command mode, otherwise send input + * to the bind system. + */ + if (!strcmp("colon", keyname)) { + fprintf(stderr, "active console\n"); + imv_console_activate(imv->console); + imv->need_redraw = true; + return; + } + + char *keyname = imv_keyboard_describe_key(imv->keyboard, scancode); + if (!keyname) { + return; + } + + struct list *cmds = imv_bind_handle_event(imv->binds, keyname); + if (cmds) { + imv_command_exec_list(imv->commands, cmds, imv); + } + fprintf(stderr, "user hit: '%s'\n", keyname); + free(keyname); + } + + imv->need_redraw = true; +} + +static void resize_callback(GLFWwindow *window, int width, int height) +{ + /* This callback is used for both window and framebuffer resize + * events, so ignore what it passes in and explicitly get the values + * we need for both. + */ + (void)width; + (void)height; + struct imv *imv = glfwGetWindowUserPointer(window); + int ww, wh; + int bw, bh; + glfwGetWindowSize(imv->window, &ww, &wh); + glfwGetFramebufferSize(imv->window, &bw, &bh); + imv_viewport_update(imv->view, ww, wh, bw, bh, imv->current_image); + imv_canvas_resize(imv->canvas, bw, bh); + glViewport(0, 0, bw, bh); +} + +static void scroll_callback(GLFWwindow *window, double x, double y) +{ + (void)x; + struct imv *imv = glfwGetWindowUserPointer(window); + imv_viewport_zoom(imv->view, imv->current_image, IMV_ZOOM_MOUSE, + imv->last_cursor_position.x, + imv->last_cursor_position.y, -y); +} + +static void cursor_callback(GLFWwindow *window, double x, double y) +{ + struct imv *imv = glfwGetWindowUserPointer(window); + + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) { + const double dx = x - imv->last_cursor_position.x; + const double dy = y - imv->last_cursor_position.y; + imv_viewport_move(imv->view, dx, dy, imv->current_image); } + imv->last_cursor_position.x = x; + imv->last_cursor_position.y = y; +} - SDL_PushEvent(&event); + +static void log_to_stderr(enum imv_log_level level, const char *text, void *data) +{ + (void)data; + if (level >= IMV_INFO) { + fprintf(stderr, "%s", text); + } } struct imv *imv_create(void) { + /* Attach log to stderr */ + imv_log_add_log_callback(&log_to_stderr, NULL); + struct imv *imv = calloc(1, sizeof *imv); imv->initial_width = 1280; imv->initial_height = 720; @@ -369,10 +506,13 @@ struct imv *imv_create(void) imv->need_rescale = true; imv->scaling_mode = SCALING_FULL; imv->loop_input = true; - imv->font_name = strdup("Monospace:24"); + imv->font.name = strdup("Monospace"); + imv->font.size = 24; imv->binds = imv_binds_create(); imv->navigator = imv_navigator_create(); imv->commands = imv_commands_create(); + imv->console = imv_console_create(); + imv_console_set_command_callback(imv->console, &command_callback, imv); imv->title_text = strdup( "imv - [${imv_current_index}/${imv_file_count}]" " [${imv_width}x${imv_height}] [${imv_scale}%]" @@ -383,6 +523,8 @@ struct imv *imv_create(void) " [${imv_width}x${imv_height}] [${imv_scale}%]" " $imv_current_file [$imv_scaling_mode]" ); + imv->internal_events = list_create(); + pthread_mutex_init(&imv->internal_events_mutex, NULL); imv_command_register(imv->commands, "quit", &command_quit); imv_command_register(imv->commands, "pan", &command_pan); @@ -403,11 +545,11 @@ struct imv *imv_create(void) add_bind(imv, "q", "quit"); add_bind(imv, "<Left>", "select_rel -1"); - add_bind(imv, "<LeftSquareBracket>", "select_rel -1"); + add_bind(imv, "<bracketleft>", "select_rel -1"); add_bind(imv, "<Right>", "select_rel 1"); - add_bind(imv, "<RightSquareBracket>", "select_rel 1"); + add_bind(imv, "<bracketright>", "select_rel 1"); add_bind(imv, "gg", "select_abs 0"); - add_bind(imv, "<Shift+g>", "select_abs -1"); + add_bind(imv, "<Shift+G>", "select_abs -1"); add_bind(imv, "j", "pan 0 -50"); add_bind(imv, "k", "pan 0 50"); add_bind(imv, "h", "pan 50 0"); @@ -416,66 +558,56 @@ struct imv *imv_create(void) add_bind(imv, "f", "fullscreen"); add_bind(imv, "d", "overlay"); add_bind(imv, "p", "exec echo $imv_current_file"); - add_bind(imv, "<Equals>", "zoom 1"); + add_bind(imv, "<equal>", "zoom 1"); add_bind(imv, "<Up>", "zoom 1"); - add_bind(imv, "+", "zoom 1"); + add_bind(imv, "<Shift+plus>", "zoom 1"); add_bind(imv, "i", "zoom 1"); add_bind(imv, "<Down>", "zoom -1"); - add_bind(imv, "-", "zoom -1"); + add_bind(imv, "<minus>", "zoom -1"); add_bind(imv, "o", "zoom -1"); add_bind(imv, "c", "center"); add_bind(imv, "s", "scaling_mode next"); add_bind(imv, "a", "zoom actual"); add_bind(imv, "r", "reset"); - add_bind(imv, ".", "next_frame"); - add_bind(imv, "<Space>", "toggle_playing"); + add_bind(imv, "<period>", "next_frame"); + add_bind(imv, "<space>", "toggle_playing"); add_bind(imv, "t", "slideshow_duration +1"); - add_bind(imv, "<Shift+t>", "slideshow_duration -1"); + add_bind(imv, "<Shift+T>", "slideshow_duration -1"); return imv; } void imv_free(struct imv *imv) { - free(imv->font_name); + free(imv->font.name); free(imv->title_text); free(imv->overlay_text); imv_binds_free(imv->binds); imv_navigator_free(imv->navigator); - if (imv->source) { - imv->source->free(imv->source); + if (imv->current_source) { + imv->current_source->free(imv->current_source); } imv_commands_free(imv->commands); + imv_console_free(imv->console); imv_viewport_free(imv->view); - if (imv->image) { - imv_image_free(imv->image); + imv_keyboard_free(imv->keyboard); + imv_canvas_free(imv->canvas); + if (imv->current_image) { + imv_image_free(imv->current_image); } - if (imv->next_frame) { - imv_bitmap_free(imv->next_frame); + if (imv->next_frame.image) { + imv_image_free(imv->next_frame.image); } - if(imv->stdin_image_data) { + if (imv->stdin_image_data) { free(imv->stdin_image_data); } - if(imv->input_buffer) { - free(imv->input_buffer); - } - if(imv->renderer) { - SDL_DestroyRenderer(imv->renderer); - } - if(imv->window) { - SDL_DestroyWindow(imv->window); - } - if(imv->background_image) { - SDL_DestroyTexture(imv->background_image); + list_free(imv->internal_events); + pthread_mutex_destroy(&imv->internal_events_mutex); + if (imv->window) { + glfwDestroyWindow(imv->window); } - if(imv->font) { - TTF_CloseFont(imv->font); - } - if(imv->ttf_init) { - TTF_Quit(); - } - if(imv->sdl_init) { - SDL_Quit(); + if (imv->glfw_init) { + glfwTerminate(); } free(imv); } @@ -490,21 +622,21 @@ void imv_install_backend(struct imv *imv, const struct imv_backend *backend) static bool parse_bg(struct imv *imv, const char *bg) { - if(strcmp("checks", bg) == 0) { - imv->background_type = BACKGROUND_CHEQUERED; + if (strcmp("checks", bg) == 0) { + imv->background.type = BACKGROUND_CHEQUERED; } else { - imv->background_type = BACKGROUND_SOLID; - if(*bg == '#') + imv->background.type = BACKGROUND_SOLID; + if (*bg == '#') ++bg; char *ep; uint32_t n = strtoul(bg, &ep, 16); - if(*ep != '\0' || ep - bg != 6 || n > 0xFFFFFF) { + if (*ep != '\0' || ep - bg != 6 || n > 0xFFFFFF) { imv_log(IMV_ERROR, "Invalid hex color: '%s'\n", bg); return false; } - imv->background_color.b = n & 0xFF; - imv->background_color.g = (n >> 8) & 0xFF; - imv->background_color.r = (n >> 16); + imv->background.color.b = n & 0xFF; + imv->background.color.g = (n >> 8) & 0xFF; + imv->background.color.r = (n >> 16); } return true; } @@ -512,24 +644,7 @@ static bool parse_bg(struct imv *imv, const char *bg) static bool parse_slideshow_duration(struct imv *imv, const char *duration) { char *decimal; - imv->slideshow_image_duration = strtoul(duration, &decimal, 10); - imv->slideshow_image_duration *= 1000; - if (*decimal == '.') { - char *ep; - long delay = strtoul(++decimal, &ep, 10); - for (int i = 3 - (ep - decimal); i; i--) { - delay *= 10; - } - if (delay < 1000) { - imv->slideshow_image_duration += delay; - } else { - imv->slideshow_image_duration = ULONG_MAX; - } - } - if (imv->slideshow_image_duration == ULONG_MAX) { - imv_log(IMV_ERROR, "Wrong slideshow duration '%s'. Aborting.\n", optarg); - return false; - } + imv->slideshow.duration = strtod(duration, &decimal); return true; } @@ -588,28 +703,31 @@ static bool parse_resizing_mode(struct imv *imv, const char *method) return false; } -static int load_paths_from_stdin(void *data) +static void *load_paths_from_stdin(void *data) { struct imv *imv = data; imv_log(IMV_INFO, "Reading paths from stdin..."); char buf[PATH_MAX]; - while(fgets(buf, sizeof(buf), stdin) != NULL) { + while (fgets(buf, sizeof(buf), stdin) != NULL) { size_t len = strlen(buf); - if(buf[len-1] == '\n') { + if (buf[len-1] == '\n') { buf[--len] = 0; } - if(len > 0) { - /* return the path via SDL event queue */ - SDL_Event event; - SDL_zero(event); - event.type = imv->events.NEW_PATH; - event.user.data1 = strdup(buf); - SDL_PushEvent(&event); + if (len > 0) { + struct internal_event *event = calloc(1, sizeof *event); + event->type = NEW_PATH; + event->data.new_path.path = strdup(buf); + + pthread_mutex_lock(&imv->internal_events_mutex); + list_append(imv->internal_events, event); + pthread_mutex_unlock(&imv->internal_events_mutex); + + glfwPostEmptyEvent(); } } - return 0; + return NULL; } static void print_help(struct imv *imv) @@ -646,7 +764,8 @@ bool imv_parse_args(struct imv *imv, int argc, char **argv) int o; - while((o = getopt(argc, argv, "frdwWxhvlu:s:n:b:t:")) != -1) { + /* TODO getopt_long */ + while ((o = getopt(argc, argv, "frdwWxhvlu:s:n:b:t:")) != -1) { switch(o) { case 'f': imv->fullscreen = true; break; case 'r': imv->recursive_load = true; break; @@ -660,30 +779,30 @@ bool imv_parse_args(struct imv *imv, int argc, char **argv) print_help(imv); imv->quit = true; return true; - case 'v': + case 'v': printf("Version: %s\n", IMV_VERSION); imv->quit = true; return false; case 's': - if(!parse_scaling_mode(imv, optarg)) { + if (!parse_scaling_mode(imv, optarg)) { imv_log(IMV_ERROR, "Invalid scaling mode. Aborting.\n"); return false; } break; case 'u': - if(!parse_upscaling_method(imv, optarg)) { + if (!parse_upscaling_method(imv, optarg)) { imv_log(IMV_ERROR, "Invalid upscaling method. Aborting.\n"); return false; } break; case 'b': - if(!parse_bg(imv, optarg)) { + if (!parse_bg(imv, optarg)) { imv_log(IMV_ERROR, "Invalid background. Aborting.\n"); return false; } break; case 't': - if(!parse_slideshow_duration(imv, optarg)) { + if (!parse_slideshow_duration(imv, optarg)) { imv_log(IMV_ERROR, "Invalid slideshow duration. Aborting.\n"); return false; } @@ -698,19 +817,19 @@ bool imv_parse_args(struct imv *imv, int argc, char **argv) argv += optind; /* if no paths are given as args, expect them from stdin */ - if(argc == 0) { + if (argc == 0) { imv->paths_from_stdin = true; } else { /* otherwise, add the paths */ bool data_from_stdin = false; - for(int i = 0; i < argc; ++i) { + for (int i = 0; i < argc; ++i) { /* Special case: '-' denotes reading image data from stdin */ - if(!strcmp("-", argv[i])) { - if(imv->paths_from_stdin) { + if (!strcmp("-", argv[i])) { + if (imv->paths_from_stdin) { imv_log(IMV_ERROR, "Can't read paths AND image data from stdin. Aborting.\n"); return false; - } else if(data_from_stdin) { + } else if (data_from_stdin) { imv_log(IMV_ERROR, "Can't read image data from stdin twice. Aborting.\n"); return false; } @@ -733,64 +852,67 @@ void imv_add_path(struct imv *imv, const char *path) int imv_run(struct imv *imv) { - if(imv->quit) + if (imv->quit) return 0; - if(!setup_window(imv)) + if (!setup_window(imv)) return 1; /* if loading paths from stdin, kick off a thread to do that - we'll receive - * events back via SDL */ - if(imv->paths_from_stdin) { - SDL_Thread *thread; - thread = SDL_CreateThread(load_paths_from_stdin, "load_paths_from_stdin", imv); - SDL_DetachThread(thread); + * events back via internal events */ + if (imv->paths_from_stdin) { + pthread_t thread; + pthread_create(&thread, NULL, load_paths_from_stdin, imv); + pthread_detach(thread); } - if(imv->starting_path) { + if (imv->starting_path) { int index = imv_navigator_find_path(imv->navigator, imv->starting_path); - if(index == -1) { + if (index == -1) { index = (int) strtol(imv->starting_path, NULL, 10); index -= 1; /* input is 1-indexed, internally we're 0 indexed */ - if(errno == EINVAL) { + if (errno == EINVAL) { index = -1; } } - if(index >= 0) { + if (index >= 0) { imv_navigator_select_str(imv->navigator, index); } else { imv_log(IMV_ERROR, "Invalid starting image: %s\n", imv->starting_path); } } - /* cache current image's dimensions */ - imv->current_image.width = 0; - imv->current_image.height = 0; - /* time keeping */ - unsigned int last_time = SDL_GetTicks(); - unsigned int current_time; + double last_time = glfwGetTime(); + double current_time; + + + while (!imv->quit && !glfwWindowShouldClose(imv->window)) { - while(!imv->quit) { + glfwPollEvents(); - SDL_Event e; - while(!imv->quit && SDL_PollEvent(&e)) { - handle_event(imv, &e); + /* Handle any new internal events */ + pthread_mutex_lock(&imv->internal_events_mutex); + while (imv->internal_events->len > 0) { + struct internal_event *event = imv->internal_events->items[0]; + consume_internal_event(imv, event); + list_remove(imv->internal_events, 0); } + pthread_mutex_unlock(&imv->internal_events_mutex); /* if we're quitting, don't bother drawing any more images */ - if(imv->quit) { + if (imv->quit) { break; } /* Check if navigator wrapped around paths lists */ - if(!imv->loop_input && imv_navigator_wrapped(imv->navigator)) { + if (!imv->loop_input && imv_navigator_wrapped(imv->navigator)) { break; } /* if we're out of images, and we're not expecting more from stdin, quit */ - if(!imv->paths_from_stdin && imv_navigator_length(imv->navigator) == 0) { + if (!imv->paths_from_stdin && imv_navigator_length(imv->navigator) == 0) { imv_log(IMV_INFO, "No input files left. Exiting.\n"); imv->quit = true; continue; @@ -804,7 +926,7 @@ int imv_run(struct imv *imv) while (imv_navigator_poll_changed(imv->navigator)) { const char *current_path = imv_navigator_selection(imv->navigator); /* check we got a path back */ - if(strcmp("", current_path)) { + if (strcmp("", current_path)) { const bool path_is_stdin = !strcmp("-", current_path); struct imv_source *new_source; @@ -843,20 +965,20 @@ int imv_run(struct imv *imv) } if (result == BACKEND_SUCCESS) { - if (imv->source) { - async_free_source(imv->source); + if (imv->current_source) { + async_free_source(imv->current_source); } - imv->source = new_source; - imv->source->callback = &source_callback; - imv->source->user_data = imv; - async_load_first_frame(imv->source); + imv->current_source = new_source; + imv->current_source->callback = &source_callback; + imv->current_source->user_data = imv; + async_load_first_frame(imv->current_source); imv->loading = true; imv_viewport_set_playing(imv->view, true); char title[1024]; generate_env_text(imv, title, sizeof title, imv->title_text); - imv_viewport_set_title(imv->view, title); + glfwSetWindowTitle(imv->window, title); } else { /* Error loading path so remove it from the navigator */ imv_navigator_remove(imv->navigator, current_path); @@ -864,82 +986,96 @@ int imv_run(struct imv *imv) } } - if(imv->need_rescale) { + if (imv->need_rescale) { int ww, wh; - SDL_GetWindowSize(imv->window, &ww, &wh); + glfwGetWindowSize(imv->window, &ww, &wh); imv->need_rescale = false; - if(imv->scaling_mode == SCALING_NONE || + if (imv->scaling_mode == SCALING_NONE || (imv->scaling_mode == SCALING_DOWN - && ww > imv->current_image.width - && wh > imv->current_image.height)) { - imv_viewport_scale_to_actual(imv->view, imv->image); + && ww > imv_image_width(imv->current_image) + && wh > imv_image_height(imv->current_image))) { + imv_viewport_scale_to_actual(imv->view, imv->current_image); } else { - imv_viewport_scale_to_window(imv->view, imv->image); + imv_viewport_scale_to_window(imv->view, imv->current_image); } } - current_time = SDL_GetTicks(); + current_time = glfwGetTime(); /* Check if a new frame is due */ - if (imv_viewport_is_playing(imv->view) && imv->next_frame - && imv->next_frame_due && imv->next_frame_due <= current_time) { - imv_image_set_bitmap(imv->image, imv->next_frame); - imv->current_image.width = imv->next_frame->width; - imv->current_image.height = imv->next_frame->height; - imv_bitmap_free(imv->next_frame); - imv->next_frame = NULL; - imv->next_frame_due = current_time + imv->next_frame_duration; - imv->next_frame_duration = 0; + if (imv_viewport_is_playing(imv->view) && imv->next_frame.image + && imv->next_frame.due && imv->next_frame.due <= current_time) { + if (imv->current_image) { + imv_image_free(imv->current_image); + } + imv->current_image = imv->next_frame.image; + imv->next_frame.image = NULL; + imv->next_frame.due = current_time + imv->next_frame.duration; + imv->next_frame.duration = 0; imv->need_redraw = true; /* Trigger loading of a new frame, now this one's being displayed */ - if (imv->source && imv->source->load_next_frame) { - async_load_next_frame(imv->source); + if (imv->current_source && imv->current_source->load_next_frame) { + async_load_next_frame(imv->current_source); } } /* handle slideshow */ - if(imv->slideshow_image_duration != 0) { - unsigned int dt = current_time - last_time; + if (imv->slideshow.duration != 0.0) { + double dt = current_time - last_time; - imv->slideshow_time_elapsed += dt; - imv->need_redraw = true; /* need to update display */ - if(imv->slideshow_time_elapsed >= imv->slideshow_image_duration) { + imv->slideshow.elapsed += dt; + /* imv->need_redraw = true; /1* need to update display *1/ */ + if (imv->slideshow.elapsed >= imv->slideshow.duration) { imv_navigator_select_rel(imv->navigator, 1); - imv->slideshow_time_elapsed = 0; + imv->slideshow.elapsed = 0; } } last_time = current_time; /* check if the viewport needs a redraw */ - if(imv_viewport_needs_redraw(imv->view)) { + if (imv_viewport_needs_redraw(imv->view)) { imv->need_redraw = true; } - if(imv->need_redraw) { + if (imv->need_redraw) { + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); render_window(imv); - SDL_RenderPresent(imv->renderer); + glfwSwapBuffers(imv->window); } /* sleep until we have something to do */ - unsigned int timeout = 1000; /* milliseconds */ + double timeout = 1.0; /* seconds */ + + /* If we need to display the next frame of an animation soon we should + * limit our sleep until the next frame is due. If the frame is overdue, + * sleep for 1ms at a time to avoid a glfw race between WaitEvents and + * PostEmptyEvent. + */ + if (imv_viewport_is_playing(imv->view) && imv->next_frame.due != 0.0) { + timeout = imv->next_frame.due - current_time; + if (timeout < 0.001) { + timeout = 0.001; + } + } - /* if we need to display the next frame of an animation soon we should - * limit our sleep until the next frame is due */ - if (imv_viewport_is_playing(imv->view) - && imv->next_frame_due > current_time) { - timeout = imv->next_frame_due - current_time; + if (imv->slideshow.duration > 0) { + double timeleft = imv->slideshow.elapsed - imv->slideshow.duration; + if (timeleft > 0.0 && timeleft < timeout) { + timeout = timeleft + 0.005; + } } - /* go to sleep until an input event, etc. or the timeout expires */ - SDL_WaitEventTimeout(NULL, timeout); + /* Go to sleep until an input/internal event or the timeout expires */ + glfwWaitEventsTimeout(timeout); } - if(imv->list_files_at_exit) { - for(size_t i = 0; i < imv_navigator_length(imv->navigator); ++i) + if (imv->list_files_at_exit) { + for (size_t i = 0; i < imv_navigator_length(imv->navigator); ++i) puts(imv_navigator_at(imv->navigator, i)); } @@ -948,81 +1084,74 @@ int imv_run(struct imv *imv) static bool setup_window(struct imv *imv) { - if(SDL_Init(SDL_INIT_VIDEO) != 0) { - imv_log(IMV_ERROR, "SDL Failed to Init: %s\n", SDL_GetError()); + if (!glfwInit()) { + const char *err = NULL; + glfwGetError(&err); + imv_log(IMV_ERROR, "glfw failed to init: %s\n", err); return false; } - /* register custom events */ - imv->events.NEW_IMAGE = SDL_RegisterEvents(1); - imv->events.BAD_IMAGE = SDL_RegisterEvents(1); - imv->events.NEW_PATH = SDL_RegisterEvents(1); - imv->events.ENABLE_INPUT = SDL_RegisterEvents(1); - - imv->sdl_init = true; + imv->glfw_init = true; - imv->window = SDL_CreateWindow( - "imv", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - imv->initial_width, imv->initial_height, - SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + imv->window = glfwCreateWindow(imv->initial_width, imv->initial_height, + "imv", NULL, NULL); - if(!imv->window) { - imv_log(IMV_ERROR, "SDL Failed to create window: %s\n", SDL_GetError()); + if (!imv->window) { + const char *err = NULL; + glfwGetError(&err); + imv_log(IMV_ERROR, "glfw failed to create window: %s\n", err); return false; } - /* we'll use SDL's built-in renderer, hardware accelerated if possible */ - imv->renderer = SDL_CreateRenderer(imv->window, -1, 0); - if(!imv->renderer) { - imv_log(IMV_ERROR, "SDL Failed to create renderer: %s\n", SDL_GetError()); - return false; - } + glfwSetWindowUserPointer(imv->window, imv); + glfwSetKeyCallback(imv->window, &key_callback); + glfwSetScrollCallback(imv->window, &scroll_callback); + glfwSetCursorPosCallback(imv->window, &cursor_callback); + glfwSetWindowSizeCallback(imv->window, &resize_callback); + glfwSetFramebufferSizeCallback(imv->window, &resize_callback); - /* use the appropriate resampling method */ - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, - imv->upscaling_method == UPSCALING_LINEAR? "1" : "0"); + glfwMakeContextCurrent(imv->window); /* allow fullscreen to be maintained even when focus is lost */ - SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, - imv->stay_fullscreen_on_focus_loss ? "0" : "1"); + glfwWindowHint(GLFW_AUTO_ICONIFY, + imv->stay_fullscreen_on_focus_loss ? GLFW_FALSE : GLFW_TRUE); - /* construct a chequered background image */ - if(imv->background_type == BACKGROUND_CHEQUERED) { - imv->background_image = create_chequered(imv->renderer); + { + int ww, wh, bw, bh; + glfwGetWindowSize(imv->window, &ww, &wh); + glfwGetFramebufferSize(imv->window, &bw, &bh); + imv->view = imv_viewport_create(ww, wh, bw, bh); } - /* set up the required fonts and surfaces for displaying the overlay */ - TTF_Init(); - imv->ttf_init = true; - imv->font = load_font(imv->font_name); - if(!imv->font) { - imv_log(IMV_ERROR, "Error loading font: %s\n", TTF_GetError()); - return false; + /* put us in fullscren mode to begin with if requested */ + if (imv->fullscreen) { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(imv->window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); } - imv->image = imv_image_create(imv->renderer); - imv->view = imv_viewport_create(imv->window, imv->renderer); - - /* put us in fullscren mode to begin with if requested */ - if(imv->fullscreen) { - imv_viewport_toggle_fullscreen(imv->view); + { + imv->keyboard = imv_keyboard_create(); + assert(imv->keyboard); } - /* start outside of command mode */ - SDL_StopTextInput(); + { + int ww, wh; + glfwGetWindowSize(imv->window, &ww, &wh); + imv->canvas = imv_canvas_create(ww, wh); + imv_canvas_font(imv->canvas, imv->font.name, imv->font.size); + } return true; } -static void handle_new_image(struct imv *imv, struct imv_bitmap *bitmap, int frametime) +static void handle_new_image(struct imv *imv, struct imv_image *image, int frametime) { - imv_image_set_bitmap(imv->image, bitmap); - imv->current_image.width = bitmap->width; - imv->current_image.height = bitmap->height; - imv_bitmap_free(bitmap); + if (imv->current_image) { + imv_image_free(imv->current_image); + } + imv->current_image = image; imv->need_redraw = true; imv->need_rescale = true; /* If autoresizing on every image is enabled, make sure we do so */ @@ -1031,49 +1160,47 @@ static void handle_new_image(struct imv *imv, struct imv_bitmap *bitmap, int fra } if (imv->need_resize) { imv->need_resize = false; - SDL_SetWindowSize(imv->window, imv->current_image.width, imv->current_image.height); + glfwSetWindowSize(imv->window, imv_image_width(image), imv_image_height(image)); if (imv->resize_mode == RESIZE_CENTER) { - SDL_SetWindowPosition(imv->window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + /* TODO centering */ + /* glfwSetWindowPos(imv->window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); */ } } imv->loading = false; - imv->next_frame_due = frametime ? SDL_GetTicks() + frametime : 0; - imv->next_frame_duration = 0; + imv->next_frame.due = frametime ? glfwGetTime() + frametime * 0.001 : 0.0; + imv->next_frame.duration = 0.0; /* If this is an animated image, we should kick off loading the next frame */ - if (imv->source && imv->source->load_next_frame && frametime) { - async_load_next_frame(imv->source); + if (imv->current_source && imv->current_source->load_next_frame && frametime) { + async_load_next_frame(imv->current_source); } } -static void handle_new_frame(struct imv *imv, struct imv_bitmap *bitmap, int frametime) +static void handle_new_frame(struct imv *imv, struct imv_image *image, int frametime) { - if (imv->next_frame) { - imv_bitmap_free(imv->next_frame); + if (imv->next_frame.image) { + imv_image_free(imv->next_frame.image); } - imv->next_frame = bitmap; + imv->next_frame.image = image; - imv->next_frame_duration = frametime; + imv->next_frame.duration = frametime * 0.001; } -static void handle_event(struct imv *imv, SDL_Event *event) +static void consume_internal_event(struct imv *imv, struct internal_event *event) { - const int command_buffer_len = 1024; - - if (event->type == imv->events.NEW_IMAGE) { - /* new image vs just a new frame of the same image */ - bool is_new_image = !!event->user.data2; - if (is_new_image) { - handle_new_image(imv, event->user.data1, event->user.code); + if (event->type == NEW_IMAGE) { + /* New image vs just a new frame of the same image */ + if (event->data.new_image.is_new_image) { + handle_new_image(imv, event->data.new_image.image, event->data.new_image.frametime); } else { - handle_new_frame(imv, event->user.data1, event->user.code); + handle_new_frame(imv, event->data.new_image.image, event->data.new_image.frametime); } - return; - } else if (event->type == imv->events.BAD_IMAGE) { - /* an image failed to load, remove it from our image list */ + + } else if (event->type == BAD_IMAGE) { + /* An image failed to load, remove it from our image list */ const char *err_path = imv_navigator_selection(imv->navigator); - /* special case: the image came from stdin */ + /* Special case: the image came from stdin */ if (strcmp(err_path, "-") == 0) { if (imv->stdin_image_data) { free(imv->stdin_image_data); @@ -1084,171 +1211,78 @@ static void handle_event(struct imv *imv, SDL_Event *event) } imv_navigator_remove(imv->navigator, err_path); - return; - } else if (event->type == imv->events.NEW_PATH) { - /* received a new path from the stdin reading thread */ - imv_add_path(imv, event->user.data1); - free(event->user.data1); - /* need to update image count */ + + } else if (event->type == NEW_PATH) { + /* Received a new path from the stdin reading thread */ + imv_add_path(imv, event->data.new_path.path); + free(event->data.new_path.path); + /* Need to update image count in title */ imv->need_redraw = true; - return; - } else if (event->type == imv->events.ENABLE_INPUT) { - imv->ignore_window_events = false; - return; - } else if (imv->ignore_window_events) { - /* Don't try and process this input event, we're in event ignoring mode */ - return; } - switch (event->type) { - case SDL_QUIT: - imv_command_exec(imv->commands, "quit", imv); - break; - case SDL_TEXTINPUT: - strncat(imv->input_buffer, event->text.text, command_buffer_len - 1); - imv->need_redraw = true; - break; - - case SDL_KEYDOWN: - SDL_ShowCursor(SDL_DISABLE); - - if (imv->input_buffer) { - /* in command mode, update the buffer */ - if (event->key.keysym.sym == SDLK_ESCAPE) { - SDL_StopTextInput(); - free(imv->input_buffer); - imv->input_buffer = NULL; - imv->need_redraw = true; - } else if (event->key.keysym.sym == SDLK_RETURN) { - struct list *commands = list_create(); - list_append(commands, imv->input_buffer); - imv_command_exec_list(imv->commands, commands, imv); - SDL_StopTextInput(); - list_free(commands); - free(imv->input_buffer); - imv->input_buffer = NULL; - imv->need_redraw = true; - } else if (event->key.keysym.sym == SDLK_BACKSPACE) { - const size_t len = strlen(imv->input_buffer); - if (len > 0) { - imv->input_buffer[len - 1] = '\0'; - imv->need_redraw = true; - } - } - - return; - } - - /* Hitting : opens command-entry mode, like vim */ - if (event->key.keysym.sym == SDLK_SEMICOLON - && event->key.keysym.mod & KMOD_SHIFT) { - SDL_StartTextInput(); - imv->input_buffer = malloc(command_buffer_len); - imv->input_buffer[0] = '\0'; - imv->need_redraw = true; - return; - } - - /* If none of the above, add the key to the current key sequence and - * see if that triggers a bind */ - struct list *cmds = imv_bind_handle_event(imv->binds, event); - if (cmds) { - imv_command_exec_list(imv->commands, cmds, imv); - } - break; - case SDL_MOUSEWHEEL: - imv_viewport_zoom(imv->view, imv->image, IMV_ZOOM_MOUSE, event->wheel.y); - SDL_ShowCursor(SDL_ENABLE); - break; - case SDL_MOUSEMOTION: - if (event->motion.state & SDL_BUTTON_LMASK) { - imv_viewport_move(imv->view, event->motion.xrel, event->motion.yrel, imv->image); - } - SDL_ShowCursor(SDL_ENABLE); - break; - case SDL_WINDOWEVENT: - /* For some reason SDL passes events to us that occurred before we - * gained focus, and passes them *after* the focus gained event. - * Due to behavioural quirks from such events, whenever we gain focus - * we have to ignore all window events already in the queue. - */ - if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { - /* disable window event handling */ - imv->ignore_window_events = true; - /* push an event to the back of the event queue to re-enable - * window event handling */ - SDL_Event event; - SDL_zero(event); - event.type = imv->events.ENABLE_INPUT; - SDL_PushEvent(&event); - } - - imv_viewport_update(imv->view, imv->image); - break; - } + free(event); + return; } static void render_window(struct imv *imv) { int ww, wh; - SDL_GetWindowSize(imv->window, &ww, &wh); + glfwGetWindowSize(imv->window, &ww, &wh); /* update window title */ char title_text[1024]; generate_env_text(imv, title_text, sizeof title_text, imv->title_text); - imv_viewport_set_title(imv->view, title_text); + glfwSetWindowTitle(imv->window, title_text); /* first we draw the background */ - if(imv->background_type == BACKGROUND_SOLID) { - /* solid background */ - SDL_SetRenderDrawColor(imv->renderer, - imv->background_color.r, - imv->background_color.g, - imv->background_color.b, - 255); - SDL_RenderClear(imv->renderer); + if (imv->background.type == BACKGROUND_SOLID) { + imv_canvas_clear(imv->canvas); + imv_canvas_color(imv->canvas, + imv->background.color.r, + imv->background.color.g, + imv->background.color.b, + 1.0); + imv_canvas_fill(imv->canvas); + imv_canvas_draw(imv->canvas); } else { /* chequered background */ - int img_w, img_h; - SDL_QueryTexture(imv->background_image, NULL, NULL, &img_w, &img_h); - /* tile the image so it fills the window */ - for(int y = 0; y < wh; y += img_h) { - for(int x = 0; x < ww; x += img_w) { - SDL_Rect dst_rect = {x,y,img_w,img_h}; - SDL_RenderCopy(imv->renderer, imv->background_image, NULL, &dst_rect); - } - } + imv_canvas_fill_checkers(imv->canvas, 16); + imv_canvas_draw(imv->canvas); } /* draw our actual image */ - { + if (imv->current_image) { int x, y; double scale; imv_viewport_get_offset(imv->view, &x, &y); imv_viewport_get_scale(imv->view, &scale); - imv_image_draw(imv->image, x, y, scale); + imv_canvas_draw_image(imv->canvas, imv->current_image, x, y, scale, imv->upscaling_method); } + imv_canvas_clear(imv->canvas); + /* if the overlay needs to be drawn, draw that too */ - if(imv->overlay_enabled && imv->font) { - SDL_Color fg = {255,255,255,255}; - SDL_Color bg = {0,0,0,160}; + if (imv->overlay_enabled) { + const int height = imv->font.size * 1.2; + imv_canvas_color(imv->canvas, 0, 0, 0, 0.75); + imv_canvas_fill_rectangle(imv->canvas, 0, 0, ww, height); + imv_canvas_color(imv->canvas, 1, 1, 1, 1); char overlay_text[1024]; generate_env_text(imv, overlay_text, sizeof overlay_text, imv->overlay_text); - imv_printf(imv->renderer, imv->font, 0, 0, &fg, &bg, "%s", overlay_text); + imv_canvas_printf(imv->canvas, 0, 0, "%s", overlay_text); } /* draw command entry bar if needed */ - if(imv->input_buffer && imv->font) { - SDL_Color fg = {255,255,255,255}; - SDL_Color bg = {0,0,0,160}; - imv_printf(imv->renderer, - imv->font, - 0, wh - TTF_FontHeight(imv->font), - &fg, &bg, - ":%s", imv->input_buffer); + if (imv_console_prompt(imv->console)) { + const int height = imv->font.size * 1.2; + imv_canvas_color(imv->canvas, 0, 0, 0, 0.75); + imv_canvas_fill_rectangle(imv->canvas, 0, wh - height, ww, height); + imv_canvas_color(imv->canvas, 1, 1, 1, 1); + imv_canvas_printf(imv->canvas, 0, wh - height, ":%s|", imv_console_prompt(imv->console)); } + imv_canvas_draw(imv->canvas); + /* redraw complete, unset the flag */ imv->need_redraw = false; } @@ -1265,9 +1299,9 @@ static char *get_config_path(void) "/etc/imv_config", }; - for(size_t i = 0; i < sizeof(config_paths) / sizeof(char*); ++i) { + for (size_t i = 0; i < sizeof(config_paths) / sizeof(char*); ++i) { wordexp_t word; - if(wordexp(config_paths[i], &word, 0) == 0) { + if (wordexp(config_paths[i], &word, 0) == 0) { if (!word.we_wordv[0]) { wordfree(&word); continue; @@ -1276,7 +1310,7 @@ static char *get_config_path(void) char *path = strdup(word.we_wordv[0]); wordfree(&word); - if(!path || access(path, R_OK) == -1) { + if (!path || access(path, R_OK) == -1) { free(path); continue; } @@ -1301,6 +1335,7 @@ static int handle_ini_value(void *user, const char *section, const char *name, const char *value) { struct imv *imv = user; + fprintf(stderr, "got section='%s' name='%s' value='%s'\n", section, name, value); if (!strcmp(section, "binds")) { return add_bind(imv, name, value); @@ -1313,92 +1348,99 @@ static int handle_ini_value(void *user, const char *section, const char *name, if (!strcmp(section, "options")) { - if(!strcmp(name, "fullscreen")) { + if (!strcmp(name, "fullscreen")) { imv->fullscreen = parse_bool(value); return 1; } - if(!strcmp(name, "width")) { + if (!strcmp(name, "width")) { imv->initial_width = strtol(value, NULL, 10); return 1; } - if(!strcmp(name, "height")) { + if (!strcmp(name, "height")) { imv->initial_height = strtol(value, NULL, 10); return 1; } - if(!strcmp(name, "overlay")) { + if (!strcmp(name, "overlay")) { imv->overlay_enabled = parse_bool(value); return 1; } - if(!strcmp(name, "autoresize")) { + if (!strcmp(name, "autoresize")) { return parse_resizing_mode(imv, value); } - if(!strcmp(name, "upscaling_method")) { + if (!strcmp(name, "upscaling_method")) { return parse_upscaling_method(imv, value); } - if(!strcmp(name, "stay_fullscreen_on_focus_loss")) { + if (!strcmp(name, "stay_fullscreen_on_focus_loss")) { imv->stay_fullscreen_on_focus_loss = parse_bool(value); return 1; } - if(!strcmp(name, "recursive")) { + if (!strcmp(name, "recursive")) { imv->recursive_load = parse_bool(value); return 1; } - if(!strcmp(name, "loop_input")) { + if (!strcmp(name, "loop_input")) { imv->loop_input = parse_bool(value); return 1; } - if(!strcmp(name, "list_files_at_exit")) { + if (!strcmp(name, "list_files_at_exit")) { imv->list_files_at_exit = parse_bool(value); return 1; } - if(!strcmp(name, "scaling_mode")) { + if (!strcmp(name, "scaling_mode")) { return parse_scaling_mode(imv, value); } - if(!strcmp(name, "background")) { - if(!parse_bg(imv, value)) { + if (!strcmp(name, "background")) { + if (!parse_bg(imv, value)) { return false; } return 1; } - if(!strcmp(name, "slideshow_duration")) { - if(!parse_slideshow_duration(imv, value)) { + if (!strcmp(name, "slideshow_duration")) { + if (!parse_slideshow_duration(imv, value)) { return false; } return 1; } - if(!strcmp(name, "overlay_font")) { - free(imv->font_name); - imv->font_name = strdup(value); + if (!strcmp(name, "overlay_font")) { + free(imv->font.name); + imv->font.name = strdup(value); + char *sep = strchr(imv->font.name, ':'); + if (sep) { + *sep = 0; + imv->font.size = atoi(sep + 1); + } else { + imv->font.size = 24; + } return 1; } - if(!strcmp(name, "overlay_text")) { + if (!strcmp(name, "overlay_text")) { free(imv->overlay_text); imv->overlay_text = strdup(value); return 1; } - if(!strcmp(name, "title_text")) { + if (!strcmp(name, "title_text")) { free(imv->title_text); imv->title_text = strdup(value); return 1; } - if(!strcmp(name, "suppress_default_binds")) { + if (!strcmp(name, "suppress_default_binds")) { const bool suppress_default_binds = parse_bool(value); - if(suppress_default_binds) { + if (suppress_default_binds) { /* clear out any default binds if requested */ imv_binds_clear(imv->binds); } @@ -1415,7 +1457,7 @@ static int handle_ini_value(void *user, const char *section, const char *name, bool imv_load_config(struct imv *imv) { char *path = get_config_path(); - if(!path) { + if (!path) { /* no config, no problem - we have defaults */ return true; } @@ -1444,55 +1486,55 @@ void command_pan(struct list *args, const char *argstr, void *data) { (void)argstr; struct imv *imv = data; - if(args->len != 3) { + if (args->len != 3) { return; } long int x = strtol(args->items[1], NULL, 10); long int y = strtol(args->items[2], NULL, 10); - imv_viewport_move(imv->view, x, y, imv->image); + imv_viewport_move(imv->view, x, y, imv->current_image); } void command_select_rel(struct list *args, const char *argstr, void *data) { (void)argstr; struct imv *imv = data; - if(args->len != 2) { + if (args->len != 2) { return; } long int index = strtol(args->items[1], NULL, 10); imv_navigator_select_rel(imv->navigator, index); - imv->slideshow_time_elapsed = 0; + imv->slideshow.elapsed = 0; } void command_select_abs(struct list *args, const char *argstr, void *data) { (void)argstr; struct imv *imv = data; - if(args->len != 2) { + if (args->len != 2) { return; } long int index = strtol(args->items[1], NULL, 10); imv_navigator_select_abs(imv->navigator, index); - imv->slideshow_time_elapsed = 0; + imv->slideshow.elapsed = 0; } void command_zoom(struct list *args, const char *argstr, void *data) { (void)argstr; struct imv *imv = data; - if(args->len == 2) { + if (args->len == 2) { const char *str = args->items[1]; - if(!strcmp(str, "actual")) { - imv_viewport_scale_to_actual(imv->view, imv->image); + if (!strcmp(str, "actual")) { + imv_viewport_scale_to_actual(imv->view, imv->current_image); } else { long int amount = strtol(args->items[1], NULL, 10); - imv_viewport_zoom(imv->view, imv->image, IMV_ZOOM_KEYBOARD, amount); + imv_viewport_zoom(imv->view, imv->current_image, IMV_ZOOM_KEYBOARD, 0, 0, amount); } } } @@ -1513,8 +1555,8 @@ void command_open(struct list *args, const char *argstr, void *data) } wordexp_t word; - if(wordexp(args->items[i], &word, 0) == 0) { - for(size_t j = 0; j < word.we_wordc; ++j) { + if (wordexp(args->items[i], &word, 0) == 0) { + for (size_t j = 0; j < word.we_wordc; ++j) { imv_navigator_add(imv->navigator, word.we_wordv[j], recursive); } wordfree(&word); @@ -1531,7 +1573,7 @@ void command_close(struct list *args, const char *argstr, void *data) imv_navigator_remove(imv->navigator, path); free(path); - imv->slideshow_time_elapsed = 0; + imv->slideshow.elapsed = 0; } void command_fullscreen(struct list *args, const char *argstr, void *data) @@ -1539,7 +1581,17 @@ void command_fullscreen(struct list *args, const char *argstr, void *data) (void)args; (void)argstr; struct imv *imv = data; - imv_viewport_toggle_fullscreen(imv->view); + + + if (glfwGetWindowMonitor(imv->window)) { + glfwSetWindowMonitor(imv->window, NULL, 0, 0, imv->initial_width, imv->initial_height, GLFW_DONT_CARE); + imv->fullscreen = false; + } else { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwSetWindowMonitor(imv->window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); + imv->fullscreen = true; + } } void command_overlay(struct list *args, const char *argstr, void *data) @@ -1564,7 +1616,7 @@ void command_center(struct list *args, const char *argstr, void *data) (void)args; (void)argstr; struct imv *imv = data; - imv_viewport_center(imv->view, imv->image); + imv_viewport_center(imv->view, imv->current_image); } void command_reset(struct list *args, const char *argstr, void *data) @@ -1581,9 +1633,9 @@ void command_next_frame(struct list *args, const char *argstr, void *data) (void)args; (void)argstr; struct imv *imv = data; - if (imv->source && imv->source->load_next_frame) { - async_load_next_frame(imv->source); - imv->next_frame_due = 1; /* Earliest possible non-zero timestamp */ + if (imv->current_source && imv->current_source->load_next_frame) { + async_load_next_frame(imv->current_source); + imv->next_frame.due = 0.0001; /* Earliest possible non-zero timestamp */ } } @@ -1601,20 +1653,20 @@ void command_set_scaling_mode(struct list *args, const char *argstr, void *data) (void)argstr; struct imv *imv = data; - if(args->len != 2) { + if (args->len != 2) { return; } const char *mode = args->items[1]; - if(!strcmp(mode, "next")) { + if (!strcmp(mode, "next")) { imv->scaling_mode++; imv->scaling_mode %= SCALING_MODE_COUNT; - } else if(!strcmp(mode, "none")) { + } else if (!strcmp(mode, "none")) { imv->scaling_mode = SCALING_NONE; - } else if(!strcmp(mode, "shrink")) { + } else if (!strcmp(mode, "shrink")) { imv->scaling_mode = SCALING_DOWN; - } else if(!strcmp(mode, "full")) { + } else if (!strcmp(mode, "full")) { imv->scaling_mode = SCALING_FULL; } else { /* no changes, don't bother to redraw */ @@ -1629,14 +1681,14 @@ void command_set_slideshow_duration(struct list *args, const char *argstr, void { (void)argstr; struct imv *imv = data; - if(args->len == 2) { - long int delta = 1000 * strtol(args->items[1], NULL, 10); + if (args->len == 2) { + long int delta = strtol(args->items[1], NULL, 10); /* Ensure we can't go below 0 */ - if(delta < 0 && (size_t)labs(delta) > imv->slideshow_image_duration) { - imv->slideshow_image_duration = 0; + if (delta < 0 && (size_t)labs(delta) > imv->slideshow.duration) { + imv->slideshow.duration = 0; } else { - imv->slideshow_image_duration += delta; + imv->slideshow.duration += delta; } imv->need_redraw = true; @@ -1657,10 +1709,10 @@ static void update_env_vars(struct imv *imv) snprintf(str, sizeof str, "%zu", imv_navigator_length(imv->navigator)); setenv("imv_file_count", str, 1); - snprintf(str, sizeof str, "%d", imv_image_width(imv->image)); + snprintf(str, sizeof str, "%d", imv_image_width(imv->current_image)); setenv("imv_width", str, 1); - snprintf(str, sizeof str, "%d", imv_image_height(imv->image)); + snprintf(str, sizeof str, "%d", imv_image_height(imv->current_image)); setenv("imv_height", str, 1); { @@ -1670,10 +1722,10 @@ static void update_env_vars(struct imv *imv) setenv("imv_scale", str, 1); } - snprintf(str, sizeof str, "%zu", imv->slideshow_image_duration / 1000); + snprintf(str, sizeof str, "%f", imv->slideshow.duration); setenv("imv_slidshow_duration", str, 1); - snprintf(str, sizeof str, "%zu", imv->slideshow_time_elapsed / 1000); + snprintf(str, sizeof str, "%f", imv->slideshow.elapsed); setenv("imv_slidshow_elapsed", str, 1); } @@ -1683,8 +1735,8 @@ static size_t generate_env_text(struct imv *imv, char *buf, size_t buf_len, cons size_t len = 0; wordexp_t word; - if(wordexp(format, &word, 0) == 0) { - for(size_t i = 0; i < word.we_wordc; ++i) { + if (wordexp(format, &word, 0) == 0) { + for (size_t i = 0; i < word.we_wordc; ++i) { len += snprintf(buf + len, buf_len - len, "%s ", word.we_wordv[i]); } wordfree(&word); @@ -1695,4 +1747,34 @@ static size_t generate_env_text(struct imv *imv, char *buf, size_t buf_len, cons return len; } +static size_t read_from_stdin(void **buffer) +{ + size_t len = 0; + ssize_t r; + size_t step = 4096; /* Arbitrary value of 4 KiB */ + void *p; + + errno = 0; /* clear errno */ + + for (*buffer = NULL; (*buffer = realloc((p = *buffer), len + step)); + len += (size_t)r) { + if ((r = read(STDIN_FILENO, (uint8_t *)*buffer + len, step)) == -1) { + perror(NULL); + break; + } else if (r == 0) { + break; + } + } + + /* realloc(3) leaves old buffer allocated in case of error */ + if (*buffer == NULL && p != NULL) { + int save = errno; + free(p); + errno = save; + len = 0; + } + return len; +} + + /* vim:set ts=2 sts=2 sw=2 et: */ |