diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2efeec3 --- /dev/null +++ b/src/main.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include <stdio.h> +#include <stddef.h> +#include <SDL2/SDL.h> +#include <FreeImage.h> + +#include "image.h" +#include "texture.h" +#include "navigator.h" +#include "viewport.h" + +struct { + int autoscale; + int fullscreen; + int stdin; + int center; + int recursive; +} g_options = {0,0,0,0,0}; + +void print_usage(const char* name) +{ + fprintf(stdout, + "Usage: %s [-ifsh] [images...]\n" + "\n" + "Flags:\n" + " -i: Read paths from stdin. One path per line.\n" + " -r: Recursively search input paths.\n" + " -f: Start in fullscreen mode\n" + " -s: Auto scale images to fit window\n" + " -c: Center images in the window\n" + " -h: Print this help\n" + "\n" + "Mouse:\n" + " Click+Drag to Pan\n" + " MouseWheel to Zoom\n" + "\n" + "Hotkeys:\n" + " 'q': Quit\n" + " '[',LArrow: Previous image\n" + " ']',RArrow: Next image\n" + " 'i','+': Zoom in\n" + " 'o','=': Zoom out\n" + " 'h': Pan left\n" + " 'j': Pan down\n" + " 'k': Pan up\n" + " 'l': Pan right\n" + " 'r': Reset view\n" + " 'c': Center view\n" + " 's': Scale image to fit window\n" + " 'x': Close current image\n" + " 'f': Toggle fullscreen\n" + " ' ': Toggle gif playback\n" + " '.': Step a frame of gif playback\n" + ,name); +} + +void parse_arg(const char* name, const char* arg) +{ + for(const char *o = arg; *o != 0; ++o) { + switch(*o) { + case 'f': g_options.fullscreen = 1; break; + case 's': g_options.autoscale = 1; break; + case 'c': g_options.center = 1; break; + case 'i': g_options.stdin = 1; break; + case 'r': g_options.recursive = 1; break; + case 'h': print_usage(name); exit(0); break; + default: + fprintf(stderr, "Unknown argument '%c'. Aborting.\n", *o); + exit(1); + } + } +} + +int main(int argc, char** argv) +{ + if(argc < 2) { + print_usage(argv[0]); + exit(1); + } + + struct imv_navigator nav; + imv_init_navigator(&nav); + + for(int i = 1; i < argc; ++i) { + if(argv[i][0] == '-') { + parse_arg(argv[0], &argv[i][1]); + } else { + if(g_options.recursive) { + imv_navigator_add_path_recursive(&nav, argv[i]); + } else { + imv_navigator_add_path(&nav, argv[i]); + } + } + } + + if(g_options.stdin) { + char buf[512]; + while(fgets(buf, sizeof(buf), stdin)) { + size_t len = strlen(buf); + if(buf[len-1] == '\n') { + buf[--len] = 0; + } + if(len > 0) { + if(g_options.recursive) { + imv_navigator_add_path_recursive(&nav, buf); + } else { + imv_navigator_add_path(&nav, buf); + } + } + } + } + + if(!imv_navigator_get_current_path(&nav)) { + fprintf(stderr, "No input files. Exiting.\n"); + exit(1); + } + + if(SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "SDL Failed to Init: %s\n", SDL_GetError()); + exit(1); + } + + 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); + + SDL_Renderer *renderer = + SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + /* Use linear sampling for scaling */ + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + + struct imv_image img; + imv_init_image(&img); + + struct imv_texture tex; + imv_init_texture(&tex, renderer); + + struct imv_viewport view; + imv_init_viewport(&view, window); + + /* Put us in fullscren by default if requested */ + if(g_options.fullscreen) { + imv_viewport_toggle_fullscreen(&view); + } + + double last_time = SDL_GetTicks() / 1000.0; + + int quit = 0; + while(!quit) { + SDL_Event e; + while(!quit && SDL_PollEvent(&e)) { + switch(e.type) { + case SDL_QUIT: + quit = 1; + break; + case SDL_KEYDOWN: + switch (e.key.keysym.sym) { + case SDLK_q: quit = 1; break; + case SDLK_LEFTBRACKET: + case SDLK_LEFT: imv_navigator_prev_path(&nav); break; + case SDLK_RIGHTBRACKET: + case SDLK_RIGHT: imv_navigator_next_path(&nav); break; + case SDLK_EQUALS: + case SDLK_i: + case SDLK_UP: imv_viewport_zoom(&view, 1); break; + case SDLK_MINUS: + case SDLK_o: + case SDLK_DOWN: imv_viewport_zoom(&view, -1); break; + case SDLK_r: imv_viewport_reset(&view); break; + case SDLK_c: imv_viewport_center(&view, &img); 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: imv_navigator_remove_current_path(&nav); break; + case SDLK_f: imv_viewport_toggle_fullscreen(&view); break; + case SDLK_PERIOD: imv_image_load_next_frame(&img); break; + case SDLK_SPACE: imv_viewport_toggle_playing(&view, &img);break; + case SDLK_s: imv_viewport_scale_to_window(&view, &img);break; + } + break; + case SDL_MOUSEWHEEL: + imv_viewport_zoom(&view, e.wheel.y); + break; + case SDL_MOUSEMOTION: + if(e.motion.state & SDL_BUTTON_LMASK) { + imv_viewport_move(&view, e.motion.xrel, e.motion.yrel); + } + break; + case SDL_WINDOWEVENT: + imv_viewport_set_redraw(&view); + break; + } + } + + if(quit) { + break; + } + + while(imv_navigator_has_changed(&nav)) { + const char* current_path = imv_navigator_get_current_path(&nav); + char title[256]; + snprintf(&title[0], sizeof(title), "imv - [%i/%i] [LOADING] %s", + nav.cur_path + 1, nav.num_paths, current_path); + imv_viewport_set_title(&view, title); + + if(!current_path) { + fprintf(stderr, "No input files left. Exiting.\n"); + exit(1); + } + + if(imv_image_load(&img, current_path) != 0) { + imv_navigator_remove_current_path(&nav); + } else { + snprintf(&title[0], sizeof(title), "imv - [%i/%i] [%ix%i] %s", + nav.cur_path + 1, nav.num_paths, + img.width, img.height, current_path); + imv_viewport_set_title(&view, title); + imv_viewport_reset(&view); + } + /* Autoscale if requested */ + if(g_options.autoscale) { + imv_viewport_scale_to_window(&view, &img); + } + if(g_options.center) { + imv_viewport_center(&view, &img); + } + } + + if(view.playing) { + double cur_time = SDL_GetTicks() / 1000.0; + double dt = cur_time - last_time; + imv_image_play(&img, dt); + } + + if(imv_image_has_changed(&img)) { + imv_texture_set_image(&tex, img.cur_bmp); + imv_viewport_set_redraw(&view); + } + + if(view.redraw) { + imv_texture_draw(&tex, view.x, view.y, view.scale); + view.redraw = 0; + } + last_time = SDL_GetTicks() / 1000.0; + SDL_Delay(10); + } + + imv_destroy_image(&img); + imv_destroy_texture(&tex); + imv_destroy_navigator(&nav); + imv_destroy_viewport(&view); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} |