aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHarry Jeffery <harry@exec64.co.uk>2017-08-06 19:45:15 +0100
committerHarry Jeffery <harry@exec64.co.uk>2017-08-06 19:45:15 +0100
commit2ffd6edea17c1ec8fdb33d1135e27db0eb080625 (patch)
tree113e09b6386ad4b69ddb00fb8e6e41fe31261320 /src
parentcee36733b5db5dcd0b479663000f889788468c80 (diff)
parent12450f38753699b0e606c3ad542892752da6aca8 (diff)
downloadimv-2ffd6edea17c1ec8fdb33d1135e27db0eb080625.tar.gz
Merge v3 changes into master
Diffstat (limited to 'src')
-rw-r--r--src/commands.c90
-rw-r--r--src/commands.h35
-rw-r--r--src/imv.c872
-rw-r--r--src/imv.h36
-rw-r--r--src/list.c111
-rw-r--r--src/list.h48
-rw-r--r--src/loader.c13
-rw-r--r--src/loader.h8
-rw-r--r--src/main.c692
-rw-r--r--src/navigator.c26
-rw-r--r--src/navigator.h12
-rw-r--r--src/texture.c7
-rw-r--r--src/texture.h8
-rw-r--r--src/util.c3
-rw-r--r--src/viewport.c9
-rw-r--r--src/viewport.h8
16 files changed, 1263 insertions, 715 deletions
diff --git a/src/commands.c b/src/commands.c
new file mode 100644
index 0000000..0f31bfc
--- /dev/null
+++ b/src/commands.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2017 imv authors
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "commands.h"
+#include "list.h"
+
+struct command {
+ char* command;
+ void (*handler)(struct imv_list *args, void *data);
+ char* alias;
+};
+
+struct imv_commands *imv_commands_create(void)
+{
+ struct imv_commands *cmds = malloc(sizeof(struct imv_commands));
+ cmds->command_list = imv_list_create();
+ return cmds;
+}
+
+void imv_commands_free(struct imv_commands *cmds)
+{
+ for(size_t i = 0; i < cmds->command_list->len; ++i) {
+ struct command *cmd = cmds->command_list->items[i];
+ free(cmd->command);
+ if(cmd->alias) {
+ free(cmd->alias);
+ }
+ free(cmd);
+ }
+ imv_list_free(cmds->command_list);
+ free(cmds);
+}
+
+void imv_command_register(struct imv_commands *cmds, const char *command, void (*handler)())
+{
+ struct command *cmd = malloc(sizeof(struct command));
+ cmd->command = strdup(command);
+ cmd->handler = handler;
+ cmd->alias = NULL;
+ imv_list_append(cmds->command_list, cmd);
+}
+
+void imv_command_alias(struct imv_commands *cmds, const char *command, const char *alias)
+{
+ struct command *cmd = malloc(sizeof(struct command));
+ cmd->command = strdup(command);
+ cmd->handler = NULL;
+ cmd->alias = strdup(alias);
+ imv_list_append(cmds->command_list, cmd);
+}
+
+int imv_command_exec(struct imv_commands *cmds, const char *command, void *data)
+{
+ struct imv_list *args = imv_split_string(command, ' ');
+ int ret = 1;
+
+ if(args->len > 0) {
+ for(size_t i = 0; i < cmds->command_list->len; ++i) {
+ struct command *cmd = cmds->command_list->items[i];
+ if(!strcmp(cmd->command, args->items[0])) {
+ if(cmd->handler) {
+ cmd->handler(args, data);
+ ret = 0;
+ } else if(cmd->alias) {
+ ret = imv_command_exec(cmds, cmd->alias, data);
+ }
+ break;
+ }
+ }
+ }
+
+ imv_list_deep_free(args);
+ return ret;
+}
+
+/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/commands.h b/src/commands.h
new file mode 100644
index 0000000..2d106d9
--- /dev/null
+++ b/src/commands.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2017 imv authors
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+struct imv_list;
+
+struct imv_commands {
+ struct imv_list *command_list;
+};
+
+struct imv_commands *imv_commands_create(void);
+void imv_commands_free(struct imv_commands *cmds);
+void imv_command_register(struct imv_commands *cmds, const char *command, void (*handler)());
+void imv_command_alias(struct imv_commands *cmds, const char *command, const char *alias);
+int imv_command_exec(struct imv_commands *cmds, const char *command, void *data);
+
+#endif
+
+/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/imv.c b/src/imv.c
new file mode 100644
index 0000000..368973b
--- /dev/null
+++ b/src/imv.c
@@ -0,0 +1,872 @@
+/* Copyright (c) 2017 imv authors
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "imv.h"
+
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_ttf.h>
+#include <unistd.h>
+
+#include "commands.h"
+#include "list.h"
+#include "loader.h"
+#include "texture.h"
+#include "navigator.h"
+#include "viewport.h"
+#include "util.h"
+
+enum scaling_mode {
+ SCALING_NONE,
+ SCALING_DOWN,
+ SCALING_FULL,
+ SCALING_MODE_COUNT
+};
+
+static const char *scaling_label[] = {
+ "actual size",
+ "shrink to fit",
+ "scale to fit"
+};
+
+enum background_type {
+ BACKGROUND_SOLID,
+ BACKGROUND_CHEQUERED,
+ BACKGROUND_TYPE_COUNT
+};
+
+struct imv {
+ bool quit;
+ bool fullscreen;
+ bool overlay_enabled;
+ bool nearest_neighbour;
+ bool need_redraw;
+ bool need_rescale;
+ bool recursive_load;
+ bool cycle_input;
+ bool list_at_exit;
+ bool paths_from_stdin;
+ enum scaling_mode scaling_mode;
+ enum background_type background_type;
+ struct { unsigned char r, g, b; } background_color;
+ unsigned long slideshow_image_duration;
+ unsigned long slideshow_time_elapsed;
+ char *font_name;
+ struct imv_navigator *navigator;
+ struct imv_loader *loader;
+ struct imv_commands *commands;
+ struct imv_texture *texture;
+ struct imv_viewport *view;
+ void *stdin_image_data;
+ size_t stdin_image_data_len;
+ char *input_buffer;
+ char *starting_path;
+ struct pollfd stdin_fd;
+
+ SDL_Window *window;
+ SDL_Renderer *renderer;
+ TTF_Font *font;
+ SDL_Texture *background_texture;
+ bool sdl_init;
+ bool ttf_init;
+};
+
+void command_quit(struct imv_list *args, void *data);
+void command_pan(struct imv_list *args, void *data);
+void command_select_rel(struct imv_list *args, void *data);
+void command_select_abs(struct imv_list *args, void *data);
+void command_zoom(struct imv_list *args, void *data);
+void command_remove(struct imv_list *args, void *data);
+void command_fullscreen(struct imv_list *args, void *data);
+void command_overlay(struct imv_list *args, void *data);
+
+static bool setup_window(struct imv *imv);
+static void handle_event(struct imv *imv, SDL_Event *event);
+static void render_window(struct imv *imv);
+
+struct imv *imv_create(void)
+{
+ struct imv *imv = malloc(sizeof(struct imv));
+ imv->quit = false;
+ imv->fullscreen = false;
+ imv->overlay_enabled = false;
+ imv->nearest_neighbour = false;
+ imv->need_redraw = true;
+ imv->need_rescale = true;
+ imv->recursive_load = false;
+ imv->scaling_mode = SCALING_FULL;
+ imv->cycle_input = true;
+ imv->list_at_exit = false;
+ imv->paths_from_stdin = false;
+ imv->background_color.r = imv->background_color.g = imv->background_color.b = 0;
+ imv->slideshow_image_duration = 0;
+ imv->slideshow_time_elapsed = 0;
+ imv->font_name = strdup("Monospace:24");
+ imv->navigator = imv_navigator_create();
+ imv->loader = imv_loader_create();
+ imv->commands = imv_commands_create();
+ imv->stdin_image_data = NULL;
+ imv->stdin_image_data_len = 0;
+ imv->input_buffer = NULL;
+ imv->starting_path = NULL;
+ imv->window = NULL;
+ imv->renderer = NULL;
+ imv->font = NULL;
+ imv->background_texture = NULL;
+ imv->sdl_init = false;
+ imv->ttf_init = false;
+
+ imv_command_register(imv->commands, "quit", &command_quit);
+ imv_command_register(imv->commands, "pan", &command_pan);
+ imv_command_register(imv->commands, "select_rel", &command_select_rel);
+ imv_command_register(imv->commands, "select_abs", &command_select_abs);
+ imv_command_register(imv->commands, "zoom", &command_zoom);
+ imv_command_register(imv->commands, "remove", &command_remove);
+ imv_command_register(imv->commands, "fullscreen", &command_fullscreen);
+ imv_command_register(imv->commands, "overlay", &command_overlay);
+
+ imv_command_alias(imv->commands, "q", "quit");
+ imv_command_alias(imv->commands, "next", "select_rel 1");
+ imv_command_alias(imv->commands, "previous", "select_rel -1");
+ imv_command_alias(imv->commands, "n", "select_rel 1");
+ imv_command_alias(imv->commands, "p", "select_rel -1");
+
+ return imv;
+}
+
+void imv_free(struct imv *imv)
+{
+ free(imv->font_name);
+ imv_navigator_free(imv->navigator);
+ imv_loader_free(imv->loader);
+ imv_commands_free(imv->commands);
+ 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_texture) {
+ SDL_DestroyTexture(imv->background_texture);
+ }
+ if(imv->font) {
+ TTF_CloseFont(imv->font);
+ }
+ if(imv->ttf_init) {
+ TTF_Quit();
+ }
+ if(imv->sdl_init) {
+ SDL_Quit();
+ }
+ free(imv);
+}
+
+bool imv_parse_args(struct imv *imv, int argc, char **argv)
+{
+ /* Do not print getopt errors */
+ opterr = 0;
+
+ char *argp, *ep = *argv;
+ int o;
+
+ while((o = getopt(argc, argv, "frasSudxhln:b:e:t:")) != -1) {
+ switch(o) {
+ case 'f': imv->fullscreen = true; break;
+ case 'r': imv->recursive_load = true; break;
+ case 'a': imv->scaling_mode = SCALING_NONE; break;
+ case 's': imv->scaling_mode = SCALING_DOWN; break;
+ case 'S': imv->scaling_mode = SCALING_FULL; break;
+ case 'u': imv->nearest_neighbour = true; break;
+ case 'd': imv->overlay_enabled = true; break;
+ case 'x': imv->cycle_input = false; break;
+ case 'l': imv->list_at_exit = true; break;
+ case 'n': imv->starting_path = optarg; break;
+ case 'e': imv->font_name = strdup(optarg); break;
+ case 'h':
+ fprintf(stdout,
+ "imv %s\n"
+ "See manual for usage information.\n"
+ "\n"
+ "Legal:\n"
+ "This program is free software; you can redistribute it and/or\n"
+ "modify it under the terms of the GNU General Public License\n"
+ "as published by the Free Software Foundation; either version 2\n"
+ "of the License, or (at your option) any later version.\n"
+ "\n"
+ "This software uses the FreeImage open source image library.\n"
+ "See http://freeimage.sourceforge.net for details.\n"
+ "FreeImage is used under the GNU GPLv2.\n"
+ , IMV_VERSION);
+ imv->quit = true;
+ return true;
+ case 'b':
+ if(strcmp("checks", optarg) == 0) {
+ imv->background_type = BACKGROUND_CHEQUERED;
+ } else {
+ imv->background_type = BACKGROUND_SOLID;
+ argp = (*optarg == '#') ? optarg + 1 : optarg;
+ uint32_t n = strtoul(argp, &ep, 16);
+ if(*ep != '\0' || ep - argp != 6 || n > 0xFFFFFF) {
+ fprintf(stderr, "Invalid hex color: '%s'\n", optarg);
+ return false;
+ }
+ imv->background_color.b = n & 0xFF;
+ imv->background_color.g = (n >> 8) & 0xFF;
+ imv->background_color.r = (n >> 16);
+ }
+ break;
+ case 't':
+ imv->slideshow_image_duration = strtoul(optarg, &argp, 10);
+ imv->slideshow_image_duration *= 1000;
+ if (*argp == '.') {
+ long delay = strtoul(++argp, &ep, 10);
+ for (int i = 3 - (ep - argp); 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) {
+ fprintf(stderr, "Wrong slideshow delay '%s'. Aborting.\n", optarg);
+ return false;
+ }
+ break;
+ case '?':
+ fprintf(stderr, "Unknown argument '%c'. Aborting.\n", optopt);
+ return false;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* if no paths are given as args, expect them from stdin */
+ 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) {
+
+ /* Special case: '-' denotes reading image data from stdin */
+ if(!strcmp("-", argv[i])) {
+ if(imv->paths_from_stdin) {
+ fprintf(stderr, "Can't read paths AND image data from stdin. Aborting.\n");
+ return false;
+ } else if(data_from_stdin) {
+ fprintf(stderr, "Can't read image data from stdin twice. Aborting.\n");
+ return false;
+ }
+ data_from_stdin = true;
+
+ imv->stdin_image_data_len = read_from_stdin(&imv->stdin_image_data);
+ }
+
+ imv_add_path(imv, argv[i]);
+ }
+ }
+
+ if(imv->paths_from_stdin) {
+ imv->stdin_fd.fd = STDIN_FILENO;
+ imv->stdin_fd.events = POLLIN;
+ fprintf(stderr, "Reading paths from stdin...");
+
+ char buf[PATH_MAX];
+ char *stdin_ok;
+ while((stdin_ok = fgets(buf, sizeof(buf), stdin)) != NULL) {
+ size_t len = strlen(buf);
+ if(buf[len-1] == '\n') {
+ buf[--len] = 0;
+ }
+ if(len > 0) {
+ imv_add_path(imv, buf);
+ break;
+ }
+ }
+ if(!stdin_ok) {
+ fprintf(stderr, " no input!\n");
+ return false;
+ }
+ fprintf(stderr, "\n");
+ }
+ return true;
+}
+
+static void check_stdin_for_paths(struct imv *imv)
+{
+ /* check stdin to see if we've been given any new paths to load */
+ if(poll(&imv->stdin_fd, 1, 10) != 1 || imv->stdin_fd.revents & (POLLERR|POLLNVAL)) {
+ fprintf(stderr, "error polling stdin");
+ imv->quit = true;
+ return;
+ }
+
+ if(imv->stdin_fd.revents & (POLLIN|POLLHUP)) {
+ char buf[PATH_MAX];
+ if(fgets(buf, sizeof(buf), stdin) == NULL && ferror(stdin)) {
+ clearerr(stdin);
+ return;
+ }
+ if(feof(stdin)) {
+ imv->paths_from_stdin = false;
+ fprintf(stderr, "done with stdin\n");
+ return;
+ }
+
+ size_t len = strlen(buf);
+ if(buf[len-1] == '\n') {
+ buf[--len] = 0;
+ }
+ if(len > 0) {
+ imv_add_path(imv, buf);
+ imv->need_redraw = true;
+ }
+ }
+}
+
+void imv_add_path(struct imv *imv, const char *path)
+{
+ imv_navigator_add(imv->navigator, path, imv->recursive_load);
+}
+
+bool imv_run(struct imv *imv)
+{
+ if(!setup_window(imv))
+ return false;
+
+ imv->quit = false;
+
+ /* cache current image's dimensions */
+ int iw = 0;
+ int ih = 0;
+
+ /* time keeping */
+ unsigned int last_time = SDL_GetTicks();
+ unsigned int current_time;
+
+ while(!imv->quit) {
+
+ SDL_Event e;
+ while(!imv->quit && SDL_PollEvent(&e)) {
+ handle_event(imv, &e);
+ }
+
+ /* if we're quitting, don't bother drawing any more images */
+ if(imv->quit) {
+ break;
+ }
+
+ /* check if an image failed to load, if so, remove it from our image list */
+ char *err_path = imv_loader_get_error(imv->loader);
+ if(err_path) {
+ imv_navigator_remove(imv->navigator, err_path);
+
+ /* special case: the image came from stdin */
+ if(strncmp(err_path, "-", 2) == 0) {
+ if(imv->stdin_image_data) {
+ free(imv->stdin_image_data);
+ imv->stdin_image_data = NULL;
+ imv->stdin_image_data_len = 0;
+ }
+ fprintf(stderr, "Failed to load image from stdin.\n");
+ }
+ free(err_path);
+ }
+
+ /* Check if navigator wrapped around paths lists */
+ if(!imv->cycle_input && imv_navigator_wrapped(imv->navigator)) {
+ break;
+ }
+
+ /* if the user has changed image, start loading the new one */
+ if(imv_navigator_poll_changed(imv->navigator)) {
+ const char *current_path = imv_navigator_selection(imv->navigator);
+ if(!current_path) {
+ if(!imv->paths_from_stdin) {
+ fprintf(stderr, "No input files left. Exiting.\n");
+ imv->quit = true;
+ }
+ continue;
+ }
+
+ char title[1024];
+ snprintf(title, sizeof(title), "imv - [%i/%i] [LOADING] %s [%s]",
+ imv->navigator->cur_path + 1, imv->navigator->num_paths, current_path,
+ scaling_label[imv->scaling_mode]);
+ imv_viewport_set_title(imv->view, title);
+
+ imv_loader_load(imv->loader, current_path,
+ imv->stdin_image_data, imv->stdin_image_data_len);
+ imv->view->playing = true;
+ }
+
+
+ /* check if a new image is available to display */
+ FIBITMAP *bmp;
+ int is_new_image;
+ if(imv_loader_get_image(imv->loader, &bmp, &is_new_image)) {
+ imv_texture_set_image(imv->texture, bmp);
+ iw = FreeImage_GetWidth(bmp);
+ ih = FreeImage_GetHeight(bmp);
+ FreeImage_Unload(bmp);
+ imv->need_redraw = true;
+ imv->need_rescale += is_new_image;
+ }
+
+ if(imv->need_rescale) {
+ int ww, wh;
+ SDL_GetWindowSize(imv->window, &ww, &wh);
+
+ imv->need_rescale = false;
+ if(imv->scaling_mode == SCALING_NONE ||
+ (imv->scaling_mode == SCALING_DOWN && ww > iw && wh > ih)) {
+ imv_viewport_scale_to_actual(imv->view, imv->texture);
+ } else {
+ imv_viewport_scale_to_window(imv->view, imv->texture);
+ }
+ }
+
+ /* tell the loader time has passed (for gifs) */
+ current_time = SDL_GetTicks();
+
+ /* if we're playing an animated gif, tell the loader how much time has
+ * passed */
+ if(imv->view->playing) {
+ unsigned int dt = current_time - last_time;
+ /* We cap the delta-time to 100 ms so that if imv is asleep for several
+ * seconds or more (e.g. suspended), upon waking up it doesn't try to
+ * catch up all the time it missed by playing through the gif quickly. */
+ if(dt > 100) {
+ dt = 100;
+ }
+ imv_loader_time_passed(imv->loader, dt / 1000.0);
+ }
+
+ /* handle slideshow */
+ if(imv->slideshow_image_duration != 0) {
+ unsigned int 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_navigator_select_rel(imv->navigator, 1);
+ imv->slideshow_time_elapsed = 0;
+ }
+ }
+
+ last_time = current_time;
+
+
+ /* check if the viewport needs a redraw */
+ if(imv_viewport_needs_redraw(imv->view)) {
+ imv->need_redraw = true;
+ }
+
+ if(imv->need_redraw) {
+ render_window(imv);
+ SDL_RenderPresent(imv->renderer);
+ }
+
+ if(imv->paths_from_stdin) {
+ /* check stdin for any more paths */
+ check_stdin_for_paths(imv);
+ } else {
+ /* sleep a little bit so we don't waste CPU time */
+ SDL_Delay(10);
+ }
+ }
+
+ return false;
+}
+
+static bool setup_window(struct imv *imv)
+{
+ if(SDL_Init(SDL_INIT_VIDEO) != 0) {
+ fprintf(stderr, "SDL Failed to Init: %s\n", SDL_GetError());
+ return false;
+ }
+ imv->sdl_init = true;
+
+ /* width and height arbitrarily chosen. Perhaps there's a smarter way to
+ * set this */
+ const int width = 1280;
+ const int height = 720;
+
+ imv->window = SDL_CreateWindow(
+ "imv",
+ SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED,
+ width, height,
+ SDL_WINDOW_RESIZABLE);
+
+ if(!imv->window) {
+ fprintf(stderr, "SDL Failed to create window: %s\n", SDL_GetError());
+ 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) {
+ fprintf(stderr, "SDL Failed to create renderer: %s\n", SDL_GetError());
+ return false;
+ }
+
+ /* use the appropriate resampling method */
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,
+ imv->nearest_neighbour ? "0" : "1");
+
+ /* construct a chequered background texture */
+ if(imv->background_type == BACKGROUND_CHEQUERED) {
+ imv->background_texture = create_chequered(imv->renderer);
+ }
+
+ /* 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) {
+ fprintf(stderr, "Error loading font: %s\n", TTF_GetError());
+ return false;
+ }
+
+ imv->texture = imv_texture_create(imv->renderer);
+ imv->view = imv_viewport_create(imv->window);
+
+ /* put us in fullscren mode to begin with if requested */
+ if(imv->fullscreen) {
+ imv_viewport_toggle_fullscreen(imv->view);
+ }
+
+ /* start outside of command mode */
+ SDL_StopTextInput();
+
+ return true;
+}
+
+static void handle_event(struct imv *imv, SDL_Event *event)
+{
+ const int command_buffer_len = 1024;
+ 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) {
+ imv_command_exec(imv->commands, imv->input_buffer, imv);
+ SDL_StopTextInput();
+ 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;
+ }
+
+ switch (event->key.keysym.sym) {
+ case SDLK_SEMICOLON:
+ if(event->key.keysym.mod & KMOD_SHIFT) {
+ SDL_StartTextInput();
+ imv->input_buffer = malloc(command_buffer_len);
+ imv->input_buffer[0] = '\0';
+ imv->need_redraw = true;
+ }
+ break;
+ case SDLK_q:
+ imv->quit = true;
+ imv_command_exec(imv->commands, "quit", imv);
+ break;
+ case SDLK_LEFTBRACKET:
+ case SDLK_LEFT:
+ imv_command_exec(imv->commands, "select_rel -1", imv);
+ break;
+ case SDLK_RIGHTBRACKET:
+ case SDLK_RIGHT:
+ imv_command_exec(imv->commands, "select_rel 1", imv);
+ break;
+ case SDLK_EQUALS:
+ case SDLK_PLUS:
+ case SDLK_i:
+ case SDLK_UP:
+ imv_viewport_zoom(imv->view, imv->texture, IMV_ZOOM_KEYBOARD, 1);
+ break;
+ case SDLK_MINUS:
+ case SDLK_o:
+ case SDLK_DOWN:
+ imv_viewport_zoom(imv->view, imv->texture, IMV_ZOOM_KEYBOARD, -1);
+ break;
+ case SDLK_s:
+ if(!event->key.repeat) {
+ imv->scaling_mode++;
+ imv->scaling_mode %= SCALING_MODE_COUNT;
+ }
+ /* FALLTHROUGH */
+ case SDLK_r:
+ if(!event->key.repeat) {
+ imv->need_rescale = true;
+ imv->need_redraw = true;
+ }
+ break;
+ case SDLK_a:
+ if(!event->key.repeat) {
+ imv_viewport_scale_to_actual(imv->view, imv->texture);
+ }
+ break;
+ case SDLK_c:
+ if(!event->key.repeat) {
+ imv_viewport_center(imv->view, imv->texture);
+ }
+ break;
+ case SDLK_j:
+ imv_command_exec(imv->commands, "pan 0 -50", imv);
+ break;
+ case SDLK_k:
+ imv_command_exec(imv->commands, "pan 0 50", imv);
+ break;
+ case SDLK_h:
+ imv_command_exec(imv->commands, "pan 50 0", imv);
+ break;
+ case SDLK_l:
+ imv_command_exec(imv->commands, "pan -50 0", imv);
+ break;
+ case SDLK_x:
+ if(!event->key.repeat) {
+ imv_command_exec(imv->commands, "remove", imv);
+ }
+ break;
+ case SDLK_f:
+ if(!event->key.repeat) {
+ imv_command_exec(imv->commands, "fullscreen", imv);
+ }
+ break;
+ case SDLK_PERIOD:
+ imv_loader_load_next_frame(imv->loader);
+ break;
+ case SDLK_SPACE:
+ if(!event->key.repeat) {
+ imv_viewport_toggle_playing(imv->view);
+ }
+ break;
+ case SDLK_p:
+ if(!event->key.repeat) {
+ puts(imv_navigator_selection(imv->navigator));
+ }
+ break;
+ case SDLK_d:
+ if(!event->key.repeat) {
+ imv_command_exec(imv->commands, "overlay", imv);
+ }
+ break;
+ case SDLK_t:
+ if(event->key.keysym.mod & (KMOD_SHIFT|KMOD_CAPS)) {
+ if(imv->slideshow_image_duration >= 1000) {
+ imv->slideshow_image_duration -= 1000;
+ }
+ } else {
+ imv->slideshow_image_duration += 1000;
+ }
+ imv->need_redraw = true;
+ break;
+ }
+ break;
+ case SDL_MOUSEWHEEL:
+ imv_viewport_zoom(imv->view, imv->texture, 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);
+ }
+ SDL_ShowCursor(SDL_ENABLE);
+ break;
+ case SDL_WINDOWEVENT:
+ imv_viewport_update(imv->view, imv->texture);
+ break;
+ }
+}
+
+static void render_window(struct imv *imv)
+{
+ char title[1024];
+ int ww, wh;
+ SDL_GetWindowSize(imv->window, &ww, &wh);
+
+ /* update window title */
+ const char *current_path = imv_navigator_selection(imv->navigator);
+ int len = snprintf(title, sizeof(title), "imv - [%i/%i] [%ix%i] [%.2f%%] %s [%s]",
+ imv->navigator->cur_path + 1, imv->navigator->num_paths, imv->texture->width, imv->texture->height,
+ 100.0 * imv->view->scale,
+ current_path, scaling_label[imv->scaling_mode]);
+ if(imv->slideshow_image_duration >= 1000) {
+ len += snprintf(title + len, sizeof(title) - len, "[%lu/%lus]",
+ imv->slideshow_time_elapsed / 1000 + 1, imv->slideshow_image_duration / 1000);
+ }
+ imv_viewport_set_title(imv->view, title);
+
+ /* 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);
+ } else {
+ /* chequered background */
+ int img_w, img_h;
+ SDL_QueryTexture(imv->background_texture, NULL, NULL, &img_w, &img_h);
+ /* tile the texture 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_texture, NULL, &dst_rect);
+ }
+ }
+ }
+
+ /* draw our actual texture */
+ imv_texture_draw(imv->texture, imv->view->x, imv->view->y, imv->view->scale);
+
+ /* 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};
+ imv_printf(imv->renderer, imv->font, 0, 0, &fg, &bg, "%s",
+ title + strlen("imv - "));
+ }
+
+ /* 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);
+ }
+
+ /* redraw complete, unset the flag */
+ imv->need_redraw = false;
+}
+
+void command_quit(struct imv_list *args, void *data)
+{
+ (void)args;
+ struct imv *imv = data;
+ imv->quit = true;
+}
+
+void command_pan(struct imv_list *args, void *data)
+{
+ struct imv *imv = data;
+ 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);
+}
+
+void command_select_rel(struct imv_list *args, void *data)
+{
+ struct imv *imv = data;
+ 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;
+}
+
+void command_select_abs(struct imv_list *args, void *data)
+{
+ (void)args;
+ (void)data;
+}
+
+void command_zoom(struct imv_list *args, void *data)
+{
+ (void)args;
+ (void)data;
+}
+
+void command_remove(struct imv_list *args, void *data)
+{
+ (void)args;
+ struct imv *imv = data;
+ char* path = strdup(imv_navigator_selection(imv->navigator));
+ imv_navigator_remove(imv->navigator, path);
+ free(path);
+
+ imv->slideshow_time_elapsed = 0;
+}
+
+void command_fullscreen(struct imv_list *args, void *data)
+{
+ (void)args;
+ struct imv *imv = data;
+ imv_viewport_toggle_fullscreen(imv->view);
+}
+
+void command_overlay(struct imv_list *args, void *data)
+{
+ (void)args;
+ struct imv *imv = data;
+ imv->overlay_enabled = !imv->overlay_enabled;
+ imv->need_redraw = true;
+}
+
+/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/imv.h b/src/imv.h
new file mode 100644
index 0000000..0945fe2
--- /dev/null
+++ b/src/imv.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2017 imv authors
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef IMV_H
+#define IMV_H
+
+#include <stdbool.h>
+
+struct imv;
+
+struct imv *imv_create(void);
+void imv_free(struct imv *imv);
+
+bool imv_parse_args(struct imv *imv, int argc, char **argv);
+
+void imv_add_path(struct imv *imv, const char *path);
+
+bool imv_run(struct imv *imv);
+
+#endif
+
+/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/list.c b/src/list.c
new file mode 100644
index 0000000..03195e0
--- /dev/null
+++ b/src/list.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2017 imv authors
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "list.h"
+
+struct imv_list *imv_list_create(void)
+{
+ struct imv_list *list = malloc(sizeof(struct imv_list));
+ list->len = 0;
+ list->cap = 64;
+ list->items = malloc(sizeof(void*) * list->cap);
+ return list;
+}
+
+void imv_list_free(struct imv_list *list)
+{
+ free(list->items);
+}
+
+void imv_list_deep_free(struct imv_list *list)
+{
+ for(size_t i = 0; i < list->len; ++i) {
+ free(list->items[i]);
+ }
+ imv_list_free(list);
+}
+
+void imv_list_append(struct imv_list *list, void *item)
+{
+ imv_list_grow(list, list->len + 1);
+ list->items[list->len++] = item;
+}
+
+void imv_list_grow(struct imv_list *list, size_t min_size)
+{
+ if(list->cap >= min_size) {
+ return;
+ }
+
+ while(list->cap < min_size) {
+ list->cap *= 2;
+ }
+
+ list->items = realloc(list->items, sizeof(void*) * list->cap);
+}
+
+void imv_list_remove(struct imv_list *list, size_t index)
+{
+ if(index >= list->len) {
+ return;
+ }
+
+ memmove(&list->items[index], &list->items[index + 1], list->len - index);
+
+ list->len -= 1;
+}
+
+void imv_list_insert(struct imv_list *list, size_t index, void *item)
+{
+ imv_list_grow(list, list->len + 1);
+
+ if(index > list->len) {
+ index = list->len;
+ }
+
+ memmove(&list->items[index + 1], &list->items[index], list->len - index);
+ list->items[index] = item;
+ list->len += 1;
+}
+
+struct imv_list *imv_split_string(const char *string, char delim)
+{
+ struct imv_list *list = imv_list_create();
+
+ const char *base = string;
+
+ while(*base) {
+ while(*base && *base == delim) {
+ ++base;
+ }
+
+ const char *end = base;
+ while(*end && *end != delim) {
+ ++end;
+ }
+
+ if(*base && base != end) {
+ char *item = strndup(base, end - base);
+ imv_list_append(list, item);
+ base = end;
+ }
+ }
+
+ return list;
+}
+
+/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/list.h b/src/list.h
new file mode 100644
index 0000000..a329e6d
--- /dev/null
+++ b/src/list.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2017 imv authors
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef LIST_H
+#define LIST_H
+
+#include <stdlib.h>
+#include <string.h>
+
+struct imv_list {
+ size_t len;
+ size_t cap;
+ void **items;
+};
+
+struct imv_list *imv_list_create(void);
+
+void imv_list_free(struct imv_list *list);
+
+void imv_list_deep_free(struct imv_list *list);
+
+void imv_list_append(struct imv_list *list, void *item);
+
+void imv_list_grow(struct imv_list *list, size_t min_size);
+
+void imv_list_remove(struct imv_list *list, size_t index);
+
+void imv_list_insert(struct imv_list *list, size_t index, void *item);
+
+struct imv_list *imv_split_string(const char *string, char delim);
+
+#endif
+
+/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/loader.c b/src/loader.c
index d70b66d..b21b140 100644
--- a/src/loader.c
+++ b/src/loader.c
@@ -44,18 +44,22 @@ static int is_thread_cancelled(void)
return sigismember(&sigmask, SIGUSR1);
}
-void imv_init_loader(struct imv_loader *ldr)
+struct imv_loader *imv_loader_create(void)
{
+ struct imv_loader *ldr = malloc(sizeof(struct imv_loader));
memset(ldr, 0, sizeof(struct imv_loader));
pthread_mutex_init(&ldr->lock, NULL);
/* ignore this signal in case we accidentally receive it */
block_usr1_signal();
+ return ldr;
}
-void imv_destroy_loader(struct imv_loader *ldr)
+void imv_loader_free(struct imv_loader *ldr)
{
/* wait for any existing bg thread to finish */
- pthread_join(ldr->bg_thread, NULL);
+ if(ldr->bg_thread) {
+ pthread_join(ldr->bg_thread, NULL);
+ }
pthread_mutex_destroy(&ldr->lock);
if(ldr->bmp) {
@@ -70,6 +74,7 @@ void imv_destroy_loader(struct imv_loader *ldr)
if(ldr->path) {
free(ldr->path);
}
+ free(ldr);
}
void imv_loader_load(struct imv_loader *ldr, const char *path,
@@ -93,6 +98,7 @@ void imv_loader_load(struct imv_loader *ldr, const char *path,
ldr->buffer_size = buffer_size;
} else if (ldr->fi_buffer != NULL) {
FreeImage_CloseMemory(ldr->fi_buffer);
+ ldr->fi_buffer = NULL;
}
pthread_create(&ldr->bg_thread, NULL, &bg_new_img, ldr);
pthread_mutex_unlock(&ldr->lock);
@@ -186,6 +192,7 @@ static void *bg_new_img(void *data)
if (from_stdin) {
pthread_mutex_lock(&ldr->lock);
FreeImage_CloseMemory(ldr->fi_buffer);
+ ldr->fi_buffer = NULL;
pthread_mutex_unlock(&ldr->lock);
}
error_occurred(ldr);
diff --git a/src/loader.h b/src/loader.h
index d2418a2..04dc640 100644
--- a/src/loader.h
+++ b/src/loader.h
@@ -44,11 +44,11 @@ struct imv_loader {
double frame_time;
};
-/* Initialises an instance of imv_loader */
-void imv_init_loader(struct imv_loader *img);
+/* Creates an instance of imv_loader */
+struct imv_loader *imv_loader_create(void);
-/* Cleans up all resources owned by a imv_loader instance */
-void imv_destroy_loader(struct imv_loader *img);
+/* Cleans up an imv_loader instance */
+void imv_loader_free(struct imv_loader *ldr);
/* Asynchronously load the given file */
void imv_loader_load(struct imv_loader *ldr, const char *path,
diff --git a/src/main.c b/src/main.c
index 1e3df67..c2eb394 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015 imv authors
+/* Copyright (c) 2017 imv authors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -15,696 +15,26 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include <limits.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <SDL2/SDL.h>
-#include <SDL2/SDL_ttf.h>
-#include <FreeImage.h>
-#include <getopt.h>
-#include <ctype.h>
-#include <poll.h>
-#include <errno.h>
-
-#include "loader.h"
-#include "texture.h"
-#include "navigator.h"
-#include "viewport.h"
-#include "util.h"
-
-enum scaling_mode {
- NONE,
- DOWN,
- FULL
-};
-
-static char *scaling_label[] = {
- "actual size",
- "best fit",
- "perfect fit"
-};
-
-struct {
- int fullscreen;
- int stdin_list;
- int recursive;
- int scaling;
- int nearest_neighbour;
- int solid_bg;
- int list;
- unsigned long delay;
- int cycle;
- unsigned char bg_r;
- unsigned char bg_g;
- unsigned char bg_b;
- int overlay;
- const char *start_at;
- const char *font;
-} g_options = {
- .fullscreen = 0,
- .stdin_list = 0,
- .recursive = 0,
- .scaling = FULL,
- .nearest_neighbour = 0,
- .solid_bg = 1,
- .list = 0,
- .delay = 0,
- .cycle = 1,
- .bg_r = 0,
- .bg_g = 0,
- .bg_b = 0,
- .overlay = 0,
- .start_at = NULL,
- .font = "Monospace:24",
-};
-
-static void print_usage(void)
-{
- fprintf(stdout,
- "imv %s\n"
- "See manual for usage information.\n"
- "\n"
- "Legal:\n"
- "This program is free software; you can redistribute it and/or\n"
- "modify it under the terms of the GNU General Public License\n"
- "as published by the Free Software Foundation; either version 2\n"
- "of the License, or (at your option) any later version.\n"
- "\n"
- "This software uses the FreeImage open source image library.\n"
- "See http://freeimage.sourceforge.net for details.\n"
- "FreeImage is used under the GNU GPLv2.\n"
- , IMV_VERSION);
-}
-
-static void parse_args(int argc, char** argv)
-{
- /* Do not print getopt errors */
- opterr = 0;
-
- char *argp, *ep = *argv;
- int o;
-
- while((o = getopt(argc, argv, "firasSudxhln:b:e:t:")) != -1) {
- switch(o) {
- case 'f': g_options.fullscreen = 1; break;
- case 'i':
- g_options.stdin_list = 1;
- fprintf(stderr, "Warning: '-i' is deprecated. No flag is needed.\n");
- break;
- case 'r': g_options.recursive = 1; break;
- case 'a': g_options.scaling = NONE; break;
- case 's': g_options.scaling = DOWN; break;
- case 'S': g_options.scaling = FULL; break;
- case 'u': g_options.nearest_neighbour = 1; break;
- case 'd': g_options.overlay = 1; break;
- case 'x': g_options.cycle = 0; break;
- case 'h': print_usage(); exit(0); break;
- case 'l': g_options.list = 1; break;
- case 'n':
- g_options.start_at = optarg;
- break;
- case 'b':
- if(strcmp("checks", optarg) == 0) {
- g_options.solid_bg = 0;
- } else {
- g_options.solid_bg = 1;
- argp = (*optarg == '#') ? optarg + 1 : optarg;
- uint32_t n = strtoul(argp, &ep, 16);
- if(*ep != '\0' || ep - argp != 6 || n > 0xFFFFFF) {
- fprintf(stderr, "Invalid hex color: '%s'\n", optarg);
- exit(1);
- }
- g_options.bg_b = n & 0xFF;
- g_options.bg_g = (n >> 8) & 0xFF;
- g_options.bg_r = (n >> 16);
- }
- break;
- case 'e':
- g_options.font = optarg;
- break;
- case 't':
- g_options.delay = strtoul(optarg, &argp, 10);
- g_options.delay *= 1000;
- if (*argp == '.') {
- long delay = strtoul(++argp, &ep, 10);
- for (int i = 3 - (ep - argp); i; i--) {
- delay *= 10;
- }
- if (delay < 1000) {
- g_options.delay += delay;
- } else {
- g_options.delay = ULONG_MAX;
- }
- }
- if (g_options.delay == ULONG_MAX) {
- fprintf(stderr, "Wrong slideshow delay '%s'. Aborting.\n", optarg);
- exit(1);
- }
- break;
- case '?':
- fprintf(stderr, "Unknown argument '%c'. Aborting.\n", optopt);
- exit(1);
- }
- }
-}
+#include "imv.h"
int main(int argc, char** argv)
{
- struct imv_navigator nav;
- imv_navigator_init(&nav);
-
- /* parse any command line options given */
- parse_args(argc, argv);
-
- argc -= optind;
- argv += optind;
+ struct imv *imv = imv_create();
- /* if no names are given, expect them on stdin */
- if(argc == 0) {
- g_options.stdin_list = 1;
+ if(!imv) {
+ return 1;
}
- /* if the user asked to load paths from stdin, set up poll(2)ing and read
- the first path */
- struct pollfd rfds[1];
- if(g_options.stdin_list) {
- rfds[0].fd = STDIN_FILENO;
- rfds[0].events = POLLIN;
- fprintf(stderr, "Reading paths from stdin...");
-
- char buf[PATH_MAX];
- char *stdin_ok;
- while((stdin_ok = fgets(buf, sizeof(buf), stdin)) != NULL) {
- size_t len = strlen(buf);
- if(buf[len-1] == '\n') {
- buf[--len] = 0;
- }
- if(len > 0) {
- if(imv_navigator_add(&nav, buf, g_options.recursive) != 0) {
- imv_navigator_destroy(&nav);
- exit(1);
- }
- break;
- }
- }
- if(!stdin_ok) {
- fprintf(stderr, " no input!\n");
- return -1;
- }
- fprintf(stderr, "\n");
- }
-
- void *stdin_buffer = NULL;
- size_t stdin_buffer_size = 0;
- int stdin_error = 0;
-
- /* handle any image paths given as arguments */
- for(int i = 0; i < argc; ++i) {
- /* special case: '-' is actually an option */
- if(!strcmp("-",argv[i])) {
- if (stdin_buffer) {
- fprintf(stderr, "Can't read from stdin twice\n");
- exit(1);
- }
- stdin_buffer_size = read_from_stdin(&stdin_buffer);
- if (stdin_buffer_size == 0) {
- perror(NULL);
- continue; /* we can't recover from the freed buffer, just ignore it */
- }
- stdin_error = errno;
- errno = 0; /* clear errno */
- }
- /* add the given path to the list to load */
- if(imv_navigator_add(&nav, argv[i], g_options.recursive) != 0) {
- imv_navigator_destroy(&nav);
- exit(1);
- }
- }
-
- /* if we weren't given any paths we have nothing to view. exit */
- if(!imv_navigator_selection(&nav)) {
- fprintf(stderr, "No input files. Exiting.\n");
- exit(1);
- }
-
- /* go to the chosen starting image if possible */
- if(g_options.start_at) {
- int start_index = imv_navigator_find_path(&nav, g_options.start_at);
- if(start_index < 0) {
- start_index = strtol(g_options.start_at,NULL,10) - 1;
- }
- imv_navigator_select_str(&nav, start_index);
- }
-
- /* we've got something to display, so create an SDL window */
- if(SDL_Init(SDL_INIT_VIDEO) != 0) {
- fprintf(stderr, "SDL Failed to Init: %s\n", SDL_GetError());
- exit(1);
- }
-
- /* width and height arbitrarily chosen. Perhaps there's a smarter way to
- * set this */
- const int width = 1280;
- const int height = 720;
-
- SDL_Window *window = SDL_CreateWindow(
- "imv",
- SDL_WINDOWPOS_CENTERED,
- SDL_WINDOWPOS_CENTERED,
- width, height,
- SDL_WINDOW_RESIZABLE);
- if(!window) {
- fprintf(stderr, "SDL Failed to create window: %s\n", SDL_GetError());
- SDL_Quit();
- exit(1);
- }
-
- /* we'll use SDL's built-in renderer, hardware accelerated if possible */
- SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
- if(!renderer) {
- fprintf(stderr, "SDL Failed to create renderer: %s\n", SDL_GetError());
- SDL_DestroyWindow(window);
- SDL_Quit();
- exit(1);
- }
-
- /* use the appropriate resampling method */
- SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,
- g_options.nearest_neighbour ? "0" : "1");
-
- /* construct a chequered background texture */
- SDL_Texture *chequered_tex = NULL;
- if(!g_options.solid_bg) {
- chequered_tex = create_chequered(renderer);
- }
-
- /* set up the required fonts and surfaces for displaying the overlay */
- TTF_Init();
- TTF_Font *font = load_font(g_options.font);
- if(!font) {
- fprintf(stderr, "Error loading font: %s\n", TTF_GetError());
- }
-
- /* create our main classes on the stack*/
- struct imv_loader ldr;
- imv_init_loader(&ldr);
-
- struct imv_texture tex;
- imv_init_texture(&tex, renderer);
-
- struct imv_viewport view;
- imv_init_viewport(&view, window);
-
- /* put us in fullscren mode to begin with if requested */
- if(g_options.fullscreen) {
- imv_viewport_toggle_fullscreen(&view);
+ if(!imv_parse_args(imv, argc, argv)) {
+ imv_free(imv);
+ return 1;
}
- /* help keeping track of time */
- unsigned int last_time = SDL_GetTicks();
- unsigned int current_time;
-
- /* keep file change polling rate under control */
- static uint8_t poll_countdown = UINT8_MAX;
-
- /* do we need to redraw the window? */
- int need_redraw = 1;
- int need_rescale = 0;
-
- /* keep title buffer around for reuse */
- char title[256];
-
- /* used to calculate when to skip to the next image in slideshow mode */
- unsigned long delay_msec = 0;
-
- /* initialize variables holding image dimentions */
- int iw = 0, ih = 0;
-
- int quit = 0;
- while(!quit) {
- /* handle any input/window events sent by SDL */
- SDL_Event e;
- while(!quit && SDL_PollEvent(&e)) {
- switch(e.type) {
- case SDL_QUIT:
- quit = 1;
- break;
- case SDL_KEYDOWN:
- SDL_ShowCursor(SDL_DISABLE);
- switch (e.key.keysym.sym) {
- case SDLK_q:
- quit = 1;
- break;
- case SDLK_LEFTBRACKET:
- case SDLK_LEFT:
- imv_navigator_select_rel(&nav, -1);
- /* reset slideshow delay */
- delay_msec = 0;
- break;
- case SDLK_RIGHTBRACKET:
- case SDLK_RIGHT:
- imv_navigator_select_rel(&nav, 1);
- /* reset slideshow delay */
- delay_msec = 0;
- break;
- case SDLK_EQUALS:
- case SDLK_PLUS:
- case SDLK_i:
- case SDLK_UP:
- imv_viewport_zoom(&view, &tex, IMV_ZOOM_KEYBOARD, 1);
- break;
- case SDLK_MINUS:
- case SDLK_o:
- case SDLK_DOWN:
- imv_viewport_zoom(&view, &tex, IMV_ZOOM_KEYBOARD, -1);
- break;
- case SDLK_s:
- if(!e.key.repeat) {
- if((g_options.scaling += 1) > FULL) {
- g_options.scaling = NONE;
- }
- }
- /* FALLTHROUGH */
- case SDLK_r:
- if(!e.key.repeat) {
- need_rescale = 1;
- need_redraw = 1;
- }
- break;
- case SDLK_a:
- if(!e.key.repeat) {
- imv_viewport_scale_to_actual(&view, &tex);
- }
- break;
- case SDLK_c:
- if(!e.key.repeat) {
- imv_viewport_center(&view, &tex);
- }
- break;
- case SDLK_j:
- imv_viewport_move(&view, 0, -50);
- break;
- case SDLK_k:
- imv_viewport_move(&view, 0, 50);
- break;
- case SDLK_h:
- imv_viewport_move(&view, 50, 0);
- break;
- case SDLK_l:
- imv_viewport_move(&view, -50, 0);
- break;
- case SDLK_x:
- if(!e.key.repeat) {
- char* path = strdup(imv_navigator_selection(&nav));
- imv_navigator_remove(&nav, path);
-
- if (SDL_GetModState() & KMOD_SHIFT) {
- if (remove(path)) {
- fprintf(stderr, "Warning: can't remove %s from disk.\n", path);
- }
- }
-
- free(path);
-
- /* reset slideshow delay */
- delay_msec = 0;
- }
- break;
- case SDLK_f:
- if(!e.key.repeat) {
- imv_viewport_toggle_fullscreen(&view);
- }
- break;
- case SDLK_PERIOD:
- imv_loader_load_next_frame(&ldr);
- break;
- case SDLK_SPACE:
- if(!e.key.repeat) {
- imv_viewport_toggle_playing(&view);
- }
- break;
- case SDLK_p:
- if(!e.key.repeat) {
- puts(imv_navigator_selection(&nav));
- }
- break;
- case SDLK_d:
- if(!e.key.repeat) {
- g_options.overlay = !g_options.overlay;
- need_redraw = 1;
- }
- break;
- case SDLK_t:
- if(e.key.keysym.mod & (KMOD_SHIFT|KMOD_CAPS)) {
- if(g_options.delay >= 1000) {
- g_options.delay -= 1000;
- }
- } else {
- g_options.delay += 1000;
- }
- need_redraw = 1;
- break;
- }
- break;
- case SDL_MOUSEWHEEL:
- imv_viewport_zoom(&view, &tex, IMV_ZOOM_MOUSE, e.wheel.y);
- SDL_ShowCursor(SDL_ENABLE);
- break;
- case SDL_MOUSEMOTION:
- if(e.motion.state & SDL_BUTTON_LMASK) {
- imv_viewport_move(&view, e.motion.xrel, e.motion.yrel);
- }
- SDL_ShowCursor(SDL_ENABLE);
- break;
- case SDL_WINDOWEVENT:
- imv_viewport_update(&view, &tex);
- break;
- }
- }
-
- /* if we're quitting, don't bother drawing any more images */
- if(quit) {
- break;
- }
-
- /* check if an image failed to load, if so, remove it from our image list */
- char *err_path = imv_loader_get_error(&ldr);
- if(err_path) {
- imv_navigator_remove(&nav, err_path);
- if (strncmp(err_path, "-", 2) == 0) {
- free(stdin_buffer);
- stdin_buffer_size = 0;
- if (stdin_error != 0) {
- errno = stdin_error;
- perror("Failed to load image from standard input");
- errno = 0;
- }
- }
- free(err_path);
- }
-
- /* Check if navigator wrapped around paths lists */
- if(!g_options.cycle && imv_navigator_wrapped(&nav)) {
- break;
- }
-
- /* if the user has changed image, start loading the new one */
- if(imv_navigator_poll_changed(&nav, poll_countdown--)) {
- const char *current_path = imv_navigator_selection(&nav);
- if(!current_path) {
- if(g_options.stdin_list) {
- continue;
- }
- fprintf(stderr, "No input files left. Exiting.\n");
- exit(1);
- }
-
- snprintf(title, sizeof(title), "imv - [%i/%i] [LOADING] %s [%s]",
- nav.cur_path + 1, nav.num_paths, current_path,
- scaling_label[g_options.scaling]);
- imv_viewport_set_title(&view, title);
-
- imv_loader_load(&ldr, current_path, stdin_buffer, stdin_buffer_size);
- view.playing = 1;
- }
-
- /* get window height and width */
- int ww, wh;
- SDL_GetWindowSize(window, &ww, &wh);
-
- /* check if a new image is available to display */
- FIBITMAP *bmp;
- int is_new_image;
- if(imv_loader_get_image(&ldr, &bmp, &is_new_image)) {
- imv_texture_set_image(&tex, bmp);
- iw = FreeImage_GetWidth(bmp);
- ih = FreeImage_GetHeight(bmp);
- FreeImage_Unload(bmp);
- need_redraw = 1;
- need_rescale += is_new_image;
- }
-
- if(need_rescale) {
- need_rescale = 0;
- if(g_options.scaling == NONE ||
- (g_options.scaling == DOWN && ww > iw && wh > ih)) {
- imv_viewport_scale_to_actual(&view, &tex);
- } else {
- imv_viewport_scale_to_window(&view, &tex);
- }
- }
-
- current_time = SDL_GetTicks();
+ int ret = imv_run(imv);
- /* if we're playing an animated gif, tell the loader how much time has
- * passed */
- if(view.playing) {
- unsigned int dt = current_time - last_time;
- /* We cap the delta-time to 100 ms so that if imv is asleep for several
- * seconds or more (e.g. suspended), upon waking up it doesn't try to
- * catch up all the time it missed by playing through the gif quickly. */
- if(dt > 100) {
- dt = 100;
- }
- imv_loader_time_passed(&ldr, dt / 1000.0);
- }
+ imv_free(imv);
- /* handle slideshow */
- if(g_options.delay) {
- unsigned int dt = current_time - last_time;
-
- delay_msec += dt;
- need_redraw = 1;
- if(delay_msec >= g_options.delay) {
- imv_navigator_select_rel(&nav, 1);
- delay_msec = 0;
- }
- }
-
- last_time = current_time;
-
- /* check if the viewport needs a redraw */
- if(imv_viewport_needs_redraw(&view)) {
- need_redraw = 1;
- }
-
- /* only redraw when something's changed */
- if(need_redraw) {
- /* update window title */
- int len;
- const char *current_path = imv_navigator_selection(&nav);
- len = snprintf(title, sizeof(title), "imv - [%i/%i] [%ix%i] [%.2f%%] %s [%s]",
- nav.cur_path + 1, nav.num_paths, tex.width, tex.height,
- 100.0 * view.scale,
- current_path, scaling_label[g_options.scaling]);
- if(g_options.delay >= 1000) {
- len += snprintf(title + len, sizeof(title) - len, "[%lu/%lus]",
- delay_msec / 1000 + 1, g_options.delay / 1000);
- }
- imv_viewport_set_title(&view, title);
-
- /* first we draw the background */
- if(g_options.solid_bg) {
- /* solid background */
- SDL_SetRenderDrawColor(renderer,
- g_options.bg_r, g_options.bg_g, g_options.bg_b, 255);
- SDL_RenderClear(renderer);
- } else {
- /* chequered background */
- int img_w, img_h;
- SDL_QueryTexture(chequered_tex, NULL, NULL, &img_w, &img_h);
- /* tile the texture 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(renderer, chequered_tex, NULL, &dst_rect);
- }
- }
- }
-
- /* draw our actual texture */
- imv_texture_draw(&tex, view.x, view.y, view.scale);
-
- /* if the overlay needs to be drawn, draw that too */
- if(g_options.overlay && font) {
- SDL_Color fg = {255,255,255,255};
- SDL_Color bg = {0,0,0,160};
- imv_printf(renderer, font, 0, 0, &fg, &bg, "%s",
- title + strlen("imv - "));
- }
-
- /* redraw complete, unset the flag */
- need_redraw = 0;
-
- /* reset poll countdown timer */
- poll_countdown = UINT8_MAX;
-
- /* tell SDL to show the newly drawn frame */
- SDL_RenderPresent(renderer);
- }
-
- /* sleep a little bit so we don't waste CPU time */
- if(g_options.stdin_list) {
- if(poll(rfds, 1, 10) != 1 || rfds[0].revents & (POLLERR|POLLNVAL)) {
- fprintf(stderr, "error polling stdin");
- return 1;
- }
- if(rfds[0].revents & (POLLIN|POLLHUP)) {
- char buf[PATH_MAX];
- if(fgets(buf, sizeof(buf), stdin) == NULL && ferror(stdin)) {
- clearerr(stdin);
- continue;
- }
- if(feof(stdin)) {
- g_options.stdin_list = 0;
- fprintf(stderr, "done with stdin\n");
- continue;
- }
-
- size_t len = strlen(buf);
- if(buf[len-1] == '\n') {
- buf[--len] = 0;
- }
- if(len > 0) {
- if(imv_navigator_add(&nav, buf, g_options.recursive)) {
- break;
- }
- need_redraw = 1;
- }
- }
- } else {
- SDL_Delay(10);
- }
- }
- while(g_options.list) {
- const char *path = imv_navigator_selection(&nav);
- if(!path) {
- break;
- }
- fprintf(stdout, "%s\n", path);
- imv_navigator_remove(&nav, path);
- }
- /* clean up our resources now that we're exiting */
- imv_destroy_loader(&ldr);
- imv_destroy_texture(&tex);
- imv_navigator_destroy(&nav);
- imv_destroy_viewport(&view);
-
- if(font) {
- TTF_CloseFont(font);
- }
- TTF_Quit();
- if(chequered_tex) {
- SDL_DestroyTexture(chequered_tex);
- }
- SDL_DestroyRenderer(renderer);
- SDL_DestroyWindow(window);
- SDL_Quit();
-
- return 0;
+ return ret;
}
-
/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/navigator.c b/src/navigator.c
index 97772cd..ae7bccd 100644
--- a/src/navigator.c
+++ b/src/navigator.c
@@ -24,13 +24,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <string.h>
#include <stdio.h>
-void imv_navigator_init(struct imv_navigator *nav)
+struct imv_navigator *imv_navigator_create(void)
{
+ struct imv_navigator *nav = malloc(sizeof(struct imv_navigator));
memset(nav, 0, sizeof(struct imv_navigator));
nav->last_move_direction = 1;
+ return nav;
}
-void imv_navigator_destroy(struct imv_navigator *nav)
+void imv_navigator_free(struct imv_navigator *nav)
{
if(nav->paths) {
for(int i = 0; i < nav->num_paths; ++i) {
@@ -45,7 +47,11 @@ void imv_navigator_destroy(struct imv_navigator *nav)
free(nav->mtimes);
}
- memset(nav, 0, sizeof(struct imv_navigator));
+ if(nav->ctimes) {
+ free(nav->ctimes);
+ }
+
+ free(nav);
}
static int add_item(struct imv_navigator *nav, const char *path,
@@ -54,19 +60,23 @@ static int add_item(struct imv_navigator *nav, const char *path,
if(nav->num_paths % BUFFER_SIZE == 0) {
char **new_paths;
time_t *new_mtimes;
+ time_t *new_ctimes;
size_t new_size = nav->num_paths + BUFFER_SIZE;
new_paths = realloc(nav->paths, sizeof(char*) * new_size);
new_mtimes = realloc(nav->mtimes, sizeof(time_t) * new_size);
- if (new_paths == NULL || new_mtimes == NULL) {
+ new_ctimes = realloc(nav->ctimes, sizeof(time_t) * new_size);
+ if (new_paths == NULL || new_mtimes == NULL || new_ctimes == NULL) {
return 1;
}
nav->paths = new_paths;
nav->mtimes = new_mtimes;
+ nav->ctimes = new_ctimes;
}
if((nav->paths[nav->num_paths] = strndup(path, PATH_MAX)) == NULL) {
return 1;
}
nav->mtimes[nav->num_paths] = mtime;
+ nav->ctimes[nav->num_paths] = time(NULL);
nav->num_paths += 1;
if(nav->num_paths == 1) {
nav->changed = 1;
@@ -216,7 +226,7 @@ int imv_navigator_find_path(struct imv_navigator *nav, const char *path)
return -1;
}
-int imv_navigator_poll_changed(struct imv_navigator *nav, const int nopoll)
+int imv_navigator_poll_changed(struct imv_navigator *nav)
{
if(nav->changed) {
nav->changed = 0;
@@ -227,7 +237,11 @@ int imv_navigator_poll_changed(struct imv_navigator *nav, const int nopoll)
return 0;
};
- if(!nopoll) {
+ time_t cur_time = time(NULL);
+ /* limit polling to once per second */
+ if(nav->ctimes[nav->cur_path] < cur_time - 1) {
+ nav->ctimes[nav->cur_path] = cur_time;
+
struct stat file_info;
if(stat(nav->paths[nav->cur_path], &file_info) == -1) {
return 0;
diff --git a/src/navigator.h b/src/navigator.h
index ac3cc59..0d56fa7 100644
--- a/src/navigator.h
+++ b/src/navigator.h
@@ -27,16 +27,18 @@ struct imv_navigator {
int cur_path;
char **paths;
time_t *mtimes;
+ time_t *ctimes;
int last_move_direction;
int changed;
int wrapped;
+ int poll_countdown;
};
-/* Initialises an instance of imv_navigator */
-void imv_navigator_init(struct imv_navigator *nav);
+/* Creates an instance of imv_navigator */
+struct imv_navigator *imv_navigator_create(void);
-/* Cleans up all resources owned by a imv_navigator instance */
-void imv_navigator_destroy(struct imv_navigator *nav);
+/* Cleans up an imv_navigator instance */
+void imv_navigator_free(struct imv_navigator *nav);
/* Adds the given path to the navigator's internal list.
* If a directory is given, all files within that directory are added.
@@ -65,7 +67,7 @@ int imv_navigator_find_path(struct imv_navigator *nav, const char *path);
/* Returns 1 if either the currently selected path or underlying file has
* changed since last called */
-int imv_navigator_poll_changed(struct imv_navigator *nav, const int nopoll);
+int imv_navigator_poll_changed(struct imv_navigator *nav);
/* Check whether navigator wrapped around paths list */
int imv_navigator_wrapped(struct imv_navigator *nav);
diff --git a/src/texture.c b/src/texture.c
index 52ec521..bdfe91f 100644
--- a/src/texture.c
+++ b/src/texture.c
@@ -17,8 +17,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "texture.h"
-void imv_init_texture(struct imv_texture *tex, SDL_Renderer *r)
+struct imv_texture *imv_texture_create(SDL_Renderer *r)
{
+ struct imv_texture *tex = malloc(sizeof(struct imv_texture));
memset(tex, 0, sizeof(struct imv_texture));
tex->renderer = r;
@@ -26,9 +27,10 @@ void imv_init_texture(struct imv_texture *tex, SDL_Renderer *r)
SDL_GetRendererInfo(r, &ri);
tex->chunk_width = ri.max_texture_width != 0 ? ri.max_texture_width : 4096;
tex->chunk_height = ri.max_texture_height != 0 ? ri.max_texture_height : 4096;
+ return tex;
}
-void imv_destroy_texture(struct imv_texture *tex)
+void imv_texture_free(struct imv_texture *tex)
{
if(tex->num_chunks > 0) {
for(int i = 0; i < tex->num_chunks; ++i) {
@@ -39,6 +41,7 @@ void imv_destroy_texture(struct imv_texture *tex)
tex->chunks = NULL;
tex->renderer = NULL;
}
+ free(tex);
}
int imv_texture_set_image(struct imv_texture *tex, FIBITMAP *image)
diff --git a/src/texture.h b/src/texture.h
index 4d2aea9..2931525 100644
--- a/src/texture.h
+++ b/src/texture.h
@@ -36,11 +36,11 @@ struct imv_texture {
};
-/* Initialises an instance of imv_texture */
-void imv_init_texture(struct imv_texture *tex, SDL_Renderer *r);
+/* Creates an instance of imv_texture */
+struct imv_texture *imv_texture_create(SDL_Renderer *r);
-/* Cleans up all resources owned by a imv_texture instance */
-void imv_destroy_texture(struct imv_texture *tex);
+/* Cleans up an imv_texture instance */
+void imv_texture_free(struct imv_texture *tex);
/* Updates the texture to contain the data in the image parameter */
int imv_texture_set_image(struct imv_texture *tex, FIBITMAP *image);
diff --git a/src/util.c b/src/util.c
index 75d6910..71d4c14 100644
--- a/src/util.c
+++ b/src/util.c
@@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
size_t read_from_stdin(void **buffer) {
size_t len = 0;
ssize_t r;
- size_t step = 1024; /* Arbitrary value of 1 KiB */
+ size_t step = 4096; /* Arbitrary value of 4 KiB */
void *p;
errno = 0; /* clear errno */
@@ -153,5 +153,4 @@ void imv_printf(SDL_Renderer *renderer, TTF_Font *font, int x, int y,
va_end(args);
}
-
/* vim:set ts=2 sts=2 sw=2 et: */
diff --git a/src/viewport.c b/src/viewport.c
index e706643..d096a45 100644
--- a/src/viewport.c
+++ b/src/viewport.c
@@ -17,19 +17,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "viewport.h"
-void imv_init_viewport(struct imv_viewport *view, SDL_Window *window)
+struct imv_viewport *imv_viewport_create(SDL_Window *window)
{
+ struct imv_viewport *view = malloc(sizeof(struct imv_viewport));
view->window = window;
view->scale = 1;
view->x = view->y = view->fullscreen = view->redraw = 0;
view->playing = 1;
view->locked = 0;
+ return view;
}
-void imv_destroy_viewport(struct imv_viewport *view)
+void imv_viewport_free(struct imv_viewport *view)
{
- view->window = NULL;
- return;
+ free(view);
}
void imv_viewport_toggle_fullscreen(struct imv_viewport *view)
diff --git a/src/viewport.h b/src/viewport.h
index 0f19b85..d1803d4 100644
--- a/src/viewport.h
+++ b/src/viewport.h
@@ -37,11 +37,11 @@ enum imv_zoom_source {
IMV_ZOOM_KEYBOARD
};
-/* Initialises an instance of imv_viewport */
-void imv_init_viewport(struct imv_viewport *view, SDL_Window *window);
+/* Creates an instance of imv_viewport */
+struct imv_viewport *imv_viewport_create(SDL_Window *window);
-/* Cleans up all resources owned by a imv_viewport instance */
-void imv_destroy_viewport(struct imv_viewport *view);
+/* Cleans up an imv_viewport instance */
+void imv_viewport_free(struct imv_viewport *view);
/* Toggle their viewport's fullscreen mode. Triggers a redraw */
void imv_viewport_toggle_fullscreen(struct imv_viewport *view);