X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/utils/blobdiff_plain/5340832dd218ccde00ae0c94c0070086fec1f233..3a3007a42fd846aa494270f8181717e13a548142:/xcopy/xcopy.c diff --git a/xcopy/xcopy.c b/xcopy/xcopy.c index 140716a..5a654a0 100644 --- a/xcopy/xcopy.c +++ b/xcopy/xcopy.c @@ -31,6 +31,7 @@ char *display = NULL; 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; @@ -40,6 +41,7 @@ int sellen, selsize; /* functional parameters */ int reading; /* read instead of writing? */ int convert_to_ctext = True; /* Xmb convert to compound text? */ +int verbose; const char usagemsg[] = "usage: xcopy [ -r ] [ -u | -c ] [ -C ]\n" @@ -52,6 +54,8 @@ const char usagemsg[] = " -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" + " -v proceed verbosely when reading selection\n" " also: xcopy --version report version number\n" " xcopy --help display this help text\n" " xcopy --licence display the (MIT) licence text\n" @@ -62,7 +66,7 @@ void usage(void) { } const char licencemsg[] = - "xcopy is copyright 2001-2004 Simon Tatham.\n" + "xcopy is copyright 2001-2004,2008 Simon Tatham.\n" "\n" "Permission is hereby granted, free of charge, to any person\n" "obtaining a copy of this software and associated documentation files\n" @@ -142,6 +146,10 @@ int main(int ac, char **av) { else error ("expected an argument to `-a'"); mode = CUSTOM; + } else if (!strcmp(p, "-F")) { + fork_when_writing = False; + } else if (!strcmp(p, "-v")) { + verbose = True; } else if (!strcmp(p, "--help")) { usage(); return 0; @@ -191,7 +199,7 @@ int main(int ac, char **av) { } eventloop = init_X(); - if (!reading) { + if (!reading && fork_when_writing) { /* * If we are writing the selection, we must go into the * background now. @@ -240,7 +248,8 @@ char *ucasename = "XCopy"; Display *disp = NULL; Window ourwin = None; -Atom compound_text_atom, targets_atom; +Atom compound_text_atom, targets_atom, timestamp_atom, atom_atom, integer_atom; +Atom multiple_atom, atom_pair_atom; int screen, wwidth, wheight; Atom strtype = XA_STRING; @@ -248,6 +257,14 @@ Atom sel_atom = XA_PRIMARY; Atom expected_type = (Atom)None; int expected_format = 8; +static const char *translate_atom(Display *disp, Atom atom) +{ + if (atom == None) + return "None"; + else + return XGetAtomName(disp, atom); +} + /* * Returns TRUE if we need to enter an event loop, FALSE otherwise. */ @@ -267,31 +284,31 @@ int init_X(void) { error ("unable to open display"); targets_atom = XInternAtom(disp, "TARGETS", False); - if (!targets_atom) - error ("unable to get TARGETS property"); + timestamp_atom = XInternAtom(disp, "TIMESTAMP", False); + atom_atom = XInternAtom(disp, "ATOM", False); + atom_pair_atom = XInternAtom(disp, "ATOM_PAIR", False); + multiple_atom = XInternAtom(disp, "MULTIPLE", 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); - if (!strtype) - error ("unable to get COMPOUND_TEXT property"); } else if (mode == TARGETS) { strtype = targets_atom; - expected_type = XInternAtom(disp, "ATOM", False); + expected_type = atom_atom; expected_format = 32; } else if (mode == TIMESTAMP) { - strtype = XInternAtom(disp, "TIMESTAMP", False); + strtype = timestamp_atom; + expected_type = integer_atom; expected_format = 32; } else if (mode == CUSTOM) { - strtype = XInternAtom(disp, custom_seltype, False); + strtype = XInternAtom(disp, custom_seltype, True); + if (!strtype) + error ("atom '%s' does not exist on the server", custom_seltype); expected_format = 0; } if (use_clipboard) { sel_atom = XInternAtom(disp, "CLIPBOARD", False); - if (!sel_atom) - error ("unable to get CLIPBOARD property"); } /* get the screen and root-window */ @@ -314,15 +331,30 @@ int init_X(void) { /* do selection fiddling */ if (reading) { /* - * We are reading the selection, so we must FIXME. + * We are reading the selection. Call XConvertSelection to + * request transmission of the selection data in the + * appropriate format; the X event loop will then wait to + * receive the data from the selection owner. + * + * If there is no selection owner, look in the cut buffer + * property on the root window. */ if (XGetSelectionOwner(disp, sel_atom) == None) { /* No primary selection, so use the cut buffer. */ + if (verbose) + fprintf(stderr, "no selection owner: trying cut buffer\n"); if (strtype == XA_STRING) do_paste(DefaultRootWindow(disp), XA_CUT_BUFFER0, False); return False; } else { Atom sel_property = XInternAtom(disp, "VT_SELECTION", False); + if (verbose) + fprintf(stderr, "calling XConvertSelection: selection=%s" + " target=%s property=%s requestor=%08lx\n", + translate_atom(disp, sel_atom), + translate_atom(disp, strtype), + translate_atom(disp, sel_property), + ourwin); XConvertSelection(disp, sel_atom, strtype, sel_property, ourwin, CurrentTime); return True; @@ -336,7 +368,7 @@ int init_X(void) { */ XSetSelectionOwner (disp, sel_atom, ourwin, CurrentTime); if (XGetSelectionOwner (disp, sel_atom) != ourwin) - error ("unable to obtain primary X selection\n"); + error ("unable to obtain primary X selection"); compound_text_atom = XInternAtom(disp, "COMPOUND_TEXT", False); if (strtype == XA_STRING) { /* @@ -368,6 +400,102 @@ int init_X(void) { } } +Atom convert_sel_inner(Window requestor, Atom target, Atom property) { + if (target == strtype) { + XChangeProperty (disp, requestor, property, strtype, + 8, PropModeReplace, seltext, sellen); + return property; + } else if (target == compound_text_atom && convert_to_ctext) { + XTextProperty tp; + XmbTextListToTextProperty (disp, &seltext, 1, + XCompoundTextStyle, &tp); + XChangeProperty (disp, requestor, property, target, + tp.format, PropModeReplace, + tp.value, tp.nitems); + return property; + } else if (target == targets_atom) { + Atom targets[16]; + int len = 0; + targets[len++] = timestamp_atom; + targets[len++] = targets_atom; + targets[len++] = multiple_atom; + targets[len++] = strtype; + if (strtype != compound_text_atom && convert_to_ctext) + targets[len++] = compound_text_atom; + XChangeProperty (disp, requestor, property, + atom_atom, 32, PropModeReplace, + (unsigned char *)targets, len); + return property; + } else if (target == timestamp_atom) { + Time rettime = CurrentTime; + XChangeProperty (disp, requestor, property, + integer_atom, 32, PropModeReplace, + (unsigned char *)&rettime, 1); + return property; + } else { + return None; + } +} + +Atom convert_sel_outer(Window requestor, Atom target, Atom property) { + if (target == multiple_atom) { + /* + * Support for the MULTIPLE selection type, since it's + * specified as required in the ICCCM. Completely + * untested, though, because I have no idea of what X + * application might implement it for me to test against. + */ + + int size = SELDELTA; + Atom actual_type; + int actual_format, i; + long nitems, bytes_after, nread; + unsigned char *data; + Atom *adata; + + if (property == (Atom)None) + return None; /* ICCCM says this isn't allowed */ + + /* + * Fetch the requestor's window property giving a list of + * selection requests. + */ + while (XGetWindowProperty(disp, requestor, property, 0, size, + False, AnyPropertyType, &actual_type, + &actual_format, &nitems, &bytes_after, + (unsigned char **) &data) == Success && + nitems * (actual_format / 8) == size) { + XFree(data); + size *= 3 / 2; + } + + if (actual_type != atom_pair_atom || actual_format != 32) { + XFree(data); + return None; + } + + adata = (Atom *)data; + + for (i = 0; i+1 < nitems; i += 2) { + if (adata[i+1] != (Atom)None) /* ICCCM says this isn't allowed */ + adata[i+1] = convert_sel_inner(requestor, adata[i], + adata[i+1]); + } + + XChangeProperty (disp, requestor, property, + atom_pair_atom, 32, PropModeReplace, + data, nitems); + + XFree(data); + + return property; + } else { + if (property == (Atom)None) + property = target; /* ICCCM says this is a sensible default */ + return convert_sel_inner(requestor, target, property); + } +} + void run_X(void) { XEvent ev, e2; @@ -376,6 +504,14 @@ void run_X(void) { if (reading) { switch (ev.type) { case SelectionNotify: + if (verbose) + fprintf(stderr, "got SelectionNotify: requestor=%08lx " + "selection=%s target=%s property=%s\n", + ev.xselection.requestor, + translate_atom(disp, ev.xselection.selection), + translate_atom(disp, ev.xselection.target), + translate_atom(disp, ev.xselection.property)); + if (ev.xselection.property != None) do_paste(ev.xselection.requestor, ev.xselection.property, True); @@ -392,37 +528,12 @@ void run_X(void) { e2.xselection.selection = ev.xselectionrequest.selection; e2.xselection.target = ev.xselectionrequest.target; e2.xselection.time = ev.xselectionrequest.time; - if (ev.xselectionrequest.target == strtype) { - XChangeProperty (disp, ev.xselectionrequest.requestor, - ev.xselectionrequest.property, strtype, - 8, PropModeReplace, seltext, sellen); - e2.xselection.property = ev.xselectionrequest.property; - } else if (ev.xselectionrequest.target == compound_text_atom && - convert_to_ctext) { - XTextProperty tp; - XmbTextListToTextProperty (disp, &seltext, 1, - XCompoundTextStyle, &tp); - XChangeProperty (disp, ev.xselectionrequest.requestor, - ev.xselectionrequest.property, - ev.xselectionrequest.target, - tp.format, PropModeReplace, - tp.value, tp.nitems); - e2.xselection.property = ev.xselectionrequest.property; - } else if (ev.xselectionrequest.target == targets_atom) { - Atom targets[2]; - int len = 0; - 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, - (unsigned char *)targets, len); - } else { - e2.xselection.property = None; - } - XSendEvent (disp, ev.xselectionrequest.requestor, False, 0, &e2); + e2.xselection.property = + convert_sel_outer(ev.xselectionrequest.requestor, + ev.xselectionrequest.target, + ev.xselectionrequest.property); + XSendEvent (disp, ev.xselectionrequest.requestor, + False, 0, &e2); } } } @@ -448,6 +559,11 @@ void do_paste(Window window, Atom property, int Delete) { Delete, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, (unsigned char **) &data) == Success) { + if (verbose) + fprintf(stderr, "got %ld items of %d-byte data, type=%s;" + " %ld to go\n", nitems, actual_format, + translate_atom(disp, actual_type), bytes_after); + if (nitems > 0) { /* * We expect all returned chunks of data to be @@ -465,18 +581,17 @@ void do_paste(Window window, Atom property, int Delete) { */ if ((nread & 3) != 0) { error("unexpected data size: %d read (not a multiple" - " of 4), but more to come\n", nread); + " of 4), but more to come", 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", + const char *expout = translate_atom(disp, expected_type); + const char *gotout = translate_atom(disp, actual_type); + error("unexpected data type: expected %s, got %s", expout, gotout); } if (expected_format && expected_format != actual_format) { - error("unexpected data format: expected %d-bit, got %d-bit\n", + error("unexpected data format: expected %d-bit, got %d-bit", expected_format, actual_format); } } @@ -487,7 +602,7 @@ void do_paste(Window window, Atom property, int Delete) { int i; for (i = 0; i < nitems; i++) { Atom x = ((Atom *)data)[i]; - printf("%s\n", XGetAtomName(disp, x)); + printf("%s\n", translate_atom(disp, x)); } } else if (mode == TIMESTAMP) { assert(actual_format == 32);