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 }