commit de33336bdf4a91940a960149135a220fdd28ed20
parent 7bd0d9adca219f37e471c3e2c6a80c88532557b8
Author: lumidify <nobody@lumidify.org>
Date: Sat, 2 Jan 2021 21:14:47 +0100
Exit when no loadable images specified; clean up a bit
Diffstat:
M | croptool.c | | | 161 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
1 file changed, 102 insertions(+), 59 deletions(-)
diff --git a/croptool.c b/croptool.c
@@ -1,4 +1,3 @@
-/* FIXME: show error if there are no loadable images */
/*
* Copyright (c) 2021 lumidify <nobody[at]lumidify.org>
*
@@ -103,6 +102,7 @@ static struct {
XColor col1;
XColor col2;
int cur_col;
+ Atom wm_delete_msg;
Imlib_Image cur_image;
Imlib_Updates updates;
} state;
@@ -119,6 +119,9 @@ static struct {
Cursor grab;
} cursors;
+static void mainloop(void);
+static void setup(int argc, char *argv[]);
+static void cleanup(void);
static void sort_coordinates(int *x0, int *y0, int *x1, int *y1);
static void swap(int *a, int *b);
static void redraw();
@@ -146,12 +149,7 @@ static void queue_update(int x, int y, int w, int h);
static int parse_small_positive_int(const char *str, int *value);
int
-main(int argc, char **argv) {
- XEvent event;
- int running = 1;
- Atom wm_delete_msg;
- XSetWindowAttributes attrs;
- XGCValues gcv;
+main(int argc, char *argv[]) {
char c;
while ((c = getopt(argc, argv, "f:w:c:mrp:s:")) != -1) {
@@ -198,6 +196,69 @@ main(int argc, char **argv) {
fprintf(stderr, "No file given\n");
exit(1);
}
+ setup(argc, argv);
+
+ mainloop();
+
+ for (int i = 0; i < argc; i++) {
+ if (state.selections[i].valid) {
+ print_selection(&state.selections[i], state.filenames[i]);
+ }
+ }
+
+ cleanup();
+
+ return 0;
+}
+
+static void
+mainloop(void) {
+ XEvent event;
+ int running = 1;
+
+ while (running) {
+ do {
+ XNextEvent(state.dpy, &event);
+ switch (event.type) {
+ case Expose:
+ if (RESIZE_REDRAW)
+ queue_update(event.xexpose.x, event.xexpose.y,
+ event.xexpose.width, event.xexpose.height);
+ break;
+ case ConfigureNotify:
+ if (RESIZE_REDRAW)
+ resize_window(event.xconfigure.width, event.xconfigure.height);
+ break;
+ case ButtonPress:
+ if (event.xbutton.button == Button1)
+ button_press(event);
+ break;
+ case ButtonRelease:
+ if (event.xbutton.button == Button1)
+ button_release(event);
+ break;
+ case MotionNotify:
+ drag_motion(event);
+ break;
+ case KeyPress:
+ key_press(event);
+ break;
+ case ClientMessage:
+ if (event.xclient.data.l[0] == state.wm_delete_msg)
+ running = 0;
+ default:
+ break;
+ }
+ } while (XPending(state.dpy));
+
+ redraw();
+ }
+}
+
+static void
+setup(int argc, char *argv[]) {
+ XSetWindowAttributes attrs;
+ XGCValues gcv;
state.cur_image = NULL;
state.selections = malloc(argc * sizeof(struct Selection));
@@ -246,10 +307,9 @@ main(int argc, char **argv) {
XAllocColor(state.dpy, state.cm, &state.col2);
XSelectInput(state.dpy, state.win, StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask);
- XMapWindow(state.dpy, state.win);
- wm_delete_msg = XInternAtom(state.dpy, "WM_DELETE_WINDOW", False);
- XSetWMProtocols(state.dpy, state.win, &wm_delete_msg, 1);
+ state.wm_delete_msg = XInternAtom(state.dpy, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(state.dpy, state.win, &state.wm_delete_msg, 1);
cursors.top = XCreateFontCursor(state.dpy, XC_top_side);
cursors.bottom = XCreateFontCursor(state.dpy, XC_bottom_side);
@@ -271,51 +331,15 @@ main(int argc, char **argv) {
state.updates = imlib_updates_init();
next_picture(0);
+ /* Only map window here so the program exits immediately if
+ there are no loadable images, without first opening the
+ window and closing it again immediately */
+ XMapWindow(state.dpy, state.win);
redraw();
+}
- while (running) {
- do {
- XNextEvent(state.dpy, &event);
- switch (event.type) {
- case Expose:
- if (RESIZE_REDRAW)
- queue_update(event.xexpose.x, event.xexpose.y,
- event.xexpose.width, event.xexpose.height);
- break;
- case ConfigureNotify:
- if (RESIZE_REDRAW)
- resize_window(event.xconfigure.width, event.xconfigure.height);
- break;
- case ButtonPress:
- if (event.xbutton.button == Button1)
- button_press(event);
- break;
- case ButtonRelease:
- if (event.xbutton.button == Button1)
- button_release(event);
- break;
- case MotionNotify:
- drag_motion(event);
- break;
- case KeyPress:
- key_press(event);
- break;
- case ClientMessage:
- if (event.xclient.data.l[0] == wm_delete_msg)
- running = 0;
- default:
- break;
- }
- } while (XPending(state.dpy));
-
- redraw();
- }
-
- for (int i = 0; i < argc; i++) {
- if (state.selections[i].valid) {
- print_selection(&state.selections[i], argv[i]);
- }
- }
+static void
+cleanup(void) {
if (state.cur_image) {
imlib_context_set_image(state.cur_image);
imlib_free_image();
@@ -323,8 +347,6 @@ main(int argc, char **argv) {
free(state.selections);
XDestroyWindow(state.dpy, state.win);
XCloseDisplay(state.dpy);
-
- return 0;
}
/* TODO: Allow printing filename without ending */
@@ -446,15 +468,19 @@ redraw(void) {
imlib_updates_free(state.updates);
state.updates = imlib_updates_init();
+ /* wipe the black area around the image */
XSetForeground(state.dpy, state.gc, BlackPixel(state.dpy, state.screen));
XFillRectangle(state.dpy, state.win, state.gc, 0, sel->scaled_h, sel->scaled_w, state.window_h - sel->scaled_h);
XFillRectangle(state.dpy, state.win, state.gc, sel->scaled_w, 0, state.window_w - sel->scaled_w, state.window_h);
- XColor col = state.cur_col == 1 ? state.col1 : state.col2;
- XSetForeground(state.dpy, state.gc, col.pixel);
+ /* draw the rectangle */
struct Rect rect = sel->rect;
- sort_coordinates(&rect.x0, &rect.y0, &rect.x1, &rect.y1);
- XDrawRectangle(state.dpy, state.win, state.gc, rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0);
+ if (rect.x0 != -200) {
+ XColor col = state.cur_col == 1 ? state.col1 : state.col2;
+ XSetForeground(state.dpy, state.gc, col.pixel);
+ sort_coordinates(&rect.x0, &rect.y0, &rect.x1, &rect.y1);
+ XDrawRectangle(state.dpy, state.win, state.gc, rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0);
+ }
}
static void
@@ -585,6 +611,8 @@ button_release(XEvent event) {
state.resizing = 0;
state.lock_x = 0;
state.lock_y = 0;
+ /* redraw everything if automatic redrawing of the rectangle
+ is disabled (so it's redrawn when the mouse is released */
if (!SELECTION_REDRAW)
queue_update(0, 0, state.window_w, state.window_h);
}
@@ -648,6 +676,7 @@ drag_motion(XEvent event) {
int x0 = rect->x0, x1 = rect->x1;
int y0 = rect->y0, y1 = rect->y1;
sort_coordinates(&x0, &y0, &x1, &y1);
+ /* redraw the old rectangle */
if (SELECTION_REDRAW && (state.moving || state.resizing))
queue_rectangle_redraw(x0, y0, x1, y1);
if (state.moving) {
@@ -689,6 +718,7 @@ drag_motion(XEvent event) {
return;
}
+ /* redraw the new rectangle */
if (SELECTION_REDRAW)
queue_rectangle_redraw(rect->x0, rect->y0, rect->x1, rect->y1);
}
@@ -776,6 +806,15 @@ next_picture(int copy_box) {
while (!tmp_image && tmp_cur_selection + 1 < state.num_files) {
tmp_cur_selection++;
tmp_image = imlib_load_image(state.filenames[tmp_cur_selection]);
+ if (!tmp_image) {
+ fprintf(stderr, "Warning: Unable to load image '%s'.\n",
+ state.filenames[tmp_cur_selection]);
+ }
+ }
+ if (state.cur_selection < 0 && !tmp_image) {
+ fprintf(stderr, "No loadable images found.\n");
+ cleanup();
+ exit(1);
}
if (!tmp_image)
return;
@@ -793,6 +832,10 @@ last_picture(void) {
while (!tmp_image && tmp_cur_selection > 0) {
tmp_cur_selection--;
tmp_image = imlib_load_image(state.filenames[tmp_cur_selection]);
+ if (!tmp_image) {
+ fprintf(stderr, "Warning: Unable to load image '%s'.\n",
+ state.filenames[tmp_cur_selection]);
+ }
}
if (!tmp_image)