main.c (scrot-1.6.tar.bz2) | : | main.c (scrot-1.7.tar.bz2) | ||
---|---|---|---|---|
/* main.c | /* main.c | |||
Copyright 1999-2000 Tom Gilbert <tom@linuxbrit.co.uk, | Copyright 1999-2000 Tom Gilbert <tom@linuxbrit.co.uk, | |||
gilbertt@linuxbrit.co.uk, | gilbertt@linuxbrit.co.uk, | |||
scrot_sucks@linuxbrit.co.uk> | scrot_sucks@linuxbrit.co.uk> | |||
Copyright 2009 James Cameron <quozl@us.netrek.org> | Copyright 2009 James Cameron <quozl@us.netrek.org> | |||
Copyright 2010 Ibragimov Rinat <ibragimovrinat@mail.ru> | Copyright 2010 Ibragimov Rinat <ibragimovrinat@mail.ru> | |||
Copyright 2017 Stoney Sauce <stoneysauce@gmail.com> | Copyright 2017 Stoney Sauce <stoneysauce@gmail.com> | |||
Copyright 2019-2021 Daniel T. Borelli <daltomi@disroot.org> | Copyright 2019-2021 Daniel T. Borelli <danieltborelli@gmail.com> | |||
Copyright 2019 Jade Auer <jade@trashwitch.dev> | Copyright 2019 Jade Auer <jade@trashwitch.dev> | |||
Copyright 2020 blockparole | Copyright 2020 blockparole | |||
Copyright 2020 Cungsten Tarbide <ctarbide@tuta.io> | Copyright 2020 Cungsten Tarbide <ctarbide@tuta.io> | |||
Copyright 2020 Hinigatsu <hinigatsu@protonmail.com> | Copyright 2020 Hinigatsu <hinigatsu@protonmail.com> | |||
Copyright 2020 nothub | Copyright 2020 nothub | |||
Copyright 2020 Sean Brennan <zettix1@gmail.com> | Copyright 2020 Sean Brennan <zettix1@gmail.com> | |||
Copyright 2021 c0dev0id <sh+github@codevoid.de> | Copyright 2021 c0dev0id <sh+github@codevoid.de> | |||
Copyright 2021 Christopher R. Nelson <christopher.nelson@languidnights.com> | Copyright 2021 Christopher R. Nelson <christopher.nelson@languidnights.com> | |||
Copyright 2021 Guilherme Janczak <guilherme.janczak@yandex.com> | Copyright 2021 Guilherme Janczak <guilherme.janczak@yandex.com> | |||
Copyright 2021 IFo Hancroft <contact@ifohancroft.com> | ||||
Copyright 2021 Peter Wu <peterwu@hotmail.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 42 | skipping to change at line 44 | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
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 "slist.h" | ||||
#include <assert.h> | #include <assert.h> | |||
/* atexit register func. */ | /* atexit register func. */ | |||
static void uninit_x_and_imlib(void) | static void uninitXAndImlib(void) | |||
{ | { | |||
if (disp) { | if (opt.note) | |||
XCloseDisplay(disp); | scrotNoteFree(); | |||
disp = NULL; | ||||
} | if (disp) { | |||
XCloseDisplay(disp); | ||||
disp = NULL; | ||||
} | ||||
} | } | |||
// It assumes that the local variable 'main.c:Imlib_Image image' is in context | // It assumes that the local variable 'main.c:Imlib_Image image' is in context | |||
static void apply_filter_if_required(void) | static void applyFilterIfRequired(void) | |||
{ | { | |||
if (opt.script != NULL) { | if (opt.script) | |||
imlib_apply_filter(opt.script); | imlib_apply_filter(opt.script); | |||
} | } | |||
} | ||||
int | ||||
main(int argc, | ||||
char **argv) | ||||
{ | ||||
Imlib_Image image; | ||||
Imlib_Image thumbnail; | ||||
Imlib_Load_Error err; | ||||
char *filename_im = NULL, *filename_thumb = NULL; | ||||
char *have_extension = NULL; | ||||
time_t t; | ||||
struct tm *tm; | ||||
init_parse_options(argc, argv); | ||||
init_x_and_imlib(opt.display, 0); | ||||
atexit(uninit_x_and_imlib); | ||||
if (!opt.output_file) { | ||||
opt.output_file = strdup("%Y-%m-%d-%H%M%S_$wx$h_scrot.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.focused) | ||||
image = scrot_grab_focused(); | ||||
else if (opt.select) | ||||
image = scrot_sel_and_grab_image(); | ||||
else if (opt.autoselect) | ||||
image = scrot_grab_autoselect(); | ||||
else | ||||
{ | ||||
scrot_do_delay(); | ||||
if (opt.multidisp) { | ||||
image = scrot_grab_shot_multi(); | ||||
} else if (opt.stack) { | ||||
image = scrot_grab_stack_windows(); | ||||
} else { | ||||
image = scrot_grab_shot(); | ||||
} | ||||
} | ||||
if (opt.note != NULL) | int main(int argc, char** argv) | |||
scrot_note_draw(image); | { | |||
Imlib_Image image; | ||||
Imlib_Image thumbnail; | ||||
Imlib_Load_Error imErr; | ||||
char* filenameIM = NULL; | ||||
char* filenameThumb = NULL; | ||||
if (!image) { | char* haveExtension = NULL; | |||
fprintf(stderr, "no image grabbed: %s", strerror(errno)); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
time(&t); /* Get the time directly after the screenshot */ | ||||
tm = localtime(&t); | ||||
imlib_context_set_image(image); | ||||
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); | ||||
scrot_check_if_overwrite_file(&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) | ||||
{ | ||||
int cwidth, cheight; | ||||
int twidth, theight; | ||||
cwidth = imlib_image_get_width(); | ||||
cheight = imlib_image_get_height(); | ||||
/* Geometry based thumb size */ | ||||
if (opt.thumb_width || opt.thumb_height) | ||||
{ | ||||
if (!opt.thumb_width) | ||||
{ | ||||
twidth = cwidth * opt.thumb_height / cheight; | ||||
theight = opt.thumb_height; | ||||
} | ||||
else if (!opt.thumb_height) | ||||
{ | ||||
twidth = opt.thumb_width; | ||||
theight = cheight * opt.thumb_width / cwidth; | ||||
} | ||||
else | ||||
{ | ||||
twidth = opt.thumb_width; | ||||
theight = opt.thumb_height; | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
twidth = cwidth * opt.thumb / 100; | ||||
theight = cheight * opt.thumb / 100; | ||||
} | ||||
imlib_context_set_anti_alias(1); | time_t t; | |||
thumbnail = | struct tm* tm; | |||
imlib_create_cropped_scaled_image(0, 0, cwidth, cheight, | ||||
twidth, theight); | optionsParse(argc, argv); | |||
if (thumbnail == NULL) { | ||||
fprintf(stderr, "Unable to create scaled Image: %s\n", strerror(errno)); | initXAndImlib(opt.display, 0); | |||
exit(EXIT_FAILURE); | ||||
} | atexit(uninitXAndImlib); | |||
else | ||||
{ | if (!opt.outputFile) { | |||
if (opt.note != NULL) | opt.outputFile = strdup("%Y-%m-%d-%H%M%S_$wx$h_scrot.png"); | |||
scrot_note_draw(image); | opt.thumbFile = strdup("%Y-%m-%d-%H%M%S_$wx$h_scrot-thumb.png"); | |||
} else { | ||||
if (opt.thumb) | ||||
opt.thumbFile = optionsNameThumbnail(opt.outputFile); | ||||
scrotHaveFileExtension(opt.outputFile, &haveExtension); | ||||
} | ||||
if (opt.focused) | ||||
image = scrotGrabFocused(); | ||||
else if (opt.selection.mode & SELECTION_MODE_ANY) | ||||
image = scrotSelectionSelectMode(); | ||||
else if (opt.autoselect) | ||||
image = scrotGrabAutoselect(); | ||||
else { | ||||
scrotDoDelay(); | ||||
if (opt.multidisp) | ||||
image = scrotGrabShotMulti(); | ||||
else if (opt.stack) | ||||
image = scrotGrabStackWindows(); | ||||
else | ||||
image = scrotGrabShot(); | ||||
} | ||||
if (!image) | ||||
err(EXIT_FAILURE, "no image grabbed"); | ||||
if (opt.note) | ||||
scrotNoteDraw(image); | ||||
time(&t); /* Get the time directly after the screenshot */ | ||||
tm = localtime(&t); | ||||
scrot_have_file_extension(opt.thumb_file, &have_extension); | imlib_context_set_image(image); | |||
imlib_image_attach_data_value("quality", NULL, opt.quality, NULL); | ||||
if (!have_extension) { | if (!haveExtension) | |||
imlib_context_set_image(thumbnail); | ||||
imlib_image_set_format("png"); | imlib_image_set_format("png"); | |||
} | ||||
filename_thumb = im_printf(opt.thumb_file, tm, NULL, NULL, thumbnail); | filenameIM = imPrintf(opt.outputFile, tm, NULL, NULL, image); | |||
scrot_check_if_overwrite_file(&filename_thumb); | scrotCheckIfOverwriteFile(&filenameIM); | |||
apply_filter_if_required(); | applyFilterIfRequired(); | |||
imlib_save_image_with_error_return(filenameIM, &imErr); | ||||
if (imErr) | ||||
err(EXIT_FAILURE, "Saving to file %s failed", filenameIM); | ||||
if (opt.thumb) { | ||||
int cwidth, cheight; | ||||
int twidth, theight; | ||||
cwidth = imlib_image_get_width(); | ||||
cheight = imlib_image_get_height(); | ||||
/* Geometry based thumb size */ | ||||
if (opt.thumbWidth || opt.thumbHeight) { | ||||
if (!opt.thumbWidth) { | ||||
twidth = cwidth * opt.thumbHeight / cheight; | ||||
theight = opt.thumbHeight; | ||||
} else if (!opt.thumbHeight) { | ||||
twidth = opt.thumbWidth; | ||||
theight = cheight * opt.thumbWidth / cwidth; | ||||
} else { | ||||
twidth = opt.thumbWidth; | ||||
theight = opt.thumbHeight; | ||||
} | ||||
} else { | ||||
twidth = cwidth * opt.thumb / 100; | ||||
theight = cheight * opt.thumb / 100; | ||||
} | ||||
imlib_context_set_anti_alias(1); | ||||
thumbnail = imlib_create_cropped_scaled_image(0, 0, cwidth, cheight, | ||||
twidth, theight); | ||||
if (!thumbnail) | ||||
err(EXIT_FAILURE, "unable to create thumbnail"); | ||||
else { | ||||
scrotHaveFileExtension(opt.thumbFile, &haveExtension); | ||||
imlib_context_set_image(thumbnail); | ||||
if (!haveExtension) | ||||
imlib_image_set_format("png"); | ||||
filenameThumb = imPrintf(opt.thumbFile, tm, NULL, NULL, thumbnail); | ||||
scrotCheckIfOverwriteFile(&filenameThumb); | ||||
imlib_save_image_with_error_return(filenameThumb, &imErr); | ||||
imlib_free_image_and_decache(); | ||||
if (imErr) | ||||
err(EXIT_FAILURE, "Saving thumbnail %s failed", filenameThumb); | ||||
} | ||||
} | ||||
if (opt.exec) | ||||
scrotExecApp(image, tm, filenameIM, filenameThumb); | ||||
imlib_context_set_image(image); | ||||
imlib_free_image_and_decache(); | ||||
return 0; | ||||
} | ||||
imlib_save_image_with_error_return(filename_thumb, &err); | void scrotDoDelay(void) | |||
if (err) { | { | |||
fprintf(stderr, "Saving thumbnail %s failed: %s\n", filename_thumb, stre | if (opt.delay) { | |||
rror(errno)); | if (opt.countdown) { | |||
exit(EXIT_FAILURE); | int i; | |||
} | ||||
printf("Taking shot in %d.. ", opt.delay); | ||||
fflush(stdout); | ||||
sleep(1); | ||||
for (i = opt.delay - 1; i > 0; i--) { | ||||
printf("%d.. ", i); | ||||
fflush(stdout); | ||||
sleep(1); | ||||
} | ||||
printf("0.\n"); | ||||
fflush(stdout); | ||||
} else | ||||
sleep(opt.delay); | ||||
} | } | |||
} | ||||
if (opt.exec) | ||||
scrot_exec_app(image, tm, filename_im, filename_thumb); | ||||
imlib_free_image_and_decache(); | ||||
return 0; | ||||
} | ||||
void | ||||
scrot_do_delay(void) | ||||
{ | ||||
if (opt.delay) { | ||||
if (opt.countdown) { | ||||
int i; | ||||
printf("Taking shot in %d.. ", opt.delay); | ||||
fflush(stdout); | ||||
sleep(1); | ||||
for (i = opt.delay - 1; i > 0; i--) { | ||||
printf("%d.. ", i); | ||||
fflush(stdout); | ||||
sleep(1); | ||||
} | ||||
printf("0.\n"); | ||||
fflush(stdout); | ||||
} else | ||||
sleep(opt.delay); | ||||
} | ||||
} | } | |||
size_t scrot_have_file_extension(char const *filename, char **ext) | size_t scrotHaveFileExtension(char const* filename, char** ext) | |||
{ | { | |||
*ext = strrchr(filename, '.'); | *ext = strrchr(filename, '.'); | |||
if (*ext) { | if (*ext) | |||
return strlen(*ext); | return strlen(*ext); | |||
} | ||||
return 0; | return 0; | |||
} | } | |||
void scrot_check_if_overwrite_file(char **filename) | void scrotCheckIfOverwriteFile(char** filename) | |||
{ | { | |||
char *curfile = *filename; | if (opt.overwrite) | |||
return; | ||||
if (opt.overwrite == 1) return; | if (access(*filename, F_OK) == -1) | |||
return; | ||||
if (access(curfile, F_OK) == -1) return; | const size_t maxCounter = 999; | |||
size_t counter = 0; | ||||
char* ext = NULL; | ||||
size_t extLength = 0; | ||||
const size_t slen = strlen(*filename); | ||||
size_t nalloc = slen + 4 + 1; // _000 + NUL byte | ||||
char fmt[5]; | ||||
char* newName = NULL; | ||||
const size_t max_counter = 999; | extLength = scrotHaveFileExtension(*filename, &ext); | |||
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 += extLength; // .ext | ||||
if (ext) | newName = calloc(nalloc, sizeof(*newName)); | |||
nalloc += ext_len; // .ext | memcpy(newName, *filename, slen); | |||
newname = calloc(nalloc, sizeof(char)); | do { | |||
char* ptr = newName + slen; | ||||
if (ext) | snprintf(fmt, sizeof(fmt), "_%03zu", counter++); | |||
// exclude ext | ||||
memcpy(newname, curfile, slen - ext_len); | ||||
else | ||||
memcpy(newname, curfile, slen); | ||||
do { | if(ext) { | |||
snprintf(fmt, 5, "_%03zu", counter++); | ptr -= extLength; | |||
memcpy(ptr, fmt, sizeof(fmt)); | ||||
memcpy(ptr + sizeof(fmt) - 1, ext, extLength); | ||||
} else | ||||
memcpy(ptr, fmt, sizeof(fmt)); | ||||
} while ((counter < maxCounter) && !access(newName, F_OK)); | ||||
if (!ext) { | assert(newName[nalloc - 1] == '\0'); | |||
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); | free(*filename); | |||
*filename = newname; | *filename = newName; | |||
if (counter == max_counter) { | if (counter == maxCounter) { | |||
fprintf(stderr, "scrot can no longer generate new file names.\n" | errx(EXIT_FAILURE, "scrot can no longer generate new file names.\n" | |||
"The last attempt is %s\n", newname); | "The last attempt is %s", newName); | |||
free(newname); | } | |||
exit(EXIT_FAILURE); | ||||
} | ||||
} | } | |||
int scrot_match_window_class_name(Window target) | int scrotMatchWindowClassName(Window target) | |||
{ | { | |||
assert(disp != NULL); | assert(disp != NULL); | |||
const int NOT_MATCH = 0; | const int NOT_MATCH = 0; | |||
const int MATCH = 1; | const int MATCH = 1; | |||
/* By default all class names match since window_class_name by default is NU LL*/ | /* By default all class names match since windowClassName by default is NULL */ | |||
int retval = MATCH; | int retval = MATCH; | |||
if (opt.window_class_name == NULL) { | if (!opt.windowClassName) | |||
return retval; | return retval; | |||
} | ||||
XClassHint classHint; | XClassHint classHint; | |||
retval = NOT_MATCH; // window_class_name != NULL, by default NOT_MATCH | retval = NOT_MATCH; // windowClassName != NULL, by default NOT_MATCH | |||
if (XGetClassHint(disp, target, &classHint) != BadWindow) { | if (XGetClassHint(disp, target, &classHint) != BadWindow) { | |||
retval = options_cmp_window_class_name(classHint.res_class); | retval = optionsCompareWindowClassName(classHint.res_class); | |||
XFree(classHint.res_name); | XFree(classHint.res_name); | |||
XFree(classHint.res_class); | XFree(classHint.res_class); | |||
} | } | |||
return retval; | return retval; | |||
} | } | |||
void | void scrotGrabMousePointer(const Imlib_Image image, | |||
scrot_grab_mouse_pointer(const Imlib_Image image, | const int xOffset, const int yOffset) | |||
const int ix_off, const int iy_off) | { | |||
{ | XFixesCursorImage* xcim = XFixesGetCursorImage(disp); | |||
XFixesCursorImage *xcim = XFixesGetCursorImage(disp); | ||||
if (!xcim) { | ||||
const unsigned short width = xcim->width; | warnx("Failed to get mouse cursor image."); | |||
const unsigned short height = xcim->height; | return; | |||
const int x = (xcim->x - xcim->xhot) - ix_off; | } | |||
const int y = (xcim->y - xcim->yhot) - iy_off; | ||||
DATA32 *pixels = NULL; | const unsigned short width = xcim->width; | |||
const unsigned short height = xcim->height; | ||||
const int x = (xcim->x - xcim->xhot) - xOffset; | ||||
const int y = (xcim->y - xcim->yhot) - yOffset; | ||||
DATA32* pixels = NULL; | ||||
#ifdef __i386__ | #ifdef __i386__ | |||
pixels = (DATA32*)xcim->pixels; | pixels = (DATA32*)xcim->pixels; | |||
#else | #else | |||
DATA32 data[width * height * 4]; | DATA32 data[width * height * 4]; | |||
unsigned int i; | unsigned int i; | |||
for (i = 0; i < (width * height); i++) | for (i = 0; i < (width * height); i++) | |||
((DATA32*)data)[i] = (DATA32)xcim->pixels[i]; | data[i] = (DATA32)xcim->pixels[i]; | |||
pixels = data; | pixels = data; | |||
#endif | #endif | |||
Imlib_Image imcursor = imlib_create_image_using_data(width, height, pixels); | Imlib_Image imcursor = imlib_create_image_using_data(width, height, pixels); | |||
XFree(xcim); | XFree(xcim); | |||
if (!imcursor) { | if (!imcursor) | |||
fprintf(stderr, "scrot_grab_mouse_pointer: Failed create image using data.\ | errx(EXIT_FAILURE, "scrotGrabMousePointer: Failed create image using dat | |||
n"); | a."); | |||
exit(EXIT_FAILURE); | ||||
} | ||||
imlib_context_set_image(imcursor); | imlib_context_set_image(imcursor); | |||
imlib_image_set_has_alpha(1); | imlib_image_set_has_alpha(1); | |||
imlib_context_set_image(image); | imlib_context_set_image(image); | |||
imlib_blend_image_onto_image(imcursor, 0, 0, 0, width, height, x, y, width, he | imlib_blend_image_onto_image(imcursor, 0, 0, 0, width, height, x, y, width, | |||
ight); | height); | |||
imlib_context_set_image(imcursor); | imlib_context_set_image(imcursor); | |||
imlib_free_image(); | imlib_free_image(); | |||
} | } | |||
Imlib_Image | Imlib_Image scrotGrabShot(void) | |||
scrot_grab_shot(void) | ||||
{ | { | |||
Imlib_Image im; | Imlib_Image im; | |||
if (! opt.silent) XBell(disp, 0); | if (!opt.silent) | |||
XBell(disp, 0); | ||||
im = | im = imlib_create_image_from_drawable(0, 0, 0, scr->width, | |||
imlib_create_image_from_drawable(0, 0, 0, scr->width, | scr->height, 1); | |||
scr->height, 1); | if (opt.pointer) | |||
if (opt.pointer == 1) | scrotGrabMousePointer(im, 0, 0); | |||
scrot_grab_mouse_pointer(im, 0, 0); | ||||
return im; | return im; | |||
} | } | |||
void | void scrotExecApp(Imlib_Image image, struct tm* tm, | |||
scrot_exec_app(Imlib_Image image, struct tm *tm, | char* filenameIM, char* filenameThumb) | |||
char *filename_im, char *filename_thumb) | ||||
{ | { | |||
char *execstr; | char* execStr; | |||
int ret; | int ret; | |||
execstr = im_printf(opt.exec, tm, filename_im, filename_thumb, image); | execStr = imPrintf(opt.exec, tm, filenameIM, filenameThumb, image); | |||
errno = 0; | errno = 0; | |||
ret = system(execstr); | ret = system(execStr); | |||
if (ret == -1) { | if (ret == -1) | |||
fprintf(stderr, "The child process could not be created: %s\n", strerror(err | warn("The child process could not be created"); | |||
no)); | else if (WEXITSTATUS(ret) == 127) | |||
} else if (WEXITSTATUS(ret) == 127) { | warnx("scrot could not execute the command: %s", execStr); | |||
fprintf(stderr, "scrot could not execute the command: %s.\n", execstr); | ||||
} | ||||
exit(0); | exit(0); | |||
} | } | |||
Imlib_Image | Imlib_Image scrotGrabFocused(void) | |||
scrot_grab_focused(void) | ||||
{ | { | |||
Imlib_Image im = NULL; | Imlib_Image im = NULL; | |||
int rx = 0, ry = 0, rw = 0, rh = 0; | int rx = 0, ry = 0, rw = 0, rh = 0; | |||
Window target = None; | Window target = None; | |||
int ignored; | int ignored; | |||
scrot_do_delay(); | scrotDoDelay(); | |||
XGetInputFocus(disp, &target, &ignored); | XGetInputFocus(disp, &target, &ignored); | |||
if (!scrot_get_geometry(target, &rx, &ry, &rw, &rh)) return NULL; | if (!scrotGetGeometry(target, &rx, &ry, &rw, &rh)) | |||
scrot_nice_clip(&rx, &ry, &rw, &rh); | return NULL; | |||
im = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); | scrotNiceClip(&rx, &ry, &rw, &rh); | |||
if (opt.pointer == 1) | im = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); | |||
scrot_grab_mouse_pointer(im, rx, ry); | if (opt.pointer) | |||
return im; | scrotGrabMousePointer(im, rx, ry); | |||
return im; | ||||
} | } | |||
Imlib_Image | Imlib_Image scrotGrabAutoselect(void) | |||
scrot_sel_and_grab_image(void) | ||||
{ | { | |||
Imlib_Image im = NULL; | Imlib_Image im = NULL; | |||
static int xfd = 0; | int rx = opt.autoselectX, ry = opt.autoselectY, rw = opt.autoselectW, rh = o | |||
static int fdsize = 0; | pt.autoselectH; | |||
XEvent ev; | ||||
fd_set fdset; | ||||
int count = 0, done = 0; | ||||
int rx = 0, ry = 0, rw = 0, rh = 0, btn_pressed = 0; | ||||
Window target = None; | ||||
Status ret; | ||||
scrot_selection_create(); | scrotDoDelay(); | |||
scrotNiceClip(&rx, &ry, &rw, &rh); | ||||
xfd = ConnectionNumber(disp); | im = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); | |||
fdsize = xfd + 1; | if (opt.pointer) | |||
scrotGrabMousePointer(im, rx, ry); | ||||
return im; | ||||
} | ||||
ret = XGrabKeyboard(disp, root, False, GrabModeAsync, GrabModeAsync, CurrentTi | /* Clip rectangle nicely */ | |||
me); | void scrotNiceClip(int* rx, int* ry, int* rw, int* rh) | |||
if (ret == AlreadyGrabbed) { | { | |||
int attempts = 20; | if (*rx < 0) { | |||
struct timespec delay = {0, 50 * 1000L * 1000L}; | *rw += *rx; | |||
do { | *rx = 0; | |||
nanosleep(&delay, NULL); | ||||
ret = XGrabKeyboard(disp, root, False, GrabModeAsync, GrabModeAsync, Curre | ||||
ntTime); | ||||
} while (--attempts > 0 && ret == AlreadyGrabbed); | ||||
} | ||||
if (ret != GrabSuccess) { | ||||
fprintf(stderr, "failed to grab keyboard\n"); | ||||
scrot_selection_destroy(); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
while (1) { | ||||
/* handle events here */ | ||||
while (!done && XPending(disp)) { | ||||
XNextEvent(disp, &ev); | ||||
switch (ev.type) { | ||||
case MotionNotify: | ||||
if (btn_pressed) { | ||||
scrot_selection_motion_draw(rx, ry, ev.xbutton.x, ev.xbutton.y); | ||||
} | ||||
break; | ||||
case ButtonPress: | ||||
btn_pressed = 1; | ||||
rx = ev.xbutton.x; | ||||
ry = ev.xbutton.y; | ||||
target = scrot_get_window(disp, ev.xbutton.subwindow, ev.xbutton.x, ev | ||||
.xbutton.y); | ||||
if (target == None) | ||||
target = root; | ||||
break; | ||||
case ButtonRelease: | ||||
done = 1; | ||||
break; | ||||
case KeyPress: | ||||
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; | ||||
case KeyRelease: | ||||
/* ignore */ | ||||
break; | ||||
default: | ||||
break; | ||||
} | ||||
} | } | |||
if (done) | if (*ry < 0) { | |||
break; | *rh += *ry; | |||
*ry = 0; | ||||
/* now block some */ | ||||
FD_ZERO(&fdset); | ||||
FD_SET(xfd, &fdset); | ||||
errno = 0; | ||||
count = select(fdsize, &fdset, NULL, NULL, NULL); | ||||
if ((count < 0) | ||||
&& ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) { | ||||
fprintf(stderr, "Connection to X display lost\n"); | ||||
scrot_selection_destroy(); | ||||
exit(EXIT_FAILURE); | ||||
} | } | |||
} | if ((*rx + *rw) > scr->width) | |||
scrot_selection_draw(); | *rw = scr->width - *rx; | |||
if ((*ry + *rh) > scr->height) | ||||
XUngrabKeyboard(disp, CurrentTime); | *rh = scr->height - *ry; | |||
} | ||||
bool const isAreaSelect = (bool)(scrot_selection_get_rect()->w > 5); | ||||
scrot_selection_destroy(); | static Bool scrotXEventVisibility(Display* dpy, XEvent* ev, XPointer arg) | |||
{ | ||||
(void)dpy; // unused | ||||
Window* win = (Window*)arg; | ||||
return (ev->xvisibility.window == *win); | ||||
} | ||||
if (done < 2) { | /* Get geometry of window and use that */ | |||
scrot_do_delay(); | int scrotGetGeometry(Window target, int* rx, int* ry, int* rw, int* rh) | |||
if (isAreaSelect) { | { | |||
/* if a rect has been drawn, it's an area selection */ | Window child; | |||
rw = ev.xbutton.x - rx; | XWindowAttributes attr; | |||
rh = ev.xbutton.y - ry; | int stat, frames = 0; | |||
if ((ev.xbutton.x + 1) == WidthOfScreen(scr)) { | ||||
++rw; | ||||
} | ||||
if ((ev.xbutton.y + 1) == HeightOfScreen(scr)) { | ||||
++rh; | ||||
} | ||||
if (rw < 0) { | ||||
rx += rw; | ||||
rw = 0 - rw; | ||||
} | ||||
if (rh < 0) { | ||||
ry += rh; | ||||
rh = 0 - rh; | ||||
} | ||||
// 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 { | /* Get windowmanager frame of window */ | |||
/* else it's a window click */ | if (target != root) { | |||
if (!scrot_get_geometry(target, &rx, &ry, &rw, &rh)) return NULL; | unsigned int d; | |||
int x; | ||||
int status; | ||||
status = XGetGeometry(disp, target, &root, &x, &x, &d, &d, &d, &d); | ||||
if (status) { | ||||
Window rt, *children, parent; | ||||
XEvent ev; | ||||
for (;;) { | ||||
/* Find window manager frame. */ | ||||
status = XQueryTree(disp, target, &rt, &parent, &children, &d); | ||||
if (status && (children != None)) | ||||
XFree(children); | ||||
if (!status || (parent == None) || (parent == rt)) | ||||
break; | ||||
target = parent; | ||||
++frames; | ||||
} | ||||
/* Get client window. */ | ||||
if (!opt.border) | ||||
target = scrotGetClientWindow(disp, target); | ||||
XRaiseWindow(disp, target); | ||||
/* Give the WM time to update the hidden area of the window. | ||||
Some windows never send the event, a time limit is placed. | ||||
*/ | ||||
XSelectInput(disp, target, FocusChangeMask); | ||||
struct timespec delay = {0, 10000000L}; // 10ms | ||||
for (short i = 0; i < 30; ++i) { | ||||
if (XCheckIfEvent(disp, &ev, &scrotXEventVisibility, (XPointer)& | ||||
target)) | ||||
break; | ||||
nanosleep(&delay, NULL); | ||||
} | ||||
} | ||||
} | } | |||
scrot_nice_clip(&rx, &ry, &rw, &rh); | stat = XGetWindowAttributes(disp, target, &attr); | |||
if (!stat || (attr.map_state != IsViewable)) | ||||
return 0; | ||||
*rw = attr.width; | ||||
*rh = attr.height; | ||||
XTranslateCoordinates(disp, target, root, 0, 0, rx, ry, &child); | ||||
if (! opt.silent) XBell(disp, 0); | /* Special case when the TWM emulates the border directly on the window. */ | |||
im = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); | if (opt.border == 1 && frames < 2 && attr.border_width > 0) { | |||
*rw += attr.border_width * 2; | ||||
if (opt.pointer == 1) | *rh += attr.border_width * 2; | |||
scrot_grab_mouse_pointer(im, rx, ry); | *rx -= attr.border_width; | |||
} | *ry -= attr.border_width; | |||
return im; | } | |||
} | return 1; | |||
Imlib_Image | ||||
scrot_grab_autoselect(void) | ||||
{ | ||||
Imlib_Image im = NULL; | ||||
int rx = opt.autoselect_x, ry = opt.autoselect_y, rw = opt.autoselect_w, rh = | ||||
opt.autoselect_h; | ||||
scrot_do_delay(); | ||||
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; | ||||
} | ||||
/* 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) | Window scrotGetWindow(Display* display, Window window, int x, int y) | |||
{ | { | |||
(void) dpy; // unused | Window source, target; | |||
Window *win = (Window*)arg; | ||||
return (ev->xvisibility.window == *win); | ||||
} | ||||
/* get geometry of window and use that */ | int status, xOffset, yOffset; | |||
int | ||||
scrot_get_geometry(Window target, | source = root; | |||
int *rx, | target = window; | |||
int *ry, | if (window == None) | |||
int *rw, | window = root; | |||
int *rh) | while (1) { | |||
{ | status = XTranslateCoordinates(display, source, window, x, y, &xOffset, | |||
Window child; | &yOffset, &target); | |||
XWindowAttributes attr; | if (!status) | |||
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; | break; | |||
} | if (target == None) | |||
target = parent; | ||||
++frames; | ||||
} | ||||
/* Get client window. */ | ||||
if (!opt.border) { | ||||
target = scrot_get_client_window(disp, target); | ||||
} | ||||
XRaiseWindow(disp, target); | ||||
/* Give the WM time to update the hidden area of the window. | ||||
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; | break; | |||
} | source = window; | |||
usleep(2000); | window = target; | |||
} | x = xOffset; | |||
y = yOffset; | ||||
} | } | |||
} | ||||
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 | ||||
scrot_get_window(Display * display, | ||||
Window window, | ||||
int x, | ||||
int y) | ||||
{ | ||||
Window source, target; | ||||
int status, x_offset, y_offset; | ||||
source = root; | ||||
target = window; | ||||
if (window == None) | ||||
window = root; | ||||
while (1) { | ||||
status = | ||||
XTranslateCoordinates(display, source, window, x, y, &x_offset, | ||||
&y_offset, &target); | ||||
if (status != True) | ||||
break; | ||||
if (target == None) | if (target == None) | |||
break; | target = window; | |||
source = window; | return target; | |||
window = target; | ||||
x = x_offset; | ||||
y = y_offset; | ||||
} | ||||
if (target == None) | ||||
target = window; | ||||
return (target); | ||||
} | } | |||
char * | char* imPrintf(char* str, struct tm* tm, char* filenameIM, char* filenameThumb, | |||
im_printf(char *str, struct tm *tm, | Imlib_Image im) | |||
char *filename_im, | { | |||
char *filename_thumb, | char* c; | |||
Imlib_Image im) | char buf[20]; | |||
{ | char ret[4096]; | |||
char *c; | char strf[4096]; | |||
char buf[20]; | char* tmp; | |||
char ret[4096]; | struct stat st; | |||
char strf[4096]; | ||||
char *tmp; | ||||
struct stat st; | ||||
ret[0] = '\0'; | ||||
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++) { | ||||
if (*c == '$') { | ||||
c++; | ||||
switch (*c) { | ||||
case 'a': | ||||
gethostname(buf, sizeof(buf)); | ||||
strcat(ret, buf); | ||||
break; | ||||
case 'f': | ||||
if (filename_im) | ||||
strcat(ret, filename_im); | ||||
break; | ||||
case 'm': /* t was already taken, so m as in mini */ | ||||
if (filename_thumb) | ||||
strcat(ret, filename_thumb); | ||||
break; | ||||
case 'n': | ||||
if (filename_im) { | ||||
tmp = strrchr(filename_im, '/'); | ||||
if (tmp) | ||||
strcat(ret, tmp + 1); | ||||
else | ||||
strcat(ret, filename_im); | ||||
} | ||||
break; | ||||
case 'w': | ||||
snprintf(buf, sizeof(buf), "%d", imlib_image_get_width()); | ||||
strcat(ret, buf); | ||||
break; | ||||
case 'h': | ||||
snprintf(buf, sizeof(buf), "%d", imlib_image_get_height()); | ||||
strcat(ret, buf); | ||||
break; | ||||
case 's': | ||||
if (filename_im) { | ||||
if (!stat(filename_im, &st)) { | ||||
int size; | ||||
size = st.st_size; | ||||
snprintf(buf, sizeof(buf), "%d", size); | ||||
strcat(ret, buf); | ||||
} else | ||||
strcat(ret, "[err]"); | ||||
} | ||||
break; | ||||
case 'p': | ||||
snprintf(buf, sizeof(buf), "%d", | ||||
imlib_image_get_width() * | ||||
imlib_image_get_height()); | ||||
strcat(ret, buf); | ||||
break; | ||||
case 't': | ||||
tmp = imlib_image_format(); | ||||
if (tmp) { | ||||
strcat(ret, tmp); | ||||
} | ||||
break; | ||||
case '$': | ||||
strcat(ret, "$"); | ||||
break; | ||||
default: | ||||
strncat(ret, c, 1); | ||||
break; | ||||
} | ||||
} else if (*c == '\\') { | ||||
c++; | ||||
switch (*c) { | ||||
case 'n': | ||||
if (filename_im) | ||||
strcat(ret, "\n"); | ||||
break; | ||||
default: | ||||
strncat(ret, c, 1); | ||||
break; | ||||
} | ||||
} else { | ||||
const size_t len = strlen(ret); | ||||
ret[len] = *c; | ||||
ret[len + 1] = '\0'; | ||||
} | ||||
} | ||||
return strdup(ret); | ||||
} | ||||
Window | ret[0] = '\0'; | |||
scrot_get_client_window(Display * display, | ||||
Window target) | ||||
{ | ||||
Atom state; | ||||
Atom type = None; | ||||
int format, status; | ||||
unsigned char *data; | ||||
unsigned long after, items; | ||||
Window client; | ||||
state = XInternAtom(display, "WM_STATE", True); | if (strftime(strf, 4095, str, tm) == 0) | |||
if (state == None) | errx(EXIT_FAILURE, "strftime returned 0"); | |||
return target; | ||||
status = | ||||
XGetWindowProperty(display, target, state, 0L, 0L, False, | ||||
(Atom) AnyPropertyType, &type, &format, &items, &after, | ||||
&data); | ||||
if ((status == Success) && (type != None)) | ||||
return target; | ||||
client = scrot_find_window_by_property(display, target, state); | ||||
if (!client) | ||||
return target; | ||||
return client; | ||||
} | ||||
Window | imlib_context_set_image(im); | |||
scrot_find_window_by_property(Display * display, | for (c = strf; *c != '\0'; c++) { | |||
const Window window, | if (*c == '$') { | |||
const Atom property) | c++; | |||
{ | switch (*c) { | |||
Atom type = None; | case 'a': | |||
int format, status; | gethostname(buf, sizeof(buf)); | |||
unsigned char *data; | strlcat(ret, buf, sizeof(ret)); | |||
unsigned int i, number_children; | break; | |||
unsigned long after, number_items; | case 'f': | |||
Window child = None, *children, parent, root; | if (filenameIM) | |||
strlcat(ret, filenameIM, sizeof(ret)); | ||||
status = | break; | |||
XQueryTree(display, window, &root, &parent, &children, &number_children); | case 'm': /* t was already taken, so m as in mini */ | |||
if (!status) | if (filenameThumb) | |||
return None; | strlcat(ret, filenameThumb, sizeof(ret)); | |||
for (i = 0; (i < number_children) && (child == None); i++) { | break; | |||
status = | case 'n': | |||
XGetWindowProperty(display, children[i], property, 0L, 0L, False, | if (filenameIM) { | |||
(Atom) AnyPropertyType, &type, &format, | tmp = strrchr(filenameIM, '/'); | |||
&number_items, &after, &data); | if (tmp) | |||
if (data) | strlcat(ret, tmp + 1, sizeof(ret)); | |||
XFree(data); | else | |||
if ((status == Success) && (type != (Atom) NULL)) | strlcat(ret, filenameIM, sizeof(ret)); | |||
child = children[i]; | } | |||
} | break; | |||
for (i = 0; (i < number_children) && (child == None); i++) | case 'w': | |||
child = scrot_find_window_by_property(display, children[i], property); | snprintf(buf, sizeof(buf), "%d", imlib_image_get_width()); | |||
if (children != None) | strlcat(ret, buf, sizeof(ret)); | |||
XFree(children); | break; | |||
return (child); | case 'h': | |||
} | snprintf(buf, sizeof(buf), "%d", imlib_image_get_height()); | |||
strlcat(ret, buf, sizeof(ret)); | ||||
Imlib_Image | break; | |||
scrot_grab_stack_windows(void) | case 's': | |||
{ | if (filenameIM) { | |||
if (XGetSelectionOwner(disp, XInternAtom(disp, "_NET_WM_CM_S0", False)) == N | if (!stat(filenameIM, &st)) { | |||
one) { | int size; | |||
fprintf(stderr, "Composite Manager is not running, required to use this | ||||
option.\n"); | size = st.st_size; | |||
exit(EXIT_FAILURE); | snprintf(buf, sizeof(buf), "%d", size); | |||
strlcat(ret, buf, sizeof(ret)); | ||||
} else | ||||
strlcat(ret, "[err]", sizeof(ret)); | ||||
} | ||||
break; | ||||
case 'p': | ||||
snprintf(buf, sizeof(buf), "%d", | ||||
imlib_image_get_width() * imlib_image_get_height()); | ||||
strlcat(ret, buf, sizeof(ret)); | ||||
break; | ||||
case 't': | ||||
tmp = imlib_image_format(); | ||||
if (tmp) | ||||
strlcat(ret, tmp, sizeof(ret)); | ||||
break; | ||||
case '$': | ||||
strlcat(ret, "$", sizeof(ret)); | ||||
break; | ||||
default: | ||||
snprintf(buf, sizeof(buf), "%.1s", c); | ||||
strlcat(ret, buf, sizeof(ret)); | ||||
break; | ||||
} | ||||
} else if (*c == '\\') { | ||||
c++; | ||||
switch (*c) { | ||||
case 'n': | ||||
if (filenameIM) | ||||
strlcat(ret, "\n", sizeof(ret)); | ||||
break; | ||||
default: | ||||
snprintf(buf, sizeof(buf), "%.1s", c); | ||||
strlcat(ret, buf, sizeof(ret)); | ||||
break; | ||||
} | ||||
} else { | ||||
const size_t length = strlen(ret); | ||||
ret[length] = *c; | ||||
ret[length + 1] = '\0'; | ||||
} | ||||
} | } | |||
return strdup(ret); | ||||
} | ||||
unsigned long nitems_return; | Window scrotGetClientWindow(Display* display, Window target) | |||
unsigned long bytes_after_return; | { | |||
unsigned char *prop_return; | Atom state; | |||
long long_offset = 0L; | Atom type = None; | |||
long long_length = ~0L; | int format, status; | |||
Bool delete = False; | unsigned char* data; | |||
int actual_format_return; | unsigned long after, items; | |||
Atom actual_type_return; | Window client; | |||
Scrot_Imlib_List *list_images = NULL; | ||||
Imlib_Image im = NULL; | state = XInternAtom(display, "WM_STATE", True); | |||
XImage *ximage = NULL; | if (state == None) | |||
return target; | ||||
status = XGetWindowProperty(display, target, state, 0L, 0L, False, | ||||
AnyPropertyType, &type, &format, &items, &after, | ||||
&data); | ||||
if ((status == Success) && (type != None)) | ||||
return target; | ||||
client = scrotFindWindowByProperty(display, target, state); | ||||
if (!client) | ||||
return target; | ||||
return client; | ||||
} | ||||
Window scrotFindWindowByProperty(Display* display, const Window window, const At | ||||
om property) | ||||
{ | ||||
Atom type = None; | ||||
int format, status; | ||||
unsigned char* data; | ||||
unsigned int i, numberChildren; | ||||
unsigned long after, numberItems; | ||||
Window child = None, *children, parent, rootReturn; | ||||
status = XQueryTree(display, window, &rootReturn, &parent, &children, &numbe | ||||
rChildren); | ||||
if (!status) | ||||
return None; | ||||
for (i = 0; (i < numberChildren) && (child == None); i++) { | ||||
status = XGetWindowProperty(display, children[i], property, 0L, 0L, Fals | ||||
e, | ||||
AnyPropertyType, &type, &format, | ||||
&numberItems, &after, &data); | ||||
if (data) | ||||
XFree(data); | ||||
if ((status == Success) && type) | ||||
child = children[i]; | ||||
} | ||||
for (i = 0; (i < numberChildren) && (child == None); i++) | ||||
child = scrotFindWindowByProperty(display, children[i], property); | ||||
if (children != None) | ||||
XFree(children); | ||||
return (child); | ||||
} | ||||
Imlib_Image scrotGrabStackWindows(void) | ||||
{ | ||||
if (XGetSelectionOwner(disp, XInternAtom(disp, "_NET_WM_CM_S0", False)) == N | ||||
one) | ||||
errx(EXIT_FAILURE, "option --stack: Composite Manager is not running, re | ||||
quired to use this option."); | ||||
unsigned long numberItemsReturn; | ||||
unsigned long bytesAfterReturn; | ||||
unsigned char* propReturn; | ||||
long offset = 0L; | ||||
long length = ~0L; | ||||
Bool delete = False; | ||||
int actualFormatReturn; | ||||
Atom actualTypeReturn; | ||||
Imlib_Image im = NULL; | ||||
XImage* ximage = NULL; | ||||
XWindowAttributes attr; | XWindowAttributes attr; | |||
unsigned long i = 0; | unsigned long i = 0; | |||
#define EWMH_CLIENT_LIST "_NET_CLIENT_LIST" // spec EWMH | #define EWMH_CLIENT_LIST "_NET_CLIENT_LIST" // spec EWMH | |||
Atom atom_prop = XInternAtom(disp, EWMH_CLIENT_LIST, False); | Atom atomProp = XInternAtom(disp, EWMH_CLIENT_LIST, False); | |||
Atom atom_type = AnyPropertyType; | Atom atomType = AnyPropertyType; | |||
int result = XGetWindowProperty(disp, root, atom_prop, long_offset, long_len | int result = XGetWindowProperty(disp, root, atomProp, offset, length, | |||
gth, | delete, atomType, &actualTypeReturn, &actualFormatReturn, | |||
delete, atom_type, &actual_type_return, &actual_ | &numberItemsReturn, &bytesAfterReturn, &propReturn); | |||
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); | if (result != Success || numberItemsReturn == 0) | |||
errx(EXIT_FAILURE, "option --stack: Failed XGetWindowProperty: " EWMH_CL | ||||
IENT_LIST); | ||||
for (i = 0; i < nitems_return; i++) { | initializeScrotList(images); | |||
Window win = *((Window*)prop_return + i); | XCompositeRedirectSubwindows(disp, root, CompositeRedirectAutomatic); | |||
if (0 == XGetWindowAttributes(disp, win, &attr)) { | for (i = 0; i < numberItemsReturn; i++) { | |||
fprintf(stderr, "Failed XGetWindowAttributes\n"); | Window win = *((Window*)propReturn + i); | |||
exit(EXIT_FAILURE); | ||||
} | if (!XGetWindowAttributes(disp, win, &attr)) | |||
errx(EXIT_FAILURE, "option --stack: Failed XGetWindowAttributes"); | ||||
/* Only visible windows */ | /* Only visible windows */ | |||
if (attr.map_state != IsViewable) { | if (attr.map_state != IsViewable) | |||
continue; | continue; | |||
} | ||||
if (!scrot_match_window_class_name(win)) { | if (!scrotMatchWindowClassName(win)) | |||
continue; | continue; | |||
} | ||||
ximage = XGetImage(disp, win, 0, 0, attr.width, attr.height, AllPlanes, ZPixmap); | ximage = XGetImage(disp, win, 0, 0, attr.width, attr.height, AllPlanes, ZPixmap); | |||
if (ximage == NULL) { | if (!ximage) | |||
fprintf(stderr, "Failed XGetImage: Window id 0x%lx.\n", win); | errx(EXIT_FAILURE, "option --stack: Failed XGetImage: Window id 0x%l | |||
exit(EXIT_FAILURE); | x", win); | |||
} | ||||
im = imlib_create_image_from_ximage(ximage, NULL, attr.x, attr.y, attr.w idth, attr.height, 1); | im = imlib_create_image_from_ximage(ximage, NULL, attr.x, attr.y, attr.w idth, attr.height, 1); | |||
XFree(ximage); | XFree(ximage); | |||
list_images = append_to_scrot_imlib(list_images, im); | appendToScrotList(images, im); | |||
} | } | |||
return stalk_image_concat(list_images); | return stalkImageConcat(&images, opt.stackDirection); | |||
} | } | |||
Imlib_Image | Imlib_Image scrotGrabShotMulti(void) | |||
scrot_grab_shot_multi(void) | ||||
{ | { | |||
int screens; | int screens = ScreenCount(disp); | |||
int i; | if (screens < 2) | |||
char *dispstr, *subdisp; | return scrotGrabShot(); | |||
char newdisp[255]; | ||||
Scrot_Imlib_List *images = NULL; | int i; | |||
Imlib_Image ret = NULL; | char* dispStr; | |||
char* subDisp; | ||||
screens = ScreenCount(disp); | char newDisp[255]; | |||
if (screens < 2) | Imlib_Image ret = NULL; | |||
return scrot_grab_shot(); | ||||
initializeScrotList(images); | ||||
subdisp = strdup(DisplayString(disp)); | ||||
subDisp = strdup(DisplayString(disp)); | ||||
for (i = 0; i < screens; i++) { | ||||
dispstr = strchr(subdisp, ':'); | for (i = 0; i < screens; i++) { | |||
if (dispstr) { | dispStr = strchr(subDisp, ':'); | |||
dispstr = strchr(dispstr, '.'); | if (dispStr) { | |||
if (NULL != dispstr) | dispStr = strchr(dispStr, '.'); | |||
*dispstr = '\0'; | if (dispStr) | |||
*dispStr = '\0'; | ||||
} | ||||
snprintf(newDisp, sizeof(newDisp), "%s.%d", subDisp, i); | ||||
initXAndImlib(newDisp, i); | ||||
ret = imlib_create_image_from_drawable(0, 0, 0, scr->width, | ||||
scr->height, 1); | ||||
appendToScrotList(images, ret); | ||||
} | ||||
free(subDisp); | ||||
return stalkImageConcat(&images, HORIZONTAL); | ||||
} | ||||
Imlib_Image stalkImageConcat(ScrotList* images, enum Direction const dir) | ||||
{ | ||||
if (isEmptyScrotList(images)) | ||||
return NULL; | ||||
int total = 0, max = 0; | ||||
int x = 0, y = 0, w , h; | ||||
Imlib_Image ret, im; | ||||
ScrotListNode* image = NULL; | ||||
bool const vertical = (dir == VERTICAL) ? true : false; | ||||
forEachScrotList(images, image) { | ||||
im = (Imlib_Image) image->data; | ||||
imlib_context_set_image(im); | ||||
h = imlib_image_get_height(); | ||||
w = imlib_image_get_width(); | ||||
if (!vertical) { | ||||
if (h > max) | ||||
max = h; | ||||
total += w; | ||||
} else { | ||||
if (w > max) | ||||
max = w; | ||||
total += h; | ||||
} | ||||
} | } | |||
snprintf(newdisp, sizeof(newdisp), "%s.%d", subdisp, i); | if (!vertical) { | |||
init_x_and_imlib(newdisp, i); | w = total; | |||
ret = | h = max; | |||
imlib_create_image_from_drawable(0, 0, 0, scr->width, | } else { | |||
scr->height, 1); | w = max; | |||
images = append_to_scrot_imlib(images, ret); | h = total; | |||
} | } | |||
free(subdisp); | ret = imlib_create_image(w, h); | |||
ret = stalk_image_concat(images); | ||||
return ret; | ||||
} | ||||
Imlib_Image | ||||
stalk_image_concat(Scrot_Imlib_List * images) | ||||
{ | ||||
int tot_w = 0, max_h = 0, w, h; | ||||
int x = 0; | ||||
Scrot_Imlib_List *l, *item; | ||||
Imlib_Image ret, im; | ||||
if (is_scrot_imlib_list_empty(images)) | ||||
return NULL; | ||||
l = images; | ||||
while (l) { | ||||
im = (Imlib_Image) l->data; | ||||
imlib_context_set_image(im); | ||||
h = imlib_image_get_height(); | ||||
w = imlib_image_get_width(); | ||||
if (h > max_h) | ||||
max_h = h; | ||||
tot_w += w; | ||||
l = l->next; | ||||
} | ||||
ret = imlib_create_image(tot_w, max_h); | ||||
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; | ||||
while (l) { | ||||
im = (Imlib_Image) l->data; | ||||
item = l; | ||||
l = l->next; | ||||
imlib_context_set_image(im); | ||||
h = imlib_image_get_height(); | ||||
w = imlib_image_get_width(); | ||||
imlib_context_set_image(ret); | imlib_context_set_image(ret); | |||
imlib_context_set_anti_alias(0); | imlib_context_set_color(0, 0, 0, 255); | |||
imlib_context_set_dither(1); | imlib_image_fill_rectangle(0, 0, w, h); | |||
imlib_context_set_blend(0); | ||||
imlib_context_set_angle(0); | image = firstScrotList(images); | |||
imlib_blend_image_onto_image(im, 0, 0, 0, w, h, x, 0, w, h); | while (image) { | |||
x += w; | im = (Imlib_Image) image->data; | |||
imlib_context_set_image(im); | imlib_context_set_image(im); | |||
imlib_free_image_and_decache(); | h = imlib_image_get_height(); | |||
free(item); | w = imlib_image_get_width(); | |||
} | imlib_context_set_image(ret); | |||
return 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, y, w, h); | ||||
(!vertical) ? (x += w) : (y += h); | ||||
imlib_context_set_image(im); | ||||
imlib_free_image_and_decache(); | ||||
nextAndFreeScrotList(image); | ||||
} | ||||
return ret; | ||||
} | } | |||
End of changes. 104 change blocks. | ||||
873 lines changed or deleted | 644 lines changed or added |