Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion man/scrot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ NAME
scrot - command line screen capture utility

SYNOPSIS
scrot [-bcfhimopuvz] [-a X,Y,W,H] [-C NAME] [-D DISPLAY] [-d SEC] [-e CMD]
scrot [-bcfhimopuvxz] [-a X,Y,W,H] [-C NAME] [-D DISPLAY] [-d SEC] [-e CMD]
[-k OPT] [-l STYLE] [-M NUM] [-n OPTS] [-q NUM] [-s OPTS] [-t % | WxH]
[-w NUM] [[-F] FILE]

Expand Down Expand Up @@ -73,6 +73,7 @@ OPTIONS
-v, --version Output version information and exit.
-w, --window WID Window identifier to capture.
WID must be a valid identifier (see xwininfo(1)).
-x, --clipboard Copy the output filename to the clipboard.
-Z, --compression LVL Compression level to use, LVL must be within
[0, 9]. Higher level compression provides lower file
size at the cost of slower encoding/saving speed.
Expand Down
7 changes: 6 additions & 1 deletion src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ enum { /* long opt only */
OPT_FORMAT = UCHAR_MAX + 1,
OPT_LIST_OPTS,
};
static const char stropts[] = "a:bC:cD:d:e:F:fhik::l:M:mn:opq:S:s::t:uvw:Z:z";
static const char stropts[] = "a:bC:cD:d:e:F:fhik::l:M:mn:opq:S:s::t:uvw:xZ:z";
// NOTE: make sure lopts and opt_description indexes are kept in sync
static const struct option lopts[] = {
{"autoselect", required_argument, NULL, 'a'},
Expand Down Expand Up @@ -111,6 +111,7 @@ static const struct option lopts[] = {
{"focussed", no_argument, NULL, 'u'},
{"version", no_argument, NULL, 'v'},
{"window", required_argument, NULL, 'w'},
{"clipboard", no_argument, NULL, 'x'},
{"compression", required_argument, NULL, 'Z'},
{"silent", no_argument, NULL, 'z'},
{"format", required_argument, NULL, OPT_FORMAT},
Expand Down Expand Up @@ -147,6 +148,7 @@ static const struct option_desc {
/* u */ { "capture the currently focused window", "" },
/* v */ { "output version and exit", "" },
/* w */ { "X window ID to capture", "WID" },
/* x */ { "copy the output filename to the clipboard", "" },
/* Z */ { "image compression level", "LVL" },
/* z */ { "prevent beeping", "" },
/* OPT_FORMAT */ { "specify output file format", "FMT" },
Expand Down Expand Up @@ -491,6 +493,9 @@ void optionsParse(int argc, char *argv[])
errmsg);
}
break;
case 'x':
opt.clipboard = true;
break;
case 'Z':
opt.compression = optionsParseNum(optarg, 0, 9, &errmsg);
if (errmsg) {
Expand Down
1 change: 1 addition & 0 deletions src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ struct ScrotOptions {
bool overwrite;
bool freeze;
bool ignoreKeyboard;
bool clipboard;
};

extern struct ScrotOptions opt;
Expand Down
121 changes: 121 additions & 0 deletions src/scrot.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <Imlib2.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/Xinerama.h>
Expand All @@ -82,6 +83,7 @@ static void scrotCheckIfOverwriteFile(char **);
static void scrotExecApp(Imlib_Image, struct tm *, char *, char *);
static char *imPrintf(const char *, struct tm *, const char *, const char *,
Imlib_Image);
static void scrotCopyToClipboard(const char *);
static char *scrotGetWindowName(Window);
static Window scrotGetClientWindow(Display *, Window);
static Window scrotFindWindowByProperty(Display *, const Window, const Atom);
Expand Down Expand Up @@ -215,6 +217,9 @@ int main(int argc, char *argv[])
if (opt.exec)
scrotExecApp(image, tm, filenameIM, filenameThumb);

if (opt.clipboard)
scrotCopyToClipboard(filenameIM);

imlib_context_set_image(image);
imlib_free_image_and_decache();
free(filenameIM);
Expand Down Expand Up @@ -644,6 +649,122 @@ static void scrotExecApp(Imlib_Image image, struct tm *tm, char *filenameIM,
free(execStr);
}

static void scrotCopyToClipboard(const char *filename)
{
Atom clipboardAtom, targetsAtom, textAtom, utf8Atom;
Window clipboardOwner;
XEvent event;
int maxWait = 50; /* Wait up to 500ms for clipboard operations */
char *fullPath = NULL;

if (filename == NULL)
return;

/* Convert relative path to absolute path */
if (filename[0] != '/') {
char *cwd = getcwd(NULL, 0);
if (cwd) {
size_t pathLen = strlen(cwd) + 1 + strlen(filename) + 1;
fullPath = malloc(pathLen);
if (fullPath) {
snprintf(fullPath, pathLen, "%s/%s", cwd, filename);
} else {
warn("Failed to allocate memory for clipboard path");
free(cwd);
return;
}
free(cwd);
} else {
warn("Failed to get current directory for clipboard");
return;
}
} else {
fullPath = strdup(filename);
if (!fullPath) {
warn("Failed to allocate memory for clipboard path");
return;
}
}

const char *clipboardText = fullPath;

/* Get necessary atoms */
clipboardAtom = XInternAtom(disp, "CLIPBOARD", False);
targetsAtom = XInternAtom(disp, "TARGETS", False);
textAtom = XInternAtom(disp, "TEXT", False);
utf8Atom = XInternAtom(disp, "UTF8_STRING", False);

/* Create a window to own the clipboard */
clipboardOwner = XCreateSimpleWindow(disp, root, -10, -10, 1, 1, 0, 0, 0);

/* Set the clipboard data as a property on our window */
XChangeProperty(disp, clipboardOwner, clipboardAtom, utf8Atom, 8,
PropModeReplace, (unsigned char *)clipboardText, strlen(clipboardText));

/* Claim ownership of the clipboard */
XSetSelectionOwner(disp, clipboardAtom, clipboardOwner, CurrentTime);

/* Check if we successfully got ownership */
if (XGetSelectionOwner(disp, clipboardAtom) != clipboardOwner) {
warnx("Failed to acquire clipboard ownership");
XDestroyWindow(disp, clipboardOwner);
free(fullPath);
return;
}

/* Process clipboard requests for a short time to ensure the data is available */
XFlush(disp);

while (maxWait-- > 0) {
if (XPending(disp)) {
XNextEvent(disp, &event);

if (event.type == SelectionRequest) {
XSelectionRequestEvent *req = &event.xselectionrequest;
XSelectionEvent response;

response.type = SelectionNotify;
response.requestor = req->requestor;
response.selection = req->selection;
response.target = req->target;
response.time = req->time;
response.property = None;

if (req->target == targetsAtom) {
/* Respond with supported targets */
Atom targets[] = { utf8Atom, textAtom, XA_STRING };
XChangeProperty(disp, req->requestor, req->property,
XA_ATOM, 32, PropModeReplace,
(unsigned char *)targets, 3);
response.property = req->property;
} else if (req->target == utf8Atom || req->target == textAtom ||
req->target == XA_STRING) {
/* Provide the filename text */
XChangeProperty(disp, req->requestor, req->property,
req->target, 8, PropModeReplace,
(unsigned char *)clipboardText, strlen(clipboardText));
response.property = req->property;
}

XSendEvent(disp, req->requestor, False, 0, (XEvent *)&response);
} else if (event.type == SelectionClear) {
/* Lost clipboard ownership */
break;
}
}
scrotSleepFor(clockNow(), 10); /* Sleep for 10ms */
}

if (!opt.silent)
fprintf(stderr, "Filename copied to clipboard: %s\n", clipboardText);

free(fullPath);

/* Note: We're intentionally not destroying the window here
* as it needs to persist to serve clipboard requests.
* It will be cleaned up when the program exits. */
}

static char *imPrintf(const char *str, struct tm *tm, const char *filenameIM,
const char *filenameThumb, Imlib_Image im)
{
Expand Down