div

X11 image viewer
git clone https://git.porkepik.fr/beh
Log | Files | Refs | README | LICENSE

commit 0e65889114842403d2ca82fd5f2bfb8689fb2b96
parent b403ccd8deb3bafe584bf39eb288fe7077594c21
Author: Thomas Philippe <dev@porkepik.fr>
Date:   Sun, 19 Jul 2020 13:39:45 +0200

change project name

Diffstat:
MMakefile | 4++--
MREADME | 4++--
Dbeh.c | 426-------------------------------------------------------------------------------
Adiv.c | 425+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 429 insertions(+), 430 deletions(-)

diff --git a/Makefile b/Makefile @@ -3,8 +3,8 @@ PREFIX = /usr/local CFLAGS = -O2 -std=c99 -Wall -Wextra -I/usr/X11R6/include LDFLAGS = -L/usr/X11R6/lib LDLIBS = -lX11 -lm -SRC = beh.c -BIN = beh +SRC = div.c +BIN = div ifdef DEBUG CFLAGS += -g -Og -fsanitize=address diff --git a/README b/README @@ -1,7 +1,7 @@ -beh - X11 image viewer +div - dumb image viewer =========================== -beh is a small image viewer in development. +div is a small X11 image viewer. Dependencies ------------ diff --git a/beh.c b/beh.c @@ -1,426 +0,0 @@ -/* - TODO - - handle archives - - set window title - - display filename, res, file size -*/ - -#include <dirent.h> -#include <err.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> - -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#include <X11/XKBlib.h> - -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -#define STB_IMAGE_RESIZE_IMPLEMENTATION -#include "stb_image_resize.h" - -#define RGBA 4 - - -enum zoom { - NO_ZOOM, - FILL, - MAX -}; - -struct app { - Window win; - int width; - int height; - char *bg_color; -}; - -struct image { - int filepath; - int width; - int height; - int dpy_width; - int dpy_height; - unsigned char *data; - unsigned char *dpy_data; - int src_x; - int src_y; - int dest_x; - int dest_y; - enum zoom zoom_mode; -}; - - -static Display *dpy; -static struct app app; -static char **filepaths; -static int nimgs; -static struct image img; - - -static void -usage(void) -{ - printf("Usage: beh [--bg-color \"#ffffff\"] [FILES]\n"); - exit(0); -} - -static void -check_file(char *path) -{ - struct stat path_stat; - stat(path, &path_stat); - - if (S_ISREG(path_stat.st_mode)) { - filepaths = realloc(filepaths, sizeof(char *) * nimgs+1); - filepaths[nimgs++] = path; - } else if (S_ISDIR(path_stat.st_mode)) { - struct dirent *dir; - DIR *d; - if (!(d = opendir(path))) { - printf("Could not open directory: %s\n", path); - return; - } - while ((dir = readdir(d))) { - char *filepath = malloc(strlen(path) + - strlen(dir->d_name) + 2); - strcpy(filepath, path); - strcat(filepath, "/"); - strcat(filepath, dir->d_name); - stat(filepath, &path_stat); - if (!S_ISREG(path_stat.st_mode)) - continue; - filepaths = realloc(filepaths, sizeof(char *)*nimgs+1); - filepaths[nimgs++] = filepath; - } - closedir(d); - } -} - - -static void -check_args(int argc, char **argv) -{ - app.bg_color = NULL; - - for (int i = 1; i <= argc - 1; i++) { - if (strcmp(argv[i], "--bg-color") == 0 && i+1 != argc) - app.bg_color = argv[++i]; - else if (strcmp(argv[i], "-h") == 0) - usage(); - else - check_file(argv[i]); - } -} - -static void -rgb_to_bgr(void) -{ - unsigned char red, blue; - - for (long i = 0; i < img.width * img.height * RGBA; i += RGBA) { - red = img.data[i+2]; - blue = img.data[i]; - img.data[i] = red; - img.data[i+2] = blue; - } -} - -static int -init_img(char *filepath) -{ - img.data = stbi_load(filepath, &img.width, &img.height, NULL, RGBA); - if (img.data == NULL) { - printf("Could not open %s\n", filepath); - return 1; - } - printf("Opened: %s\n", filepath); - rgb_to_bgr(); - - img.dpy_data = img.data; - img.dpy_width = img.width; - img.dpy_height = img.height; - - return 0; -} - -static void -free_img(void) -{ - if (img.dpy_data != img.data) - free(img.dpy_data); - stbi_image_free(img.data); -} - -static unsigned char * -resize(int new_w, int new_h) -{ - unsigned char *new_data = malloc(new_h * new_w * RGBA); - stbir_resize_uint8(img.data, img.width, img.height, 0, - new_data, new_w, new_h, 0, RGBA); - - return new_data; -} - -static void -set_zoom(enum zoom zoom_mode) -{ - int scaled_w, scaled_h, new_w, new_h, tmp; - - scaled_w = (float)app.height * ((float)img.width / (float)img.height); - scaled_h = (float)app.width / ((float)img.width / (float)img.height); - - if ((zoom_mode == FILL && scaled_w < app.width) || - (zoom_mode == MAX && scaled_h <= app.height)) { - new_w = app.width; - new_h = scaled_h; - } else { - new_w = scaled_w; - new_h = app.height; - } - - if (img.dpy_data != img.data) - free(img.dpy_data); - if (zoom_mode == FILL || zoom_mode == MAX) { - img.dpy_data = resize(new_w, new_h); - img.dpy_width = new_w; - img.dpy_height = new_h; - } else if (zoom_mode == NO_ZOOM) { - img.dpy_data = img.data; - img.dpy_width = img.width; - img.dpy_height = img.height; - } - - if (zoom_mode == FILL) { - img.src_x = (img.dpy_width - app.width) / 2; - img.src_y = (img.dpy_height - app.height) / 2; - img.dest_x = 0; - img.dest_y = 0; - } else if (zoom_mode == MAX) { - img.dest_x = (app.width - img.dpy_width) / 2; - img.dest_y = (app.height - img.dpy_height) / 2; - img.src_x = 0; - img.src_y = 0; - } else if (zoom_mode == NO_ZOOM) { - tmp = (img.dpy_width - app.width) / 2; - img.src_x = tmp < 0 ? 0 : tmp; - tmp = (img.dpy_height - app.height) / 2; - img.src_y = tmp < 0 ? 0 : tmp; - tmp = (app.width - img.dpy_width) / 2; - img.dest_x = tmp < 0 ? 0 : tmp; - tmp = (app.height - img.dpy_height) / 2; - img.dest_y = tmp < 0 ? 0 : tmp; - } -} - -static Pixmap -create_pixmap(void) -{ - XColor color; - GC gc; - XGCValues gcval; - - Pixmap pixmap = XCreatePixmap(dpy, app.win, app.width, app.height, - DefaultDepth(dpy, 0)); - Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy)); - - if (app.bg_color == NULL) - XAllocNamedColor(dpy, cmap, "black", &color, &color); - else - XAllocNamedColor(dpy, cmap, app.bg_color, &color, &color); - gcval.foreground = color.pixel; - gc = XCreateGC(dpy, app.win, GCForeground, &gcval); - XFillRectangle(dpy, pixmap, gc, 0, 0, app.width, app.height); - - XFreeGC(dpy, gc); - - return pixmap; -} - -static void -apply_image(Pixmap pixmap) -{ - XImage *ximg = XCreateImage(dpy, DefaultVisual(dpy, 0), 24, ZPixmap, 0, - (char *)img.dpy_data, img.dpy_width, img.dpy_height, 8, 0); - - XSetWindowBackgroundPixmap(dpy, app.win, pixmap); - XPutImage(dpy, pixmap, DefaultGC(dpy, 0), ximg, img.src_x, img.src_y, - img.dest_x, img.dest_y, img.dpy_width, img.dpy_height); - XClearWindow(dpy, app.win); - - XFree(ximg); -} - -static void -event_loop() -{ - Pixmap pixmap; - XEvent ev; - KeySym keysym; - XConfigureEvent xce; - int tmp_x, tmp_y, left_mouse_hold, hold_x, hold_y; - int imgid, status; - char gotobuf[10]; - - memset(gotobuf, 0, 10); - left_mouse_hold = imgid = 0; - - set_zoom(NO_ZOOM); - pixmap = create_pixmap(); - apply_image(pixmap); - - while (1) { - XNextEvent(dpy, &ev); - switch (ev.type) { - case ConfigureNotify: - xce = ev.xconfigure; - if (xce.width != app.width || - xce.height != app.height) { - app.width = xce.width; - app.height = xce.height; - set_zoom(NO_ZOOM); - pixmap = create_pixmap(); - apply_image(pixmap); - } - break; - case KeyPress: - keysym = XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0, - ev.xkey.state & ShiftMask ? 1 : 0); - if (keysym == XK_q || keysym == XK_Escape) { - goto quit; - } else if (keysym >= XK_0 && keysym <= XK_9) { - if (gotobuf[9] == '\0') { - char tmp[2]; - sprintf(tmp, "%lu", keysym - 48); - strcat(gotobuf, tmp); - } - } else if (keysym == XK_g) { - int newid = atoi(gotobuf) - 1; - memset(gotobuf, 0, 10); - if (newid < 0 || newid > nimgs - 1) - break; - free_img(); - if (init_img(filepaths[newid]) != 0) - init_img(filepaths[imgid]); - imgid = newid; - set_zoom(NO_ZOOM); - } else if (keysym == XK_m) { - set_zoom(MAX); - } else if (keysym == XK_l) { - set_zoom(FILL); - } else if (keysym == XK_o) { - set_zoom(NO_ZOOM); - } else if (keysym == XK_p) { - if (imgid == 0) - break; - free_img(); - do { - imgid--; - status = init_img(filepaths[imgid]); - set_zoom(NO_ZOOM); - } while (status != 0 && imgid > 0); - if (status != 0) - goto quit; - } else if (keysym == XK_n) { - if (imgid == nimgs - 1) - break; - free_img(); - do { - imgid++; - status = init_img(filepaths[imgid]); - set_zoom(NO_ZOOM); - } while (status != 0 && imgid < nimgs - 1); - if (status != 0) - goto quit; - } else { - break; - } - pixmap = create_pixmap(); - apply_image(pixmap); - break; - case ButtonPress: - if (ev.xbutton.button == 1) { - left_mouse_hold = 1; - hold_x = ev.xmotion.x; - hold_y = ev.xmotion.y; - } - break; - case ButtonRelease: - if (ev.xbutton.button == 1) - left_mouse_hold = 0; - case MotionNotify: - if (!left_mouse_hold) - break; - tmp_x = img.src_x - ev.xmotion.x + hold_x; - tmp_y = img.src_y - ev.xmotion.y + hold_y; - - if (img.dpy_width > app.width) { - if (tmp_x < 0) - tmp_x = 0; - if (app.width + tmp_x > img.dpy_width) - tmp_x = img.dpy_width - app.width; - img.src_x = tmp_x; - } - if (img.dpy_height > app.height) { - if (tmp_y < 0) - tmp_y = 0; - if (app.height + tmp_y > img.dpy_height) - tmp_y = img.dpy_height - app.height; - - img.src_y = tmp_y; - } - if (img.src_x == tmp_x || img.src_y == tmp_y) - apply_image(pixmap); - - hold_x = ev.xmotion.x; - hold_y = ev.xmotion.y; - break; - } - } -quit: - XFreePixmap(dpy, pixmap); -} - -int -main(int argc, char **argv) -{ - int screen, status, i; - - dpy = XOpenDisplay(NULL); - if (dpy == NULL) - errx(1, "Failed opening DISPLAY."); - - if (argc < 2) - usage(); - check_args(argc, argv); - - for (i = 0; i < nimgs - 1; i++) { - status = init_img(filepaths[i]); - if (status == 0) - break; - } - if (status != 0) - return 0; - app.width = img.width; - app.height = img.height; - - screen = DefaultScreen(dpy); - app.win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 0, 0, - app.width, app.height, 0, 0, WhitePixel(dpy, screen)); - XSelectInput(dpy, app.win, ExposureMask | KeyPressMask | - ButtonPressMask | StructureNotifyMask | PointerMotionMask | - ButtonReleaseMask); - XMapRaised(dpy, app.win); - - event_loop(); - - XDestroyWindow(dpy, app.win); - XCloseDisplay(dpy); - free_img(); - free(filepaths); - - return 0; -} diff --git a/div.c b/div.c @@ -0,0 +1,425 @@ +/* + TODO + - set window title + - display filename, res, file size +*/ + +#include <dirent.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/XKBlib.h> + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "stb_image_resize.h" + +#define RGBA 4 + + +enum zoom { + NO_ZOOM, + FILL, + MAX +}; + +struct app { + Window win; + int width; + int height; + char *bg_color; +}; + +struct image { + int filepath; + int width; + int height; + int dpy_width; + int dpy_height; + unsigned char *data; + unsigned char *dpy_data; + int src_x; + int src_y; + int dest_x; + int dest_y; + enum zoom zoom_mode; +}; + + +static Display *dpy; +static struct app app; +static char **filepaths; +static int nimgs; +static struct image img; + + +static void +usage(void) +{ + printf("Usage: div [--bg-color \"#ffffff\"] [FILES]\n"); + exit(0); +} + +static void +check_file(char *path) +{ + struct stat path_stat; + stat(path, &path_stat); + + if (S_ISREG(path_stat.st_mode)) { + filepaths = realloc(filepaths, sizeof(char *) * nimgs+1); + filepaths[nimgs++] = path; + } else if (S_ISDIR(path_stat.st_mode)) { + struct dirent *dir; + DIR *d; + if (!(d = opendir(path))) { + printf("Could not open directory: %s\n", path); + return; + } + while ((dir = readdir(d))) { + char *filepath = malloc(strlen(path) + + strlen(dir->d_name) + 2); + strcpy(filepath, path); + strcat(filepath, "/"); + strcat(filepath, dir->d_name); + stat(filepath, &path_stat); + if (!S_ISREG(path_stat.st_mode)) + continue; + filepaths = realloc(filepaths, sizeof(char *)*nimgs+1); + filepaths[nimgs++] = filepath; + } + closedir(d); + } +} + + +static void +check_args(int argc, char **argv) +{ + app.bg_color = NULL; + + for (int i = 1; i <= argc - 1; i++) { + if (strcmp(argv[i], "--bg-color") == 0 && i+1 != argc) + app.bg_color = argv[++i]; + else if (strcmp(argv[i], "-h") == 0) + usage(); + else + check_file(argv[i]); + } +} + +static void +rgb_to_bgr(void) +{ + unsigned char red, blue; + + for (long i = 0; i < img.width * img.height * RGBA; i += RGBA) { + red = img.data[i+2]; + blue = img.data[i]; + img.data[i] = red; + img.data[i+2] = blue; + } +} + +static int +init_img(char *filepath) +{ + img.data = stbi_load(filepath, &img.width, &img.height, NULL, RGBA); + if (img.data == NULL) { + printf("Could not open %s\n", filepath); + return 1; + } + printf("Opened: %s\n", filepath); + rgb_to_bgr(); + + img.dpy_data = img.data; + img.dpy_width = img.width; + img.dpy_height = img.height; + + return 0; +} + +static void +free_img(void) +{ + if (img.dpy_data != img.data) + free(img.dpy_data); + stbi_image_free(img.data); +} + +static unsigned char * +resize(int new_w, int new_h) +{ + unsigned char *new_data = malloc(new_h * new_w * RGBA); + stbir_resize_uint8(img.data, img.width, img.height, 0, + new_data, new_w, new_h, 0, RGBA); + + return new_data; +} + +static void +set_zoom(enum zoom zoom_mode) +{ + int scaled_w, scaled_h, new_w, new_h, tmp; + + scaled_w = (float)app.height * ((float)img.width / (float)img.height); + scaled_h = (float)app.width / ((float)img.width / (float)img.height); + + if ((zoom_mode == FILL && scaled_w < app.width) || + (zoom_mode == MAX && scaled_h <= app.height)) { + new_w = app.width; + new_h = scaled_h; + } else { + new_w = scaled_w; + new_h = app.height; + } + + if (img.dpy_data != img.data) + free(img.dpy_data); + if (zoom_mode == FILL || zoom_mode == MAX) { + img.dpy_data = resize(new_w, new_h); + img.dpy_width = new_w; + img.dpy_height = new_h; + } else if (zoom_mode == NO_ZOOM) { + img.dpy_data = img.data; + img.dpy_width = img.width; + img.dpy_height = img.height; + } + + if (zoom_mode == FILL) { + img.src_x = (img.dpy_width - app.width) / 2; + img.src_y = (img.dpy_height - app.height) / 2; + img.dest_x = 0; + img.dest_y = 0; + } else if (zoom_mode == MAX) { + img.dest_x = (app.width - img.dpy_width) / 2; + img.dest_y = (app.height - img.dpy_height) / 2; + img.src_x = 0; + img.src_y = 0; + } else if (zoom_mode == NO_ZOOM) { + tmp = (img.dpy_width - app.width) / 2; + img.src_x = tmp < 0 ? 0 : tmp; + tmp = (img.dpy_height - app.height) / 2; + img.src_y = tmp < 0 ? 0 : tmp; + tmp = (app.width - img.dpy_width) / 2; + img.dest_x = tmp < 0 ? 0 : tmp; + tmp = (app.height - img.dpy_height) / 2; + img.dest_y = tmp < 0 ? 0 : tmp; + } +} + +static Pixmap +create_pixmap(void) +{ + XColor color; + GC gc; + XGCValues gcval; + + Pixmap pixmap = XCreatePixmap(dpy, app.win, app.width, app.height, + DefaultDepth(dpy, 0)); + Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy)); + + if (app.bg_color == NULL) + XAllocNamedColor(dpy, cmap, "black", &color, &color); + else + XAllocNamedColor(dpy, cmap, app.bg_color, &color, &color); + gcval.foreground = color.pixel; + gc = XCreateGC(dpy, app.win, GCForeground, &gcval); + XFillRectangle(dpy, pixmap, gc, 0, 0, app.width, app.height); + + XFreeGC(dpy, gc); + + return pixmap; +} + +static void +apply_image(Pixmap pixmap) +{ + XImage *ximg = XCreateImage(dpy, DefaultVisual(dpy, 0), 24, ZPixmap, 0, + (char *)img.dpy_data, img.dpy_width, img.dpy_height, 8, 0); + + XSetWindowBackgroundPixmap(dpy, app.win, pixmap); + XPutImage(dpy, pixmap, DefaultGC(dpy, 0), ximg, img.src_x, img.src_y, + img.dest_x, img.dest_y, img.dpy_width, img.dpy_height); + XClearWindow(dpy, app.win); + + XFree(ximg); +} + +static void +event_loop() +{ + Pixmap pixmap; + XEvent ev; + KeySym keysym; + XConfigureEvent xce; + int tmp_x, tmp_y, left_mouse_hold, hold_x, hold_y; + int imgid, status; + char gotobuf[10]; + + memset(gotobuf, 0, 10); + left_mouse_hold = imgid = 0; + + set_zoom(NO_ZOOM); + pixmap = create_pixmap(); + apply_image(pixmap); + + while (1) { + XNextEvent(dpy, &ev); + switch (ev.type) { + case ConfigureNotify: + xce = ev.xconfigure; + if (xce.width != app.width || + xce.height != app.height) { + app.width = xce.width; + app.height = xce.height; + set_zoom(NO_ZOOM); + pixmap = create_pixmap(); + apply_image(pixmap); + } + break; + case KeyPress: + keysym = XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0, + ev.xkey.state & ShiftMask ? 1 : 0); + if (keysym == XK_q || keysym == XK_Escape) { + goto quit; + } else if (keysym >= XK_0 && keysym <= XK_9) { + if (gotobuf[9] == '\0') { + char tmp[2]; + sprintf(tmp, "%lu", keysym - 48); + strcat(gotobuf, tmp); + } + } else if (keysym == XK_g) { + int newid = atoi(gotobuf) - 1; + memset(gotobuf, 0, 10); + if (newid < 0 || newid > nimgs - 1) + break; + free_img(); + if (init_img(filepaths[newid]) != 0) + init_img(filepaths[imgid]); + imgid = newid; + set_zoom(NO_ZOOM); + } else if (keysym == XK_m) { + set_zoom(MAX); + } else if (keysym == XK_l) { + set_zoom(FILL); + } else if (keysym == XK_o) { + set_zoom(NO_ZOOM); + } else if (keysym == XK_p) { + if (imgid == 0) + break; + free_img(); + do { + imgid--; + status = init_img(filepaths[imgid]); + set_zoom(NO_ZOOM); + } while (status != 0 && imgid > 0); + if (status != 0) + goto quit; + } else if (keysym == XK_n) { + if (imgid == nimgs - 1) + break; + free_img(); + do { + imgid++; + status = init_img(filepaths[imgid]); + set_zoom(NO_ZOOM); + } while (status != 0 && imgid < nimgs - 1); + if (status != 0) + goto quit; + } else { + break; + } + pixmap = create_pixmap(); + apply_image(pixmap); + break; + case ButtonPress: + if (ev.xbutton.button == 1) { + left_mouse_hold = 1; + hold_x = ev.xmotion.x; + hold_y = ev.xmotion.y; + } + break; + case ButtonRelease: + if (ev.xbutton.button == 1) + left_mouse_hold = 0; + case MotionNotify: + if (!left_mouse_hold) + break; + tmp_x = img.src_x - ev.xmotion.x + hold_x; + tmp_y = img.src_y - ev.xmotion.y + hold_y; + + if (img.dpy_width > app.width) { + if (tmp_x < 0) + tmp_x = 0; + if (app.width + tmp_x > img.dpy_width) + tmp_x = img.dpy_width - app.width; + img.src_x = tmp_x; + } + if (img.dpy_height > app.height) { + if (tmp_y < 0) + tmp_y = 0; + if (app.height + tmp_y > img.dpy_height) + tmp_y = img.dpy_height - app.height; + + img.src_y = tmp_y; + } + if (img.src_x == tmp_x || img.src_y == tmp_y) + apply_image(pixmap); + + hold_x = ev.xmotion.x; + hold_y = ev.xmotion.y; + break; + } + } +quit: + XFreePixmap(dpy, pixmap); +} + +int +main(int argc, char **argv) +{ + int screen, status, i; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + errx(1, "Failed opening DISPLAY."); + + if (argc < 2) + usage(); + check_args(argc, argv); + + for (i = 0; i <= nimgs - 1; i++) { + status = init_img(filepaths[i]); + if (status == 0) + break; + } + if (status != 0) + return 0; + app.width = img.width; + app.height = img.height; + + screen = DefaultScreen(dpy); + app.win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 0, 0, + app.width, app.height, 0, 0, WhitePixel(dpy, screen)); + XSelectInput(dpy, app.win, ExposureMask | KeyPressMask | + ButtonPressMask | StructureNotifyMask | PointerMotionMask | + ButtonReleaseMask); + XMapRaised(dpy, app.win); + + event_loop(); + + XDestroyWindow(dpy, app.win); + XCloseDisplay(dpy); + free_img(); + free(filepaths); + + return 0; +}