* selection
*/
-#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* set from command-line parameters */
char *display = NULL;
-enum { STRING, CTEXT, UTF8 } mode = STRING;
+enum { STRING, CTEXT, UTF8, TARGETS, TIMESTAMP, CUSTOM } mode = STRING;
+int use_clipboard = False;
+char *custom_seltype = NULL;
+int fork_when_writing = True;
/* selection data */
char *seltext;
const char usagemsg[] =
"usage: xcopy [ -r ] [ -u | -c ] [ -C ]\n"
+ "where: -r read X selection and print on stdout\n"
+ " no -r read stdin and store in X selection\n"
+ " -u work with UTF8_STRING type selections\n"
+ " -c work with COMPOUND_TEXT type selections\n"
+ " -C suppress automatic conversion to COMPOUND_TEXT\n"
+ " -b read the CLIPBOARD rather than the PRIMARY selection\n"
+ " -t get the list of targets available to retrieve\n"
+ " -T get the time stamp of the selection contents\n"
+ " -a atom get an arbitrary form of the selection data\n"
+ " -F do not fork in write mode\n"
" also: xcopy --version report version number\n"
" xcopy --help display this help text\n"
" xcopy --licence display the (MIT) licence text\n"
- "where: -r read X selection and print on stdout\n"
- " no -r read stdin and store in X selection\n"
- " -u work with UTF8_STRING type selections\n"
- " -c work with COMPOUND_TEXT type selections\n"
- " -C suppress automatic conversion to COMPOUND_TEXT\n"
;
void usage(void) {
pname = *av;
/* parse the command line arguments */
- while (--ac) {
+ while (--ac > 0) {
char *p = *++av;
if (!strcmp(p, "-display") || !strcmp(p, "-disp")) {
mode = CTEXT;
} else if (!strcmp(p, "-C")) {
convert_to_ctext = False;
+ } else if (!strcmp(p, "-b")) {
+ use_clipboard = True;
+ } else if (!strcmp(p, "-t")) {
+ mode = TARGETS;
+ } else if (!strcmp(p, "-T")) {
+ mode = TIMESTAMP;
+ } else if (!strcmp(p, "-a")) {
+ if (--ac > 0)
+ custom_seltype = *++av;
+ else
+ error ("expected an argument to `-a'");
+ mode = CUSTOM;
+ } else if (!strcmp(p, "-F")) {
+ fork_when_writing = False;
} else if (!strcmp(p, "--help")) {
usage();
return 0;
}
if (!reading) {
+ if (mode == TARGETS || mode == TIMESTAMP || mode == CUSTOM) {
+ error ("%s not supported in writing mode; use -r",
+ (mode == TARGETS ? "-t" :
+ mode == TIMESTAMP ? "-T" :
+ /* mode == CUSTOM ? */ "-a"));
+ }
+ }
+
+ if (!reading) {
seltext = malloc(SELDELTA);
if (!seltext)
error ("out of memory");
}
eventloop = init_X();
- if (!reading) {
+ if (!reading && fork_when_writing) {
/*
* If we are writing the selection, we must go into the
* background now.
Display *disp = NULL;
Window ourwin = None;
-Atom compound_text_atom, targets_atom;
+Atom compound_text_atom, targets_atom, timestamp_atom, atom_atom, integer_atom;
int screen, wwidth, wheight;
Atom strtype = XA_STRING;
+Atom sel_atom = XA_PRIMARY;
+Atom expected_type = (Atom)None;
+int expected_format = 8;
/*
* Returns TRUE if we need to enter an event loop, FALSE otherwise.
if (!disp)
error ("unable to open display");
+ targets_atom = XInternAtom(disp, "TARGETS", False);
+ timestamp_atom = XInternAtom(disp, "TIMESTAMP", False);
+ atom_atom = XInternAtom(disp, "ATOM", False);
+ integer_atom = XInternAtom(disp, "INTEGER", False);
if (mode == UTF8) {
strtype = XInternAtom(disp, "UTF8_STRING", False);
- if (!strtype)
- error ("unable to get UTF8_STRING property");
} else if (mode == CTEXT) {
strtype = XInternAtom(disp, "COMPOUND_TEXT", False);
+ } else if (mode == TARGETS) {
+ strtype = targets_atom;
+ expected_type = atom_atom;
+ expected_format = 32;
+ } else if (mode == TIMESTAMP) {
+ strtype = timestamp_atom;
+ expected_type = integer_atom;
+ expected_format = 32;
+ } else if (mode == CUSTOM) {
+ strtype = XInternAtom(disp, custom_seltype, True);
if (!strtype)
- error ("unable to get COMPOUND_TEXT property");
+ error ("atom '%s' does not exist on the server", custom_seltype);
+ expected_format = 0;
+ }
+ if (use_clipboard) {
+ sel_atom = XInternAtom(disp, "CLIPBOARD", False);
}
- targets_atom = XInternAtom(disp, "TARGETS", False);
- if (!targets_atom)
- error ("unable to get TARGETS property");
/* get the screen and root-window */
screen = DefaultScreen (disp);
/*
* We are reading the selection, so we must FIXME.
*/
- if (XGetSelectionOwner(disp, XA_PRIMARY) == None) {
+ if (XGetSelectionOwner(disp, sel_atom) == None) {
/* No primary selection, so use the cut buffer. */
- do_paste(DefaultRootWindow(disp), XA_CUT_BUFFER0, False);
+ if (strtype == XA_STRING)
+ do_paste(DefaultRootWindow(disp), XA_CUT_BUFFER0, False);
return False;
} else {
Atom sel_property = XInternAtom(disp, "VT_SELECTION", False);
- XConvertSelection(disp, XA_PRIMARY, strtype,
+ XConvertSelection(disp, sel_atom, strtype,
sel_property, ourwin, CurrentTime);
return True;
}
* CUT_BUFFER0, if it isn't of an exotic type (cut buffers
* can only take ordinary string data, it turns out).
*/
- XSetSelectionOwner (disp, XA_PRIMARY, ourwin, CurrentTime);
- if (XGetSelectionOwner (disp, XA_PRIMARY) != ourwin)
+ XSetSelectionOwner (disp, sel_atom, ourwin, CurrentTime);
+ if (XGetSelectionOwner (disp, sel_atom) != ourwin)
error ("unable to obtain primary X selection\n");
compound_text_atom = XInternAtom(disp, "COMPOUND_TEXT", False);
if (strtype == XA_STRING) {
tp.value, tp.nitems);
e2.xselection.property = ev.xselectionrequest.property;
} else if (ev.xselectionrequest.target == targets_atom) {
- Atom targets[2];
+ Atom targets[16];
int len = 0;
+ targets[len++] = timestamp_atom;
+ targets[len++] = targets_atom;
targets[len++] = strtype;
if (strtype != compound_text_atom && convert_to_ctext)
targets[len++] = compound_text_atom;
XChangeProperty (disp, ev.xselectionrequest.requestor,
ev.xselectionrequest.property,
- ev.xselectionrequest.target,
- 32, PropModeReplace,
+ atom_atom, 32, PropModeReplace,
(unsigned char *)targets, len);
+ e2.xselection.property = ev.xselectionrequest.property;
+ } else if (ev.xselectionrequest.target == timestamp_atom) {
+ Time rettime = CurrentTime;
+ XChangeProperty (disp, ev.xselectionrequest.requestor,
+ ev.xselectionrequest.property,
+ integer_atom, 32, PropModeReplace,
+ (unsigned char *)&rettime, 1);
+ e2.xselection.property = ev.xselectionrequest.property;
} else {
e2.xselection.property = None;
}
Delete, AnyPropertyType, &actual_type,
&actual_format, &nitems, &bytes_after,
(unsigned char **) &data) == Success) {
- /*
- * We expect all returned chunks of data to be multiples of
- * 4 bytes (because we can only request the subsequent
- * starting offset in 4-byte increments). Of course you can
- * store an odd number of bytes in a selection, so this
- * can't be the case every time XGetWindowProperty returns;
- * but it should be the case every time it returns _and
- * there is more data to come_.
- *
- * Hence, whenever XGetWindowProperty returns, we verify
- * that the size of the data returned _last_ time was
- * divisible by 4.
- */
- if (nitems > 0)
- assert((nread & 3) == 0);
-
- if (actual_type == strtype && nitems > 0) {
- assert(actual_format == 8);
- fwrite(data, 1, nitems, stdout);
- nread += nitems;
+ if (nitems > 0) {
+ /*
+ * We expect all returned chunks of data to be
+ * multiples of 4 bytes (because we can only request
+ * the subsequent starting offset in 4-byte
+ * increments). Of course you can store an odd number
+ * of bytes in a selection, so this can't be the case
+ * every time XGetWindowProperty returns; but it
+ * should be the case every time it returns _and there
+ * is more data to come_.
+ *
+ * Hence, whenever XGetWindowProperty returns, we
+ * verify that the size of the data returned _last_
+ * time was divisible by 4.
+ */
+ if ((nread & 3) != 0) {
+ error("unexpected data size: %d read (not a multiple"
+ " of 4), but more to come\n", nread);
+ }
+
+ if (expected_type != (Atom)None && actual_type != expected_type) {
+ char *expout = XGetAtomName(disp, expected_type);
+ char *gotout = (actual_type == (Atom)None ? "None" :
+ XGetAtomName(disp, actual_type));
+ error("unexpected data type: expected %s, got %s\n",
+ expout, gotout);
+ }
+ if (expected_format && expected_format != actual_format) {
+ error("unexpected data format: expected %d-bit, got %d-bit\n",
+ expected_format, actual_format);
+ }
+ }
+
+ if (nitems > 0) {
+ if (mode == TARGETS) {
+ assert(actual_format == 32);
+ int i;
+ for (i = 0; i < nitems; i++) {
+ Atom x = ((Atom *)data)[i];
+ printf("%s\n", XGetAtomName(disp, x));
+ }
+ } else if (mode == TIMESTAMP) {
+ assert(actual_format == 32);
+ Time x = ((Time *)data)[0];
+ printf("%lu\n", (unsigned long)x);
+ } else {
+ fwrite(data, actual_format / 8, nitems, stdout);
+ nread += nitems * actual_format / 8;
+ }
}
XFree(data);
- if (actual_type != strtype || nitems == 0)
+ if (nitems == 0)
break;
}
}