+void write_data(Window requestor, Atom property, Atom type, int format,
+ void *vdata, size_t size)
+{
+ int bformat = format / 8; /* bytes per element */
+ unsigned char *data = (unsigned char *)vdata;
+ XEvent ev;
+
+ if (!use_outgoing_incr || size * bformat <= sel_delta) {
+ XChangeProperty(disp, requestor, property, type, format,
+ PropModeReplace, data, size);
+ } else {
+ /*
+ * For large data, an incremental transfer as per ICCCM 2.7.2.
+ */
+ Cardinal totalsize = size * bformat;
+ Cardinal sent, thissize;
+
+ /*
+ * We're going to need PropertyNotify events on the target
+ * window to tell us when to send the next chunk.
+ */
+ XSelectInput(disp, requestor, PropertyChangeMask);
+
+ /*
+ * Start by sending a single 32-bit word with type INCR giving
+ * the total size in bytes.
+ */
+ XChangeProperty(disp, requestor, property, incr_atom, 32,
+ PropModeReplace, (unsigned char *)&totalsize, 1);
+
+ /*
+ * Now set up an entry in our list of ongoing incremental
+ * transfers, so that whenever that property is deleted, we'll
+ * send the next batch.
+ */
+ if (nincrs >= incrsize) {
+ incrsize = nincrs * 9 / 8 + 16;
+ incrs = realloc(incrs, incrsize * sizeof(*incrs));
+ if (!incrs)
+ error ("out of memory");
+ }
+ incrs[nincrs].window = requestor;
+ incrs[nincrs].property = property;
+ incrs[nincrs].type = type;
+ incrs[nincrs].format = format;
+ incrs[nincrs].size = totalsize;
+ incrs[nincrs].data = malloc(totalsize);
+ if (!incrs[nincrs].data)
+ error("out of memory");
+ memcpy(incrs[nincrs].data, data, size);
+ nincrs++;
+ }
+}
+
+Atom convert_sel_inner(Window requestor, Atom target, Atom property) {
+ if (target == strtype) {
+ write_data(requestor, property, strtype, 8, seltext, sellen);
+ return property;
+ } else if (target == compound_text_atom && convert_to_ctext) {
+ XTextProperty tp;
+ XmbTextListToTextProperty(disp, &seltext, 1, XCompoundTextStyle, &tp);
+ write_data(requestor, property, target, tp.format, 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;
+ write_data(requestor, property, atom_atom, 32, targets, len);
+ return property;
+ } else if (target == timestamp_atom) {
+ Time rettime = CurrentTime;
+ write_data(requestor, property, integer_atom, 32, &rettime, 1);
+ return property;
+ } else {
+ return None;
+ }
+}
+
+Atom convert_sel_outer(Window requestor, Atom target, Atom property) {
+ /*
+ * ICCCM 2.2 says that obsolete clients requesting the selection
+ * request may not specify a property name under which they want
+ * the data written to their window; selection owners are
+ * encouraged to support such clients by reusing the selection
+ * target name as the property.
+ */
+ if (property == None)
+ property = target;
+
+ 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 = sel_delta;
+ Atom actual_type;
+ int actual_format, i;
+ unsigned 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 < (long)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);
+ }
+}
+