main.c (scrot-0.8) | : | main.c (scrot-1.6.tar.bz2) | ||
---|---|---|---|---|
/* main.c | /* main.c | |||
Copyright (C) 1999,2000 Tom Gilbert. | Copyright 1999-2000 Tom Gilbert <tom@linuxbrit.co.uk, | |||
gilbertt@linuxbrit.co.uk, | ||||
scrot_sucks@linuxbrit.co.uk> | ||||
Copyright 2009 James Cameron <quozl@us.netrek.org> | ||||
Copyright 2010 Ibragimov Rinat <ibragimovrinat@mail.ru> | ||||
Copyright 2017 Stoney Sauce <stoneysauce@gmail.com> | ||||
Copyright 2019-2021 Daniel T. Borelli <daltomi@disroot.org> | ||||
Copyright 2019 Jade Auer <jade@trashwitch.dev> | ||||
Copyright 2020 blockparole | ||||
Copyright 2020 Cungsten Tarbide <ctarbide@tuta.io> | ||||
Copyright 2020 Hinigatsu <hinigatsu@protonmail.com> | ||||
Copyright 2020 nothub | ||||
Copyright 2020 Sean Brennan <zettix1@gmail.com> | ||||
Copyright 2021 c0dev0id <sh+github@codevoid.de> | ||||
Copyright 2021 Christopher R. Nelson <christopher.nelson@languidnights.com> | ||||
Copyright 2021 Guilherme Janczak <guilherme.janczak@yandex.com> | ||||
Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to | of this software and associated documentation files (the "Software"), to | |||
deal in the Software without restriction, including without limitation the | deal in the Software without restriction, including without limitation the | |||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | 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 | sell copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | The above copyright notice and this permission notice shall be included in | |||
all copies of the Software and its documentation and acknowledgment shall be | all copies of the Software and its documentation and acknowledgment shall be | |||
skipping to change at line 28 | skipping to change at line 43 | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 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. | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
*/ | */ | |||
#include "scrot.h" | #include "scrot.h" | |||
#include "options.h" | #include "options.h" | |||
#include "slist.h" | ||||
#include <assert.h> | ||||
/* atexit register func. */ | ||||
static void uninit_x_and_imlib(void) | ||||
{ | ||||
if (disp) { | ||||
XCloseDisplay(disp); | ||||
disp = NULL; | ||||
} | ||||
} | ||||
// It assumes that the local variable 'main.c:Imlib_Image image' is in context | ||||
static void apply_filter_if_required(void) | ||||
{ | ||||
if (opt.script != NULL) { | ||||
imlib_apply_filter(opt.script); | ||||
} | ||||
} | ||||
int | int | |||
main(int argc, | main(int argc, | |||
char **argv) | char **argv) | |||
{ | { | |||
Imlib_Image image; | Imlib_Image image; | |||
Imlib_Image thumbnail; | Imlib_Image thumbnail; | |||
Imlib_Load_Error err; | Imlib_Load_Error err; | |||
char *filename_im = NULL, *filename_thumb = NULL; | char *filename_im = NULL, *filename_thumb = NULL; | |||
char *have_extension = NULL; | ||||
time_t t; | time_t t; | |||
struct tm *tm; | struct tm *tm; | |||
init_parse_options(argc, argv); | init_parse_options(argc, argv); | |||
init_x_and_imlib(NULL, 0); | init_x_and_imlib(opt.display, 0); | |||
atexit(uninit_x_and_imlib); | ||||
if (!opt.output_file) { | if (!opt.output_file) { | |||
opt.output_file = gib_estrdup("%Y-%m-%d-%H%M%S_$wx$h_scrot.png"); | opt.output_file = strdup("%Y-%m-%d-%H%M%S_$wx$h_scrot.png"); | |||
opt.thumb_file = gib_estrdup("%Y-%m-%d-%H%M%S_$wx$h_scrot-thumb.png"); | opt.thumb_file = strdup("%Y-%m-%d-%H%M%S_$wx$h_scrot-thumb.png"); | |||
} else { | ||||
scrot_have_file_extension(opt.output_file, &have_extension); | ||||
} | } | |||
if (opt.select) | if (opt.focused) | |||
image = scrot_grab_focused(); | ||||
else if (opt.select) | ||||
image = scrot_sel_and_grab_image(); | image = scrot_sel_and_grab_image(); | |||
else { | else if (opt.autoselect) | |||
image = scrot_grab_autoselect(); | ||||
else | ||||
{ | ||||
scrot_do_delay(); | scrot_do_delay(); | |||
if (opt.multidisp) { | if (opt.multidisp) { | |||
image = scrot_grab_shot_multi(); | image = scrot_grab_shot_multi(); | |||
} else if (opt.stack) { | ||||
image = scrot_grab_stack_windows(); | ||||
} else { | } else { | |||
image = scrot_grab_shot(); | image = scrot_grab_shot(); | |||
} | } | |||
} | } | |||
if (!image) | if (opt.note != NULL) | |||
gib_eprintf("no image grabbed"); | scrot_note_draw(image); | |||
if (!image) { | ||||
fprintf(stderr, "no image grabbed: %s", strerror(errno)); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
time(&t); /* Get the time directly after the screenshot */ | time(&t); /* Get the time directly after the screenshot */ | |||
tm = localtime(&t); | tm = localtime(&t); | |||
imlib_context_set_image(image); | imlib_context_set_image(image); | |||
imlib_image_attach_data_value("quality", NULL, opt.quality, NULL); | imlib_image_attach_data_value("quality", NULL, opt.quality, NULL); | |||
if (!have_extension) { | ||||
imlib_image_set_format("png"); | ||||
} | ||||
filename_im = im_printf(opt.output_file, tm, NULL, NULL, image); | filename_im = im_printf(opt.output_file, tm, NULL, NULL, image); | |||
gib_imlib_save_image_with_error_return(image, filename_im, &err); | scrot_check_if_overwrite_file(&filename_im); | |||
if (err) | ||||
gib_eprintf("Saving to file %s failed\n", filename_im); | apply_filter_if_required(); | |||
imlib_save_image_with_error_return(filename_im, &err); | ||||
if (err) { | ||||
fprintf(stderr, "Saving to file %s failed: %s\n", filename_im, strerror(errn | ||||
o)); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
if (opt.thumb) | if (opt.thumb) | |||
{ | { | |||
int cwidth, cheight; | int cwidth, cheight; | |||
int twidth, theight; | int twidth, theight; | |||
cwidth = gib_imlib_image_get_width(image); | cwidth = imlib_image_get_width(); | |||
cheight = gib_imlib_image_get_height(image); | cheight = imlib_image_get_height(); | |||
/* Geometry based thumb size */ | /* Geometry based thumb size */ | |||
if (opt.thumb_width || opt.thumb_height) | if (opt.thumb_width || opt.thumb_height) | |||
{ | { | |||
if (!opt.thumb_width) | if (!opt.thumb_width) | |||
{ | { | |||
twidth = cwidth * opt.thumb_height / cheight; | twidth = cwidth * opt.thumb_height / cheight; | |||
theight = opt.thumb_height; | theight = opt.thumb_height; | |||
} | } | |||
else if (!opt.thumb_height) | else if (!opt.thumb_height) | |||
skipping to change at line 107 | skipping to change at line 169 | |||
twidth = opt.thumb_width; | twidth = opt.thumb_width; | |||
theight = opt.thumb_height; | theight = opt.thumb_height; | |||
} | } | |||
} | } | |||
else | else | |||
{ | { | |||
twidth = cwidth * opt.thumb / 100; | twidth = cwidth * opt.thumb / 100; | |||
theight = cheight * opt.thumb / 100; | theight = cheight * opt.thumb / 100; | |||
} | } | |||
imlib_context_set_anti_alias(1); | ||||
thumbnail = | thumbnail = | |||
gib_imlib_create_cropped_scaled_image(image, 0, 0, cwidth, cheight, | imlib_create_cropped_scaled_image(0, 0, cwidth, cheight, | |||
twidth, theight, 1); | twidth, theight); | |||
if (thumbnail == NULL) | if (thumbnail == NULL) { | |||
gib_eprintf("Unable to create scaled Image\n"); | fprintf(stderr, "Unable to create scaled Image: %s\n", strerror(errno)); | |||
exit(EXIT_FAILURE); | ||||
} | ||||
else | else | |||
{ | { | |||
if (opt.note != NULL) | ||||
scrot_note_draw(image); | ||||
scrot_have_file_extension(opt.thumb_file, &have_extension); | ||||
if (!have_extension) { | ||||
imlib_context_set_image(thumbnail); | ||||
imlib_image_set_format("png"); | ||||
} | ||||
filename_thumb = im_printf(opt.thumb_file, tm, NULL, NULL, thumbnail); | filename_thumb = im_printf(opt.thumb_file, tm, NULL, NULL, thumbnail); | |||
gib_imlib_save_image_with_error_return(thumbnail, filename_thumb, &err); | scrot_check_if_overwrite_file(&filename_thumb); | |||
if (err) | ||||
gib_eprintf("Saving thumbnail %s failed\n", filename_thumb); | apply_filter_if_required(); | |||
imlib_save_image_with_error_return(filename_thumb, &err); | ||||
if (err) { | ||||
fprintf(stderr, "Saving thumbnail %s failed: %s\n", filename_thumb, stre | ||||
rror(errno)); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
} | } | |||
} | } | |||
if (opt.exec) | if (opt.exec) | |||
scrot_exec_app(image, tm, filename_im, filename_thumb); | scrot_exec_app(image, tm, filename_im, filename_thumb); | |||
gib_imlib_free_image_and_decache(image); | imlib_free_image_and_decache(); | |||
return 0; | return 0; | |||
} | } | |||
void | void | |||
scrot_do_delay(void) | scrot_do_delay(void) | |||
{ | { | |||
if (opt.delay) { | if (opt.delay) { | |||
if (opt.countdown) { | if (opt.countdown) { | |||
int i; | int i; | |||
skipping to change at line 149 | skipping to change at line 230 | |||
fflush(stdout); | fflush(stdout); | |||
sleep(1); | sleep(1); | |||
} | } | |||
printf("0.\n"); | printf("0.\n"); | |||
fflush(stdout); | fflush(stdout); | |||
} else | } else | |||
sleep(opt.delay); | sleep(opt.delay); | |||
} | } | |||
} | } | |||
size_t scrot_have_file_extension(char const *filename, char **ext) | ||||
{ | ||||
*ext = strrchr(filename, '.'); | ||||
if (*ext) { | ||||
return strlen(*ext); | ||||
} | ||||
return 0; | ||||
} | ||||
void scrot_check_if_overwrite_file(char **filename) | ||||
{ | ||||
char *curfile = *filename; | ||||
if (opt.overwrite == 1) return; | ||||
if (access(curfile, F_OK) == -1) return; | ||||
const size_t max_counter = 999; | ||||
size_t counter = 0; | ||||
char *ext = NULL; | ||||
size_t ext_len = 0; | ||||
const size_t slen = strlen(curfile); | ||||
size_t nalloc = slen + 4 + 1; // _000 + NUL byte | ||||
char fmt[5]; | ||||
char *newname = NULL; | ||||
ext_len = scrot_have_file_extension(curfile, &ext); | ||||
if (ext) | ||||
nalloc += ext_len; // .ext | ||||
newname = calloc(nalloc, sizeof(char)); | ||||
if (ext) | ||||
// exclude ext | ||||
memcpy(newname, curfile, slen - ext_len); | ||||
else | ||||
memcpy(newname, curfile, slen); | ||||
do { | ||||
snprintf(fmt, 5, "_%03zu", counter++); | ||||
if (!ext) { | ||||
strncpy(newname + slen, fmt, 5); | ||||
} else { | ||||
strncpy((newname + slen) - ext_len, fmt, 5); | ||||
strncat(newname, ext, ext_len); | ||||
} | ||||
curfile = newname; | ||||
} while ((counter < max_counter) && (access(curfile, F_OK) == 0)); | ||||
free(*filename); | ||||
*filename = newname; | ||||
if (counter == max_counter) { | ||||
fprintf(stderr, "scrot can no longer generate new file names.\n" | ||||
"The last attempt is %s\n", newname); | ||||
free(newname); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
} | ||||
int scrot_match_window_class_name(Window target) | ||||
{ | ||||
assert(disp != NULL); | ||||
const int NOT_MATCH = 0; | ||||
const int MATCH = 1; | ||||
/* By default all class names match since window_class_name by default is NU | ||||
LL*/ | ||||
int retval = MATCH; | ||||
if (opt.window_class_name == NULL) { | ||||
return retval; | ||||
} | ||||
XClassHint classHint; | ||||
retval = NOT_MATCH; // window_class_name != NULL, by default NOT_MATCH | ||||
if (XGetClassHint(disp, target, &classHint) != BadWindow) { | ||||
retval = options_cmp_window_class_name(classHint.res_class); | ||||
XFree(classHint.res_name); | ||||
XFree(classHint.res_class); | ||||
} | ||||
return retval; | ||||
} | ||||
void | ||||
scrot_grab_mouse_pointer(const Imlib_Image image, | ||||
const int ix_off, const int iy_off) | ||||
{ | ||||
XFixesCursorImage *xcim = XFixesGetCursorImage(disp); | ||||
const unsigned short width = xcim->width; | ||||
const unsigned short height = xcim->height; | ||||
const int x = (xcim->x - xcim->xhot) - ix_off; | ||||
const int y = (xcim->y - xcim->yhot) - iy_off; | ||||
DATA32 *pixels = NULL; | ||||
#ifdef __i386__ | ||||
pixels = (DATA32*)xcim->pixels; | ||||
#else | ||||
DATA32 data[width * height * 4]; | ||||
unsigned int i; | ||||
for (i = 0; i < (width * height); i++) | ||||
((DATA32*)data)[i] = (DATA32)xcim->pixels[i]; | ||||
pixels = data; | ||||
#endif | ||||
Imlib_Image imcursor = imlib_create_image_using_data(width, height, pixels); | ||||
XFree(xcim); | ||||
if (!imcursor) { | ||||
fprintf(stderr, "scrot_grab_mouse_pointer: Failed create image using data.\ | ||||
n"); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
imlib_context_set_image(imcursor); | ||||
imlib_image_set_has_alpha(1); | ||||
imlib_context_set_image(image); | ||||
imlib_blend_image_onto_image(imcursor, 0, 0, 0, width, height, x, y, width, he | ||||
ight); | ||||
imlib_context_set_image(imcursor); | ||||
imlib_free_image(); | ||||
} | ||||
Imlib_Image | Imlib_Image | |||
scrot_grab_shot(void) | scrot_grab_shot(void) | |||
{ | { | |||
Imlib_Image im; | Imlib_Image im; | |||
XBell(disp, 0); | if (! opt.silent) XBell(disp, 0); | |||
im = | im = | |||
gib_imlib_create_image_from_drawable(root, 0, 0, 0, scr->width, | imlib_create_image_from_drawable(0, 0, 0, scr->width, | |||
scr->height, 1); | scr->height, 1); | |||
if (opt.pointer == 1) | ||||
scrot_grab_mouse_pointer(im, 0, 0); | ||||
return im; | return im; | |||
} | } | |||
void | void | |||
scrot_exec_app(Imlib_Image image, struct tm *tm, | scrot_exec_app(Imlib_Image image, struct tm *tm, | |||
char *filename_im, char *filename_thumb) | char *filename_im, char *filename_thumb) | |||
{ | { | |||
char *execstr; | char *execstr; | |||
int ret; | ||||
execstr = im_printf(opt.exec, tm, filename_im, filename_thumb, image); | execstr = im_printf(opt.exec, tm, filename_im, filename_thumb, image); | |||
system(execstr); | ||||
errno = 0; | ||||
ret = system(execstr); | ||||
if (ret == -1) { | ||||
fprintf(stderr, "The child process could not be created: %s\n", strerror(err | ||||
no)); | ||||
} else if (WEXITSTATUS(ret) == 127) { | ||||
fprintf(stderr, "scrot could not execute the command: %s.\n", execstr); | ||||
} | ||||
exit(0); | exit(0); | |||
} | } | |||
Imlib_Image | Imlib_Image | |||
scrot_grab_focused(void) | ||||
{ | ||||
Imlib_Image im = NULL; | ||||
int rx = 0, ry = 0, rw = 0, rh = 0; | ||||
Window target = None; | ||||
int ignored; | ||||
scrot_do_delay(); | ||||
XGetInputFocus(disp, &target, &ignored); | ||||
if (!scrot_get_geometry(target, &rx, &ry, &rw, &rh)) return NULL; | ||||
scrot_nice_clip(&rx, &ry, &rw, &rh); | ||||
im = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); | ||||
if (opt.pointer == 1) | ||||
scrot_grab_mouse_pointer(im, rx, ry); | ||||
return im; | ||||
} | ||||
Imlib_Image | ||||
scrot_sel_and_grab_image(void) | scrot_sel_and_grab_image(void) | |||
{ | { | |||
Imlib_Image im = NULL; | Imlib_Image im = NULL; | |||
static int xfd = 0; | static int xfd = 0; | |||
static int fdsize = 0; | static int fdsize = 0; | |||
XEvent ev; | XEvent ev; | |||
fd_set fdset; | fd_set fdset; | |||
int count = 0, done = 0; | int count = 0, done = 0; | |||
int rx = 0, ry = 0, rw = 0, rh = 0, btn_pressed = 0; | int rx = 0, ry = 0, rw = 0, rh = 0, btn_pressed = 0; | |||
int rect_x = 0, rect_y = 0, rect_w = 0, rect_h = 0; | ||||
Cursor cursor, cursor2; | ||||
Window target = None; | Window target = None; | |||
GC gc; | Status ret; | |||
XGCValues gcval; | ||||
scrot_selection_create(); | ||||
xfd = ConnectionNumber(disp); | xfd = ConnectionNumber(disp); | |||
fdsize = xfd + 1; | fdsize = xfd + 1; | |||
cursor = XCreateFontCursor(disp, XC_left_ptr); | ret = XGrabKeyboard(disp, root, False, GrabModeAsync, GrabModeAsync, CurrentTi | |||
cursor2 = XCreateFontCursor(disp, XC_lr_angle); | me); | |||
if (ret == AlreadyGrabbed) { | ||||
gcval.foreground = XWhitePixel(disp, 0); | int attempts = 20; | |||
gcval.function = GXxor; | struct timespec delay = {0, 50 * 1000L * 1000L}; | |||
gcval.background = XBlackPixel(disp, 0); | do { | |||
gcval.plane_mask = gcval.background ^ gcval.foreground; | nanosleep(&delay, NULL); | |||
gcval.subwindow_mode = IncludeInferiors; | ret = XGrabKeyboard(disp, root, False, GrabModeAsync, GrabModeAsync, Curre | |||
ntTime); | ||||
gc = | } while (--attempts > 0 && ret == AlreadyGrabbed); | |||
XCreateGC(disp, root, | } | |||
GCFunction | GCForeground | GCBackground | GCSubwindowMode, | if (ret != GrabSuccess) { | |||
&gcval); | fprintf(stderr, "failed to grab keyboard\n"); | |||
scrot_selection_destroy(); | ||||
if ((XGrabPointer | exit(EXIT_FAILURE); | |||
(disp, root, False, | } | |||
ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, | ||||
GrabModeAsync, root, cursor, CurrentTime) != GrabSuccess)) | ||||
gib_eprintf("couldn't grab pointer:"); | ||||
if ((XGrabKeyboard | ||||
(disp, root, False, GrabModeAsync, GrabModeAsync, | ||||
CurrentTime) != GrabSuccess)) | ||||
gib_eprintf("couldn't grab keyboard:"); | ||||
while (1) { | while (1) { | |||
/* handle events here */ | /* handle events here */ | |||
while (!done && XPending(disp)) { | while (!done && XPending(disp)) { | |||
XNextEvent(disp, &ev); | XNextEvent(disp, &ev); | |||
switch (ev.type) { | switch (ev.type) { | |||
case MotionNotify: | case MotionNotify: | |||
if (btn_pressed) { | if (btn_pressed) { | |||
if (rect_w) { | scrot_selection_motion_draw(rx, ry, ev.xbutton.x, ev.xbutton.y); | |||
/* re-draw the last rect to clear it */ | ||||
XDrawRectangle(disp, root, gc, rect_x, rect_y, rect_w, rect_h); | ||||
} else { | ||||
/* Change the cursor to show we're selecting a region */ | ||||
XChangeActivePointerGrab(disp, | ||||
ButtonMotionMask | ButtonReleaseMask, | ||||
cursor2, CurrentTime); | ||||
} | ||||
rect_x = rx; | ||||
rect_y = ry; | ||||
rect_w = ev.xmotion.x - rect_x; | ||||
rect_h = ev.xmotion.y - rect_y; | ||||
if (rect_w < 0) { | ||||
rect_x += rect_w; | ||||
rect_w = 0 - rect_w; | ||||
} | ||||
if (rect_h < 0) { | ||||
rect_y += rect_h; | ||||
rect_h = 0 - rect_h; | ||||
} | ||||
/* draw rectangle */ | ||||
XDrawRectangle(disp, root, gc, rect_x, rect_y, rect_w, rect_h); | ||||
XFlush(disp); | ||||
} | } | |||
break; | break; | |||
case ButtonPress: | case ButtonPress: | |||
btn_pressed = 1; | btn_pressed = 1; | |||
rx = ev.xbutton.x; | rx = ev.xbutton.x; | |||
ry = ev.xbutton.y; | ry = ev.xbutton.y; | |||
target = | target = scrot_get_window(disp, ev.xbutton.subwindow, ev.xbutton.x, ev | |||
scrot_get_window(disp, ev.xbutton.subwindow, ev.xbutton.x, | .xbutton.y); | |||
ev.xbutton.y); | ||||
if (target == None) | if (target == None) | |||
target = root; | target = root; | |||
break; | break; | |||
case ButtonRelease: | case ButtonRelease: | |||
done = 1; | done = 1; | |||
break; | break; | |||
case KeyPress: | case KeyPress: | |||
fprintf(stderr, "Key was pressed, aborting shot\n"); | ||||
done = 2; | if (!btn_pressed) { | |||
key_abort_shot: | ||||
fprintf(stderr, "Key was pressed, aborting shot\n"); | ||||
done = 2; | ||||
break; | ||||
} | ||||
KeySym *keysym = NULL; | ||||
int keycode; /*dummy*/ | ||||
keysym = XGetKeyboardMapping(disp, ev.xkey.keycode, 1, &keycode); | ||||
if (keysym == NULL) break; | ||||
switch (*keysym) { | ||||
case XK_Right: | ||||
if (++rx > scr->width) rx = scr->width; | ||||
break; | ||||
case XK_Left: | ||||
if (--rx < 0 ) rx = 0; | ||||
break; | ||||
case XK_Down: | ||||
if (++ry > scr->height) ry = scr->height; | ||||
break; | ||||
case XK_Up: | ||||
if (--ry < 0 ) ry = 0; | ||||
break; | ||||
default: | ||||
goto key_abort_shot; | ||||
} | ||||
XFree(keysym); | ||||
scrot_selection_motion_draw(rx, ry, ev.xbutton.x, ev.xbutton.y); | ||||
break; | break; | |||
case KeyRelease: | case KeyRelease: | |||
/* ignore */ | /* ignore */ | |||
break; | break; | |||
default: | default: | |||
break; | break; | |||
} | } | |||
} | } | |||
if (done) | if (done) | |||
break; | break; | |||
/* now block some */ | /* now block some */ | |||
FD_ZERO(&fdset); | FD_ZERO(&fdset); | |||
FD_SET(xfd, &fdset); | FD_SET(xfd, &fdset); | |||
errno = 0; | errno = 0; | |||
count = select(fdsize, &fdset, NULL, NULL, NULL); | count = select(fdsize, &fdset, NULL, NULL, NULL); | |||
if ((count < 0) | if ((count < 0) | |||
&& ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) | && ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) { | |||
gib_eprintf("Connection to X display lost"); | fprintf(stderr, "Connection to X display lost\n"); | |||
} | scrot_selection_destroy(); | |||
if (rect_w) { | exit(EXIT_FAILURE); | |||
XDrawRectangle(disp, root, gc, rect_x, rect_y, rect_w, rect_h); | } | |||
XFlush(disp); | ||||
} | } | |||
XUngrabPointer(disp, CurrentTime); | scrot_selection_draw(); | |||
XUngrabKeyboard(disp, CurrentTime); | XUngrabKeyboard(disp, CurrentTime); | |||
XFreeCursor(disp, cursor); | ||||
XFreeGC(disp, gc); | bool const isAreaSelect = (bool)(scrot_selection_get_rect()->w > 5); | |||
XSync(disp, True); | ||||
scrot_selection_destroy(); | ||||
if (done < 2) { | if (done < 2) { | |||
scrot_do_delay(); | scrot_do_delay(); | |||
if (rect_w > 5) { | if (isAreaSelect) { | |||
/* if a rect has been drawn, it's an area selection */ | /* if a rect has been drawn, it's an area selection */ | |||
rw = ev.xbutton.x - rx; | rw = ev.xbutton.x - rx; | |||
rh = ev.xbutton.y - ry; | rh = ev.xbutton.y - ry; | |||
if ((ev.xbutton.x + 1) == WidthOfScreen(scr)) { | ||||
++rw; | ||||
} | ||||
if ((ev.xbutton.y + 1) == HeightOfScreen(scr)) { | ||||
++rh; | ||||
} | ||||
if (rw < 0) { | if (rw < 0) { | |||
rx += rw; | rx += rw; | |||
rw = 0 - rw; | rw = 0 - rw; | |||
} | } | |||
if (rh < 0) { | if (rh < 0) { | |||
ry += rh; | ry += rh; | |||
rh = 0 - rh; | rh = 0 - rh; | |||
} | } | |||
} else { | ||||
Window child; | ||||
XWindowAttributes attr; | ||||
int stat; | ||||
// Not record pointer if there is a selection area because it is busy on t | ||||
hat, | ||||
// unless the delay option is used | ||||
if (opt.delay == 0) { | ||||
opt.pointer = 0; | ||||
} | ||||
} else { | ||||
/* else it's a window click */ | /* else it's a window click */ | |||
/* get geometry of window and use that */ | if (!scrot_get_geometry(target, &rx, &ry, &rw, &rh)) return NULL; | |||
/* get windowmanager frame of window */ | } | |||
if (target != root) { | scrot_nice_clip(&rx, &ry, &rw, &rh); | |||
unsigned int d, x; | ||||
int status; | if (! opt.silent) XBell(disp, 0); | |||
im = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); | ||||
status = XGetGeometry(disp, target, &root, &x, &x, &d, &d, &d, &d); | ||||
if (status != 0) { | if (opt.pointer == 1) | |||
Window rt, *children, parent; | scrot_grab_mouse_pointer(im, rx, ry); | |||
} | ||||
for (;;) { | return im; | |||
/* Find window manager frame. */ | } | |||
status = XQueryTree(disp, target, &rt, &parent, &children, &d); | ||||
if (status && (children != None)) | Imlib_Image | |||
XFree((char *) children); | scrot_grab_autoselect(void) | |||
if (!status || (parent == None) || (parent == rt)) | { | |||
break; | Imlib_Image im = NULL; | |||
target = parent; | int rx = opt.autoselect_x, ry = opt.autoselect_y, rw = opt.autoselect_w, rh = | |||
} | opt.autoselect_h; | |||
/* Get client window. */ | ||||
if (!opt.border) | scrot_do_delay(); | |||
target = scrot_get_client_window(disp, target); | scrot_nice_clip(&rx, &ry, &rw, &rh); | |||
XRaiseWindow(disp, target); | im = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); | |||
if (opt.pointer == 1) | ||||
scrot_grab_mouse_pointer(im, rx, ry); | ||||
return im; | ||||
} | ||||
/* clip rectangle nicely */ | ||||
void | ||||
scrot_nice_clip(int *rx, | ||||
int *ry, | ||||
int *rw, | ||||
int *rh) | ||||
{ | ||||
if (*rx < 0) { | ||||
*rw += *rx; | ||||
*rx = 0; | ||||
} | ||||
if (*ry < 0) { | ||||
*rh += *ry; | ||||
*ry = 0; | ||||
} | ||||
if ((*rx + *rw) > scr->width) | ||||
*rw = scr->width - *rx; | ||||
if ((*ry + *rh) > scr->height) | ||||
*rh = scr->height - *ry; | ||||
} | ||||
static Bool scrot_xevent_visibility(Display *dpy, XEvent *ev, XPointer arg) | ||||
{ | ||||
(void) dpy; // unused | ||||
Window *win = (Window*)arg; | ||||
return (ev->xvisibility.window == *win); | ||||
} | ||||
/* get geometry of window and use that */ | ||||
int | ||||
scrot_get_geometry(Window target, | ||||
int *rx, | ||||
int *ry, | ||||
int *rw, | ||||
int *rh) | ||||
{ | ||||
Window child; | ||||
XWindowAttributes attr; | ||||
int stat, frames = 0; | ||||
/* get windowmanager frame of window */ | ||||
if (target != root) { | ||||
unsigned int d; | ||||
int x; | ||||
int status; | ||||
status = XGetGeometry(disp, target, &root, &x, &x, &d, &d, &d, &d); | ||||
if (status != 0) { | ||||
Window rt, *children, parent; | ||||
XEvent ev; | ||||
for (;;) { | ||||
/* Find window manager frame. */ | ||||
status = XQueryTree(disp, target, &rt, &parent, &children, &d); | ||||
if (status && (children != None)) { | ||||
XFree((char *) children); | ||||
} | } | |||
if (!status || (parent == None) || (parent == rt)) { | ||||
break; | ||||
} | ||||
target = parent; | ||||
++frames; | ||||
} | ||||
/* Get client window. */ | ||||
if (!opt.border) { | ||||
target = scrot_get_client_window(disp, target); | ||||
} | } | |||
stat = XGetWindowAttributes(disp, target, &attr); | XRaiseWindow(disp, target); | |||
if ((stat == False) || (attr.map_state != IsViewable)) | ||||
return NULL; | ||||
rw = attr.width; | ||||
rh = attr.height; | ||||
XTranslateCoordinates(disp, target, root, 0, 0, &rx, &ry, &child); | ||||
} | ||||
/* clip rectangle nicely */ | ||||
if (rx < 0) { | ||||
rw += rx; | ||||
rx = 0; | ||||
} | ||||
if (ry < 0) { | ||||
rh += ry; | ||||
ry = 0; | ||||
} | ||||
if ((rx + rw) > scr->width) | ||||
rw = scr->width - rx; | ||||
if ((ry + rh) > scr->height) | ||||
rh = scr->height - ry; | ||||
XBell(disp, 0); | /* Give the WM time to update the hidden area of the window. | |||
im = gib_imlib_create_image_from_drawable(root, 0, rx, ry, rw, rh, 1); | Some windows never send the event, a time limit is placed. | |||
*/ | ||||
XSelectInput(disp, target, FocusChangeMask); | ||||
for(short i = 0; i < 30; ++i) { | ||||
if (XCheckIfEvent(disp, &ev, &scrot_xevent_visibility, (XPointer)&target | ||||
) == True) { | ||||
break; | ||||
} | ||||
usleep(2000); | ||||
} | ||||
} | ||||
} | } | |||
return im; | stat = XGetWindowAttributes(disp, target, &attr); | |||
if ((stat == False) || (attr.map_state != IsViewable)) | ||||
return 0; | ||||
*rw = attr.width; | ||||
*rh = attr.height; | ||||
XTranslateCoordinates(disp, target, root, 0, 0, rx, ry, &child); | ||||
/* Special case when the TWM emulates the border directly on the window. */ | ||||
if (opt.border == 1 && frames < 2 && attr.border_width > 0) { | ||||
*rw += attr.border_width * 2; | ||||
*rh += attr.border_width * 2; | ||||
*rx -= attr.border_width; | ||||
*ry -= attr.border_width; | ||||
} | ||||
return 1; | ||||
} | } | |||
Window | Window | |||
scrot_get_window(Display * display, | scrot_get_window(Display * display, | |||
Window window, | Window window, | |||
int x, | int x, | |||
int y) | int y) | |||
{ | { | |||
Window source, target; | Window source, target; | |||
skipping to change at line 417 | skipping to change at line 738 | |||
Imlib_Image im) | Imlib_Image im) | |||
{ | { | |||
char *c; | char *c; | |||
char buf[20]; | char buf[20]; | |||
char ret[4096]; | char ret[4096]; | |||
char strf[4096]; | char strf[4096]; | |||
char *tmp; | char *tmp; | |||
struct stat st; | struct stat st; | |||
ret[0] = '\0'; | ret[0] = '\0'; | |||
strftime(strf, 4095, str, tm); | ||||
if (strftime(strf, 4095, str, tm) == 0) { | ||||
fprintf(stderr, "strftime returned 0\n"); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
imlib_context_set_image(im); | ||||
for (c = strf; *c != '\0'; c++) { | for (c = strf; *c != '\0'; c++) { | |||
if (*c == '$') { | if (*c == '$') { | |||
c++; | c++; | |||
switch (*c) { | switch (*c) { | |||
case 'a': | ||||
gethostname(buf, sizeof(buf)); | ||||
strcat(ret, buf); | ||||
break; | ||||
case 'f': | case 'f': | |||
if (filename_im) | if (filename_im) | |||
strcat(ret, filename_im); | strcat(ret, filename_im); | |||
break; | break; | |||
case 'm': /* t was allready taken, so m as in mini */ | case 'm': /* t was already taken, so m as in mini */ | |||
if (filename_thumb) | if (filename_thumb) | |||
strcat(ret, filename_thumb); | strcat(ret, filename_thumb); | |||
break; | break; | |||
case 'n': | case 'n': | |||
if (filename_im) { | if (filename_im) { | |||
tmp = strrchr(filename_im, '/'); | tmp = strrchr(filename_im, '/'); | |||
if (tmp) | if (tmp) | |||
strcat(ret, tmp + 1); | strcat(ret, tmp + 1); | |||
else | else | |||
strcat(ret, filename_im); | strcat(ret, filename_im); | |||
} | } | |||
break; | break; | |||
case 'w': | case 'w': | |||
snprintf(buf, sizeof(buf), "%d", gib_imlib_image_get_width(im)); | snprintf(buf, sizeof(buf), "%d", imlib_image_get_width()); | |||
strcat(ret, buf); | strcat(ret, buf); | |||
break; | break; | |||
case 'h': | case 'h': | |||
snprintf(buf, sizeof(buf), "%d", gib_imlib_image_get_height(im)); | snprintf(buf, sizeof(buf), "%d", imlib_image_get_height()); | |||
strcat(ret, buf); | strcat(ret, buf); | |||
break; | break; | |||
case 's': | case 's': | |||
if (filename_im) { | if (filename_im) { | |||
if (!stat(filename_im, &st)) { | if (!stat(filename_im, &st)) { | |||
int size; | int size; | |||
size = st.st_size; | size = st.st_size; | |||
snprintf(buf, sizeof(buf), "%d", size); | snprintf(buf, sizeof(buf), "%d", size); | |||
strcat(ret, buf); | strcat(ret, buf); | |||
} else | } else | |||
strcat(ret, "[err]"); | strcat(ret, "[err]"); | |||
} | } | |||
break; | break; | |||
case 'p': | case 'p': | |||
snprintf(buf, sizeof(buf), "%d", | snprintf(buf, sizeof(buf), "%d", | |||
gib_imlib_image_get_width(im) * | imlib_image_get_width() * | |||
gib_imlib_image_get_height(im)); | imlib_image_get_height()); | |||
strcat(ret, buf); | strcat(ret, buf); | |||
break; | break; | |||
case 't': | case 't': | |||
strcat(ret, gib_imlib_image_format(im)); | tmp = imlib_image_format(); | |||
if (tmp) { | ||||
strcat(ret, tmp); | ||||
} | ||||
break; | break; | |||
case '$': | case '$': | |||
strcat(ret, "$"); | strcat(ret, "$"); | |||
break; | break; | |||
default: | default: | |||
strncat(ret, c, 1); | strncat(ret, c, 1); | |||
break; | break; | |||
} | } | |||
} else if (*c == '\\') { | } else if (*c == '\\') { | |||
c++; | c++; | |||
switch (*c) { | switch (*c) { | |||
case 'n': | case 'n': | |||
if (filename_im) | if (filename_im) | |||
strcat(ret, "\n"); | strcat(ret, "\n"); | |||
break; | break; | |||
default: | default: | |||
strncat(ret, c, 1); | strncat(ret, c, 1); | |||
break; | break; | |||
} | } | |||
} else | } else { | |||
strncat(ret, c, 1); | const size_t len = strlen(ret); | |||
ret[len] = *c; | ||||
ret[len + 1] = '\0'; | ||||
} | ||||
} | } | |||
return gib_estrdup(ret); | return strdup(ret); | |||
} | } | |||
Window | Window | |||
scrot_get_client_window(Display * display, | scrot_get_client_window(Display * display, | |||
Window target) | Window target) | |||
{ | { | |||
Atom state; | Atom state; | |||
Atom type = None; | Atom type = None; | |||
int format, status; | int format, status; | |||
unsigned char *data; | unsigned char *data; | |||
skipping to change at line 553 | skipping to change at line 889 | |||
child = children[i]; | child = children[i]; | |||
} | } | |||
for (i = 0; (i < number_children) && (child == None); i++) | for (i = 0; (i < number_children) && (child == None); i++) | |||
child = scrot_find_window_by_property(display, children[i], property); | child = scrot_find_window_by_property(display, children[i], property); | |||
if (children != None) | if (children != None) | |||
XFree(children); | XFree(children); | |||
return (child); | return (child); | |||
} | } | |||
Imlib_Image | Imlib_Image | |||
scrot_grab_stack_windows(void) | ||||
{ | ||||
if (XGetSelectionOwner(disp, XInternAtom(disp, "_NET_WM_CM_S0", False)) == N | ||||
one) { | ||||
fprintf(stderr, "Composite Manager is not running, required to use this | ||||
option.\n"); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
unsigned long nitems_return; | ||||
unsigned long bytes_after_return; | ||||
unsigned char *prop_return; | ||||
long long_offset = 0L; | ||||
long long_length = ~0L; | ||||
Bool delete = False; | ||||
int actual_format_return; | ||||
Atom actual_type_return; | ||||
Scrot_Imlib_List *list_images = NULL; | ||||
Imlib_Image im = NULL; | ||||
XImage *ximage = NULL; | ||||
XWindowAttributes attr; | ||||
unsigned long i = 0; | ||||
#define EWMH_CLIENT_LIST "_NET_CLIENT_LIST" // spec EWMH | ||||
Atom atom_prop = XInternAtom(disp, EWMH_CLIENT_LIST, False); | ||||
Atom atom_type = AnyPropertyType; | ||||
int result = XGetWindowProperty(disp, root, atom_prop, long_offset, long_len | ||||
gth, | ||||
delete, atom_type, &actual_type_return, &actual_ | ||||
format_return, | ||||
&nitems_return, &bytes_after_return, &prop_retur | ||||
n); | ||||
if (result != Success || nitems_return == 0) { | ||||
fprintf(stderr, "Failed XGetWindowProperty: " EWMH_CLIENT_LIST "\n"); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
XCompositeRedirectSubwindows(disp, root, CompositeRedirectAutomatic); | ||||
for (i = 0; i < nitems_return; i++) { | ||||
Window win = *((Window*)prop_return + i); | ||||
if (0 == XGetWindowAttributes(disp, win, &attr)) { | ||||
fprintf(stderr, "Failed XGetWindowAttributes\n"); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
/* Only visible windows */ | ||||
if (attr.map_state != IsViewable) { | ||||
continue; | ||||
} | ||||
if (!scrot_match_window_class_name(win)) { | ||||
continue; | ||||
} | ||||
ximage = XGetImage(disp, win, 0, 0, attr.width, attr.height, AllPlanes, | ||||
ZPixmap); | ||||
if (ximage == NULL) { | ||||
fprintf(stderr, "Failed XGetImage: Window id 0x%lx.\n", win); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
im = imlib_create_image_from_ximage(ximage, NULL, attr.x, attr.y, attr.w | ||||
idth, attr.height, 1); | ||||
XFree(ximage); | ||||
list_images = append_to_scrot_imlib(list_images, im); | ||||
} | ||||
return stalk_image_concat(list_images); | ||||
} | ||||
Imlib_Image | ||||
scrot_grab_shot_multi(void) | scrot_grab_shot_multi(void) | |||
{ | { | |||
int screens; | int screens; | |||
int i; | int i; | |||
char *dispstr, *subdisp; | char *dispstr, *subdisp; | |||
char newdisp[255]; | char newdisp[255]; | |||
gib_list *images = NULL; | Scrot_Imlib_List *images = NULL; | |||
Imlib_Image ret = NULL; | Imlib_Image ret = NULL; | |||
screens = ScreenCount(disp); | screens = ScreenCount(disp); | |||
if (screens < 2) | if (screens < 2) | |||
return scrot_grab_shot(); | return scrot_grab_shot(); | |||
dispstr = DisplayString(disp); | subdisp = strdup(DisplayString(disp)); | |||
subdisp = gib_estrdup(DisplayString(disp)); | ||||
for (i = 0; i < screens; i++) { | for (i = 0; i < screens; i++) { | |||
dispstr = strchr(subdisp, ':'); | dispstr = strchr(subdisp, ':'); | |||
if (dispstr) { | if (dispstr) { | |||
dispstr = strchr(dispstr, '.'); | dispstr = strchr(dispstr, '.'); | |||
if (NULL != dispstr) | if (NULL != dispstr) | |||
*dispstr = '\0'; | *dispstr = '\0'; | |||
} | } | |||
snprintf(newdisp, sizeof(newdisp), "%s.%d", subdisp, i); | snprintf(newdisp, sizeof(newdisp), "%s.%d", subdisp, i); | |||
init_x_and_imlib(newdisp, i); | init_x_and_imlib(newdisp, i); | |||
ret = | ret = | |||
gib_imlib_create_image_from_drawable(root, 0, 0, 0, scr->width, | imlib_create_image_from_drawable(0, 0, 0, scr->width, | |||
scr->height, 1); | scr->height, 1); | |||
images = gib_list_add_end(images, ret); | images = append_to_scrot_imlib(images, ret); | |||
} | } | |||
free(subdisp); | free(subdisp); | |||
ret = stalk_image_concat(images); | ret = stalk_image_concat(images); | |||
return ret; | return ret; | |||
} | } | |||
Imlib_Image | Imlib_Image | |||
stalk_image_concat(gib_list * images) | stalk_image_concat(Scrot_Imlib_List * images) | |||
{ | { | |||
int tot_w = 0, max_h = 0, w, h; | int tot_w = 0, max_h = 0, w, h; | |||
int x = 0; | int x = 0; | |||
gib_list *l, *item; | Scrot_Imlib_List *l, *item; | |||
Imlib_Image ret, im; | Imlib_Image ret, im; | |||
if (gib_list_length(images) == 0) | if (is_scrot_imlib_list_empty(images)) | |||
return NULL; | return NULL; | |||
l = images; | l = images; | |||
while (l) { | while (l) { | |||
im = (Imlib_Image) l->data; | im = (Imlib_Image) l->data; | |||
h = gib_imlib_image_get_height(im); | imlib_context_set_image(im); | |||
w = gib_imlib_image_get_width(im); | h = imlib_image_get_height(); | |||
w = imlib_image_get_width(); | ||||
if (h > max_h) | if (h > max_h) | |||
max_h = h; | max_h = h; | |||
tot_w += w; | tot_w += w; | |||
l = l->next; | l = l->next; | |||
} | } | |||
ret = imlib_create_image(tot_w, max_h); | ret = imlib_create_image(tot_w, max_h); | |||
gib_imlib_image_fill_rectangle(ret, 0, 0, tot_w, max_h, 255, 0, 0, 0); | imlib_context_set_image(ret); | |||
imlib_context_set_color(255, 0, 0, 0); | ||||
imlib_image_fill_rectangle(0, 0, tot_w, max_h); | ||||
l = images; | l = images; | |||
while (l) { | while (l) { | |||
im = (Imlib_Image) l->data; | im = (Imlib_Image) l->data; | |||
item = l; | item = l; | |||
l = l->next; | l = l->next; | |||
h = gib_imlib_image_get_height(im); | imlib_context_set_image(im); | |||
w = gib_imlib_image_get_width(im); | h = imlib_image_get_height(); | |||
gib_imlib_blend_image_onto_image(ret, im, 0, 0, 0, w, h, x, 0, w, h, 1, 0, | w = imlib_image_get_width(); | |||
0); | imlib_context_set_image(ret); | |||
imlib_context_set_anti_alias(0); | ||||
imlib_context_set_dither(1); | ||||
imlib_context_set_blend(0); | ||||
imlib_context_set_angle(0); | ||||
imlib_blend_image_onto_image(im, 0, 0, 0, w, h, x, 0, w, h); | ||||
x += w; | x += w; | |||
gib_imlib_free_image_and_decache(im); | imlib_context_set_image(im); | |||
imlib_free_image_and_decache(); | ||||
free(item); | free(item); | |||
} | } | |||
return ret; | return ret; | |||
} | } | |||
End of changes. 64 change blocks. | ||||
172 lines changed or deleted | 607 lines changed or added |