URI: 
       common.c - croptool - Image cropping tool
  HTML git clone git://lumidify.org/croptool.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/croptool.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/croptool.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       common.c (9480B)
       ---
            1 /*
            2  * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
            3  *
            4  * Permission to use, copy, modify, and/or distribute this software for any
            5  * purpose with or without fee is hereby granted, provided that the above
            6  * copyright notice and this permission notice appear in all copies.
            7  *
            8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
           10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
           11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
           12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
           13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
           14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
           15  */
           16 
           17 #include <stdio.h>
           18 #include <errno.h>
           19 #include <string.h>
           20 #include <stdlib.h>
           21 #include <limits.h>
           22 
           23 #include <X11/X.h>
           24 #include <X11/Xlib.h>
           25 #include <X11/Xutil.h>
           26 #ifndef NODB
           27 #include <X11/extensions/Xdbe.h>
           28 #endif
           29 
           30 #include <Imlib2.h>
           31 
           32 #include "common.h"
           33 
           34 void
           35 setup_x(GraphicsContext *ctx, int window_w, int window_h, int line_width, int cache_size) {
           36         XSetWindowAttributes attrs;
           37         XGCValues gcv;
           38 
           39         ctx->dpy = XOpenDisplay(NULL);
           40         ctx->screen = DefaultScreen(ctx->dpy);
           41         ctx->vis = DefaultVisual(ctx->dpy, ctx->screen);
           42         ctx->depth = DefaultDepth(ctx->dpy, ctx->screen);
           43         ctx->cm = DefaultColormap(ctx->dpy, ctx->screen);
           44         ctx->dirty = 1;
           45 
           46         #ifndef NODB
           47         ctx->db_enabled = 0;
           48         /* based on http://wili.cc/blog/xdbe.html */
           49         int major, minor;
           50         if (XdbeQueryExtension(ctx->dpy, &major, &minor)) {
           51                 int num_screens = 1;
           52                 Drawable screens[] = { DefaultRootWindow(ctx->dpy) };
           53                 XdbeScreenVisualInfo *info = XdbeGetVisualInfo(
           54                     ctx->dpy, screens, &num_screens
           55                 );
           56                 if (!info || num_screens < 1 || info->count < 1) {
           57                         fprintf(stderr,
           58                             "Warning: No visuals support Xdbe, "
           59                             "double buffering disabled.\n"
           60                         );
           61                 } else {
           62                         XVisualInfo xvisinfo_templ;
           63                         xvisinfo_templ.visualid = info->visinfo[0].visual;
           64                         xvisinfo_templ.screen = 0;
           65                         xvisinfo_templ.depth = info->visinfo[0].depth;
           66                         int matches;
           67                         XVisualInfo *xvisinfo_match = XGetVisualInfo(
           68                             ctx->dpy,
           69                             VisualIDMask | VisualScreenMask | VisualDepthMask,
           70                             &xvisinfo_templ, &matches
           71                         );
           72                         if (!xvisinfo_match || matches < 1) {
           73                                 fprintf(stderr,
           74                                     "Warning: Couldn't match a Visual with "
           75                                     "double buffering, double buffering disabled.\n"
           76                                 );
           77                         } else {
           78                                 ctx->vis = xvisinfo_match->visual;
           79                                 ctx->depth = xvisinfo_match->depth;
           80                                 ctx->db_enabled = 1;
           81                         }
           82                         XFree(xvisinfo_match);
           83                 }
           84                 XdbeFreeVisualInfo(info);
           85         } else {
           86                 fprintf(stderr, "Warning: No Xdbe support, double buffering disabled.\n");
           87         }
           88         #endif
           89 
           90         memset(&attrs, 0, sizeof(attrs));
           91         attrs.background_pixel = BlackPixel(ctx->dpy, ctx->screen);
           92         attrs.colormap = ctx->cm;
           93         /* this causes the window contents to be kept
           94          * when it is resized, leading to less flicker */
           95         attrs.bit_gravity = NorthWestGravity;
           96         ctx->win = XCreateWindow(ctx->dpy, DefaultRootWindow(ctx->dpy), 0, 0,
           97             window_w, window_h, 0, ctx->depth,
           98             InputOutput, ctx->vis, CWBackPixel | CWColormap | CWBitGravity, &attrs);
           99 
          100         #ifndef NODB
          101         if (ctx->db_enabled) {
          102                 ctx->back_buf = XdbeAllocateBackBufferName(
          103                     ctx->dpy, ctx->win, XdbeCopied
          104                 );
          105                 ctx->drawable = ctx->back_buf;
          106         } else {
          107                 ctx->drawable = ctx->win;
          108         }
          109         #else
          110         ctx->drawable = ctx->win;
          111         #endif
          112 
          113         memset(&gcv, 0, sizeof(gcv));
          114         gcv.line_width = line_width;
          115         ctx->gc = XCreateGC(ctx->dpy, ctx->win, GCLineWidth, &gcv);
          116 
          117         XSelectInput(
          118             ctx->dpy, ctx->win,
          119             StructureNotifyMask | KeyPressMask | ButtonPressMask |
          120             ButtonReleaseMask | PointerMotionMask | ExposureMask
          121         );
          122 
          123         ctx->wm_delete_msg = XInternAtom(ctx->dpy, "WM_DELETE_WINDOW", False);
          124         XSetWMProtocols(ctx->dpy, ctx->win, &ctx->wm_delete_msg, 1);
          125 
          126         /* note: since cache_size is <= 1024, this definitely fits in long */
          127         long cs = (long)cache_size * 1024 * 1024;
          128         if (cs > INT_MAX) {
          129                 fprintf(stderr, "Cache size would cause integer overflow.\n");
          130                 exit(1);
          131         }
          132         imlib_set_cache_size((int)cs);
          133         imlib_set_color_usage(128);
          134         imlib_context_set_dither(1);
          135         imlib_context_set_display(ctx->dpy);
          136         imlib_context_set_visual(ctx->vis);
          137         imlib_context_set_colormap(ctx->cm);
          138         imlib_context_set_drawable(ctx->drawable);
          139         ctx->updates = imlib_updates_init();
          140         ctx->cur_image = NULL;
          141 }
          142 
          143 void
          144 cleanup_x(GraphicsContext *ctx) {
          145         if (ctx->cur_image) {
          146                 imlib_context_set_image(ctx->cur_image);
          147                 imlib_free_image();
          148         }
          149         XDestroyWindow(ctx->dpy, ctx->win);
          150         XCloseDisplay(ctx->dpy);
          151 }
          152 
          153 int
          154 parse_int(const char *str, int min, int max, int *value) {
          155         char *end;
          156         long l = strtol(str, &end, 10);
          157         if (min > max)
          158                 return 1;
          159         if (str == end || *end != '\0') {
          160                 return 1;
          161         } else if (l < min || l > max || ((l == LONG_MIN ||
          162             l == LONG_MAX) && errno == ERANGE)) {
          163                 return 1;
          164         }
          165         *value = (int)l;
          166 
          167         return 0;
          168 }
          169 
          170 void
          171 queue_area_update(GraphicsContext *ctx, ImageSize *sz, int x, int y, int w, int h) {
          172         if (x > sz->scaled_w || y > sz->scaled_h)
          173                 return;
          174         ctx->updates = imlib_update_append_rect(
          175             ctx->updates, x, y,
          176             w + x > sz->scaled_w ? sz->scaled_w - x : w,
          177             h + y > sz->scaled_h ? sz->scaled_h - y : h
          178         );
          179         ctx->dirty = 1;
          180 }
          181 
          182 void
          183 clear_screen(GraphicsContext *ctx) {
          184 
          185         /* clear the window completely */
          186         XSetForeground(ctx->dpy, ctx->gc, BlackPixel(ctx->dpy, ctx->screen));
          187         XFillRectangle(
          188             ctx->dpy, ctx->drawable, ctx->gc,
          189             0, 0, ctx->window_w, ctx->window_h
          190         );
          191 }
          192 
          193 void
          194 draw_image_updates(GraphicsContext *ctx, ImageSize *sz) {
          195         Imlib_Image buffer;
          196         Imlib_Updates current_update;
          197 
          198         /* draw the parts of the image that need to be redrawn */
          199         ctx->updates = imlib_updates_merge_for_rendering(
          200             ctx->updates, sz->scaled_w, sz->scaled_h
          201         );
          202         /* FIXME: check since when imlib_render_image_updates_on_drawable supported, also maybe just render_on_drawable part scaled */
          203         for (current_update = ctx->updates; current_update;
          204             current_update = imlib_updates_get_next(current_update)) {
          205                 int up_x, up_y, up_w, up_h;
          206                 imlib_updates_get_coordinates(current_update, &up_x, &up_y, &up_w, &up_h);
          207                 buffer = imlib_create_image(up_w, up_h);
          208                 imlib_context_set_blend(0);
          209                 imlib_context_set_image(buffer);
          210                 imlib_blend_image_onto_image(
          211                     ctx->cur_image, 0, 0, 0,
          212                     sz->orig_w, sz->orig_h,
          213                     -up_x, -up_y,
          214                     sz->scaled_w, sz->scaled_h);
          215                 imlib_render_image_on_drawable(up_x, up_y);
          216                 imlib_free_image();
          217         }
          218         if (ctx->updates)
          219                 imlib_updates_free(ctx->updates);
          220         ctx->updates = imlib_updates_init();
          221 }
          222 
          223 void
          224 wipe_around_image(GraphicsContext *ctx, ImageSize *sz) {
          225 
          226         /* wipe the black area around the image */
          227         XSetForeground(ctx->dpy, ctx->gc, BlackPixel(ctx->dpy, ctx->screen));
          228         XFillRectangle(
          229             ctx->dpy, ctx->drawable, ctx->gc,
          230             0, sz->scaled_h, sz->scaled_w, ctx->window_h - sz->scaled_h
          231         );
          232         XFillRectangle(
          233             ctx->dpy, ctx->drawable, ctx->gc,
          234             sz->scaled_w, 0, ctx->window_w - sz->scaled_w, ctx->window_h
          235         );
          236 }
          237 
          238 void
          239 swap_buffers(GraphicsContext *ctx) {
          240         #ifndef NODB
          241         if (ctx->db_enabled) {
          242                 XdbeSwapInfo swap_info;
          243                 swap_info.swap_window = ctx->win;
          244                 swap_info.swap_action = XdbeCopied;
          245 
          246                 if (!XdbeSwapBuffers(ctx->dpy, &swap_info, 1))
          247                         fprintf(stderr, "Warning: Unable to swap buffers.\n");
          248         }
          249         #endif
          250         ctx->dirty = 0;
          251 }
          252 
          253 /* get the scaled size of an image based on the current window size */
          254 void
          255 get_scaled_size(GraphicsContext *ctx, int orig_w, int orig_h, int *scaled_w, int *scaled_h) {
          256         double scale_w, scale_h;
          257         scale_w = (double)ctx->window_w / (double)orig_w;
          258         scale_h = (double)ctx->window_h / (double)orig_h;
          259         if (orig_w <= ctx->window_w && orig_h <= ctx->window_h) {
          260                 *scaled_w = orig_w;
          261                 *scaled_h = orig_h;
          262         } else if (scale_w * orig_h > ctx->window_h) {
          263                 *scaled_w = (int)(scale_h * orig_w);
          264                 *scaled_h = ctx->window_h;
          265         } else {
          266                 *scaled_w = ctx->window_w;
          267                 *scaled_h = (int)(scale_w * orig_h);
          268         }
          269 }
          270 
          271 void
          272 next_picture(int cur_selection, char **filenames, int num_files, int copy_box) {
          273         if (cur_selection + 1 >= num_files)
          274                 return;
          275         Imlib_Image tmp_image = NULL;
          276         int tmp_cur_selection = cur_selection;
          277         /* loop until we find a loadable file */
          278         while (!tmp_image && tmp_cur_selection + 1 < num_files) {
          279                 tmp_cur_selection++;
          280                 if (!filenames[tmp_cur_selection])
          281                         continue;
          282                 tmp_image = imlib_load_image_immediately(
          283                     filenames[tmp_cur_selection]
          284                 );
          285                 if (!tmp_image) {
          286                         fprintf(
          287                                 stderr, "Warning: Unable to load image '%s'.\n",
          288                                 filenames[tmp_cur_selection]
          289                         );
          290                         filenames[tmp_cur_selection] = NULL;
          291                 }
          292         }
          293         /* immediately exit program if no loadable image is found on startup */
          294         if (cur_selection < 0 && !tmp_image) {
          295                 fprintf(stderr, "No loadable images found.\n");
          296                 cleanup();
          297                 exit(1);
          298         }
          299         if (!tmp_image)
          300                 return;
          301 
          302         change_picture(tmp_image, tmp_cur_selection, copy_box);
          303 }
          304 
          305 void
          306 last_picture(int cur_selection, char **filenames, int copy_box) {
          307         if (cur_selection <= 0)
          308                 return;
          309         Imlib_Image tmp_image = NULL;
          310         int tmp_cur_selection = cur_selection;
          311         /* loop until we find a loadable file */
          312         while (!tmp_image && tmp_cur_selection > 0) {
          313                 tmp_cur_selection--;
          314                 if (!filenames[tmp_cur_selection])
          315                         continue;
          316                 tmp_image = imlib_load_image_immediately(
          317                     filenames[tmp_cur_selection]
          318                 );
          319                 if (!tmp_image) {
          320                         fprintf(
          321                                 stderr, "Warning: Unable to load image '%s'.\n",
          322                                 filenames[tmp_cur_selection]
          323                         );
          324                         filenames[tmp_cur_selection] = NULL;
          325                 }
          326         }
          327 
          328         if (!tmp_image)
          329                 return;
          330 
          331         change_picture(tmp_image, tmp_cur_selection, copy_box);
          332 }