summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Huddleston <jeremyhu@freedesktop.org>2008-09-14 09:23:17 -0700
committerJeremy Huddleston <jeremyhu@freedesktop.org>2008-09-21 17:40:14 -0700
commit5b397642cdc29920245ebe64cc85243cac893e0e (patch)
treec35dbd612507fd28f9a7010c3ff3d1b107339340
parentdac9e91870f9c787eac97c9d7d0607979c57eb5b (diff)
XQuartz: pbproxy: First round of updates to pbproxy from George.
(cherry picked from commit 5c8b956f8f3f17e8d577d97cb66424954be72684)
-rw-r--r--hw/xquartz/pbproxy/main.m37
-rw-r--r--hw/xquartz/pbproxy/pbproxy.h9
-rw-r--r--hw/xquartz/pbproxy/x-input.m30
-rw-r--r--hw/xquartz/pbproxy/x-selection.h28
-rw-r--r--hw/xquartz/pbproxy/x-selection.m603
5 files changed, 518 insertions, 189 deletions
diff --git a/hw/xquartz/pbproxy/main.m b/hw/xquartz/pbproxy/main.m
index eaf14a75e..f2597267f 100644
--- a/hw/xquartz/pbproxy/main.m
+++ b/hw/xquartz/pbproxy/main.m
@@ -14,14 +14,13 @@
Display *x_dpy;
int x_apple_wm_event_base, x_apple_wm_error_base;
-Atom x_atom_wm_state, x_atom_wm_protocols, x_atom_wm_delete_window;
-Atom x_atom_clipboard, x_atom_text, x_atom_utf8_string;
-Atom x_atom_targets, x_atom_multiple, x_atom_cstring;
+struct atom_list atom_list_inst;
+struct atom_list *atoms = &atom_list_inst;
static int x_grab_count;
static Bool x_grab_synced;
-static BOOL _is_active = YES; /* FIXME: should query server */
+static BOOL _is_active = YES; /* FIXME: should query server */ /*GPS why? Is there a race?*/
static x_selection *_selection_object;
@@ -65,19 +64,24 @@ static void x_init (void) {
}
XSetIOErrorHandler (x_io_error_handler);
- x_atom_clipboard = XInternAtom (x_dpy, "CLIPBOARD", False);
- x_atom_text = XInternAtom (x_dpy, "TEXT", False);
- x_atom_utf8_string = XInternAtom (x_dpy, "UTF8_STRING", False);
- x_atom_targets = XInternAtom (x_dpy, "TARGETS", False);
- x_atom_multiple = XInternAtom (x_dpy, "MULTIPLE", False);
- x_atom_cstring = XInternAtom (x_dpy, "CSTRING", False);
-
+ atoms->primary = XInternAtom (x_dpy, "PRIMARY", False);
+ atoms->clipboard = XInternAtom (x_dpy, "CLIPBOARD", False);
+ atoms->text = XInternAtom (x_dpy, "TEXT", False);
+ atoms->utf8_string = XInternAtom (x_dpy, "UTF8_STRING", False);
+ atoms->targets = XInternAtom (x_dpy, "TARGETS", False);
+ atoms->multiple = XInternAtom (x_dpy, "MULTIPLE", False);
+ atoms->cstring = XInternAtom (x_dpy, "CSTRING", False);
+ atoms->image_png = XInternAtom (x_dpy, "image/png", False);
+ atoms->incr = XInternAtom (x_dpy, "INCR", False);
+ atoms->atom = XInternAtom (x_dpy, "ATOM", False);
+ atoms->clipboard_manager = XInternAtom (x_dpy, "CLIPBOARD_MANAGER", False);
+
if (!XAppleWMQueryExtension (x_dpy, &x_apple_wm_event_base,
&x_apple_wm_error_base)) {
fprintf (stderr, "can't open AppleWM server extension\n");
exit (1);
}
-
+
XAppleWMSelectInput (x_dpy, AppleWMActivationNotifyMask |
AppleWMPasteboardNotifyMask);
@@ -85,15 +89,22 @@ static void x_init (void) {
x_input_register ();
x_input_run ();
+
+ [_selection_object set_clipboard_manager];
+ [_selection_object reclaim_clipboard];
}
static void x_shutdown (void) {
+ /*GPS: signal_handler() calls this, and I don't think these are async-signal safe. */
+ /*TODO use a socketpair() to trigger a cleanup. This is totally unsafe according to Jordan. */
+
[_selection_object release];
_selection_object = nil;
+
XCloseDisplay (x_dpy);
x_dpy = NULL;
- exit(0);
+ exit(0); //GPS this is almost certainly unsafe (sigaction(2) doesn't list it).
}
static void x_error_shutdown (void) {
diff --git a/hw/xquartz/pbproxy/pbproxy.h b/hw/xquartz/pbproxy/pbproxy.h
index ddadbb3eb..3682421b6 100644
--- a/hw/xquartz/pbproxy/pbproxy.h
+++ b/hw/xquartz/pbproxy/pbproxy.h
@@ -22,8 +22,13 @@ extern Time x_current_timestamp (void);
extern Display *x_dpy;
extern int x_apple_wm_event_base, x_apple_wm_error_base;
-extern Atom x_atom_clipboard, x_atom_text, x_atom_utf8_string;
-extern Atom x_atom_targets, x_atom_multiple, x_atom_cstring;
+
+struct atom_list {
+ Atom primary, clipboard, text, utf8_string, string, targets, multiple,
+ cstring, image_png, incr, atom, clipboard_manager;
+};
+
+extern struct atom_list *atoms;
/* from x-input.m */
extern void x_input_register (void);
diff --git a/hw/xquartz/pbproxy/x-input.m b/hw/xquartz/pbproxy/x-input.m
index 2e7725024..f720af5ed 100644
--- a/hw/xquartz/pbproxy/x-input.m
+++ b/hw/xquartz/pbproxy/x-input.m
@@ -21,7 +21,13 @@ CFRunLoopSourceRef x_dpy_source;
/* Timestamp when the X server last told us it's active */
static Time last_activation_time;
+static FILE *getSelectionLog(void) {
+ return fopen("/tmp/selection.log", "a");
+}
+
static void x_event_apple_wm_notify(XAppleWMNotifyEvent *e) {
+ FILE *fp = getSelectionLog();
+
switch (e->type - x_apple_wm_event_base) {
case AppleWMActivationNotify:
switch (e->kind) {
@@ -39,33 +45,47 @@ static void x_event_apple_wm_notify(XAppleWMNotifyEvent *e) {
break;
case AppleWMPasteboardNotify:
+ fprintf(fp, "AppleWMPasteboardNotify\n");
+
switch (e->kind) {
case AppleWMCopyToPasteboard:
[x_selection_object () x_copy:e->time];
}
break;
}
+ fclose(fp);
}
void x_input_run (void) {
+ FILE *fp = getSelectionLog();
+
while (XPending (x_dpy) != 0) {
- XEvent e;
-
+ XEvent e;
+
XNextEvent (x_dpy, &e);
switch (e.type) {
case SelectionClear:
- [x_selection_object () clear_event:&e.xselectionclear];
+ fprintf(fp, "SelectionClear\n");
+
+ [x_selection_object () clear_event:&e.xselectionclear];
break;
case SelectionRequest:
+ fprintf(fp, "SelectionRequest\n");
[x_selection_object () request_event:&e.xselectionrequest];
break;
case SelectionNotify:
+ fprintf(fp, "SelectionNotify\n");
[x_selection_object () notify_event:&e.xselection];
break;
+ case PropertyNotify:
+ fprintf(fp, "PropertyNotify\n");
+ [x_selection_object () property_event:&e.xproperty];
+ break;
+
default:
if (e.type - x_apple_wm_event_base >= 0
&& e.type - x_apple_wm_event_base < AppleWMNumberEvents) {
@@ -73,7 +93,10 @@ void x_input_run (void) {
}
break;
}
+
+ XFlush(x_dpy);
}
+ fclose(fp);
}
static int add_input_socket (int sock, CFOptionFlags callback_types,
@@ -111,3 +134,4 @@ void x_input_register(void) {
exit (1);
}
}
+
diff --git a/hw/xquartz/pbproxy/x-selection.h b/hw/xquartz/pbproxy/x-selection.h
index b31bf6344..88408bf6a 100644
--- a/hw/xquartz/pbproxy/x-selection.h
+++ b/hw/xquartz/pbproxy/x-selection.h
@@ -34,6 +34,12 @@
#include "pbproxy.h"
#include <AppKit/NSPasteboard.h>
+/* This stores image data or text. */
+struct propdata {
+ unsigned char *data;
+ size_t length;
+};
+
@interface x_selection : NSObject
{
@private
@@ -53,6 +59,24 @@
/* When true, we're expecting a SelectionNotify event. */
unsigned int _pending_notify :1;
+
+ Atom request_atom;
+
+ struct {
+ struct propdata propdata;
+ Window requestor;
+ Atom selection;
+ } pending;
+
+ /* This may not be needed.*/
+ /* If we can have the Apple clipboard translate to PNG or JPEG we can
+ * do away with this. Otherwise we could use libjpeg and libpng
+ * to convert some raw clipboard format to the proper format.
+ */
+ struct {
+ struct propdata propdata;
+ Atom type;
+ } request_data;
}
- (void) x_active:(Time)timestamp;
@@ -63,6 +87,10 @@
- (void) clear_event:(XSelectionClearEvent *)e;
- (void) request_event:(XSelectionRequestEvent *)e;
- (void) notify_event:(XSelectionEvent *)e;
+- (void) property_event:(XPropertyEvent *)e;
+- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata;
+- (void) reclaim_clipboard;
+- (void) set_clipboard_manager;
@end
diff --git a/hw/xquartz/pbproxy/x-selection.m b/hw/xquartz/pbproxy/x-selection.m
index 5b2ba9cc0..e390dc02d 100644
--- a/hw/xquartz/pbproxy/x-selection.m
+++ b/hw/xquartz/pbproxy/x-selection.m
@@ -30,12 +30,172 @@
#import "x-selection.h"
+#include <stdio.h>
+#include <stdlib.h>
#include <X11/Xatom.h>
-#include <unistd.h>
+#include <unistd.h> /*GPS may not be needed now */
@implementation x_selection
+static struct propdata null_propdata = {NULL, 0};
+
+static void
+init_propdata (struct propdata *pdata)
+{
+ *pdata = null_propdata;
+}
+
+static void
+free_propdata (struct propdata *pdata)
+{
+ free (pdata->data);
+ *pdata = null_propdata;
+}
+
+/* Return True if this is an INCR-style transfer. */
+static Bool
+is_incr_type (XSelectionEvent *e)
+{
+ Atom seltype;
+ int format;
+ unsigned long numitems = 0UL, bytesleft = 0UL;
+ unsigned char *chunk;
+
+ if (Success != XGetWindowProperty (x_dpy, e->requestor, e->property,
+ /*offset*/ 0L, /*length*/ 4UL,
+ /*Delete*/ False,
+ AnyPropertyType, &seltype, &format,
+ &numitems, &bytesleft, &chunk))
+ {
+ return False;
+ }
+
+ if(chunk)
+ XFree(chunk);
+
+ return (seltype == atoms->incr) ? True : False;
+}
+
+/* This finds the preferred type from a TARGETS list.*/
+static Atom
+find_preferred (struct propdata *pdata)
+{
+ Atom a = None;
+ size_t i;
+ Bool png = False, utf8 = False, string = False;
+
+ if (pdata->length % sizeof (a))
+ {
+ fprintf(stderr, "Atom list is not a multiple of the size of an atom!\n");
+ return None;
+ }
+
+ for (i = 0; i < pdata->length; i += sizeof (a))
+ {
+ memcpy (&a, pdata->data + i, sizeof (a));
+
+ if (a == atoms->image_png)
+ {
+ png = True;
+ }
+ else if (a == atoms->utf8_string)
+ {
+ utf8 = True;
+ }
+ else if (a == atoms->string)
+ {
+ string = True;
+ }
+ }
+
+ /*We prefer PNG over strings, and UTF8 over a Latin-1 string.*/
+ if (png)
+ return atoms->image_png;
+
+ if (utf8)
+ return atoms->utf8_string;
+
+ if (string)
+ return atoms->string;
+
+ /* This is evidently something we don't know how to handle.*/
+ return None;
+}
+
+/*
+ * Return True if an error occurs. Return False if pdata has data
+ * and we finished.
+ * The property is only deleted when bytesleft is 0 if delete is True.
+ */
+static Bool
+get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Atom *type)
+{
+ long offset = 0;
+ unsigned long numitems, bytesleft = 0;
+#ifdef TEST
+ /* This is used to test the growth handling. */
+ unsigned long length = 4UL;
+#else
+ unsigned long length = (100000UL + 3) / 4;
+#endif
+ unsigned char *buf = NULL, *chunk = NULL;
+ size_t buflen = 0, chunkbytesize = 0;
+ int format;
+
+ if(None == property)
+ return True;
+
+ do {
+ unsigned long newbuflen;
+ unsigned char *newbuf;
+
+ if (Success != XGetWindowProperty (x_dpy, win, property,
+ offset, length, delete,
+ AnyPropertyType,
+ type, &format, &numitems,
+ &bytesleft, &chunk)) {
+ free (buf);
+ return True;
+ }
+
+#ifdef TEST
+ printf("format %d numitems %lu bytesleft %lu\n",
+ format, numitems, bytesleft);
+
+ printf("type %s\n", XGetAtomName(dis, *type));
+#endif
+
+ /* Format is the number of bits. */
+ chunkbytesize = numitems * (format / 8);
+
+#ifdef TEST
+ printf("chunkbytesize %zu\n", chunkbytesize);
+#endif
+
+ newbuflen = buflen + chunkbytesize;
+ newbuf = realloc (buf, newbuflen);
+ if (NULL == newbuf) {
+ XFree (chunk);
+ free (buf);
+ return True;
+ }
+
+ memcpy (newbuf + buflen, chunk, chunkbytesize);
+ XFree (chunk);
+ buf = newbuf;
+ buflen = newbuflen;
+ /* offset is a multiple of 32 bits*/
+ offset += chunkbytesize / 4;
+ } while (bytesleft > 0);
+
+ pdata->data = buf;
+ pdata->length = buflen;
+
+ return /*success*/ False;
+}
+
+
static unsigned long *
read_prop_32 (Window id, Atom prop, int *nitems_ret)
{
@@ -70,50 +230,55 @@ read_prop_32 (Window id, Atom prop, int *nitems_ret)
return (unsigned long *) data;
}
-float
-get_time (void)
+/*
+ * This should be called after a selection has been copied,
+ * or when the selection is unfinished before a transfer completes.
+ */
+- (void) release_pending
{
- extern void Microseconds ();
- UnsignedWide usec;
- long long ll;
-
- Microseconds (&usec);
- ll = ((long long) usec.hi << 32) | usec.lo;
-
- return ll / 1e6;
+ free_propdata (&pending.propdata);
+ pending.requestor = None;
+ pending.selection = None;
}
-static Bool
-IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
- Bool (*pred) (Display *, XEvent *, XPointer),
- XPointer arg)
+/* Return True if an error occurs during an append.*/
+/* Return False if the append succeeds. */
+- (Bool) append_to_pending:(struct propdata *)pdata requestor:(Window)requestor
{
- float start = get_time ();
- fd_set fds;
- struct timeval tv;
-
- do {
- if (XCheckIfEvent (x_dpy, e, pred, arg))
- return True;
-
- FD_ZERO (&fds);
- FD_SET (ConnectionNumber (x_dpy), &fds);
- tv.tv_usec = 0;
- tv.tv_sec = timeout;
-
- if (select (FD_SETSIZE, &fds, NULL, NULL, &tv) != 1)
- break;
+ unsigned char *newdata;
+ size_t newlength;
+
+ if (requestor != pending.requestor)
+ {
+ [self release_pending];
+ pending.requestor = requestor;
+ }
+
+ newlength = pending.propdata.length + pdata->length;
+ newdata = realloc(pending.propdata.data, newlength);
- } while (start + timeout > get_time ());
+ if(NULL == newdata)
+ {
+ perror("realloc propdata");
+ [self release_pending];
+ return True;
+ }
+ memcpy(newdata + pending.propdata.length, pdata->data, pdata->length);
+ pending.propdata.data = newdata;
+ pending.propdata.length = newlength;
+
return False;
}
+
+
/* Called when X11 becomes active (i.e. has key focus) */
- (void) x_active:(Time)timestamp
{
TRACE ();
+#if 0
if ([_pasteboard changeCount] != _my_last_change)
{
if ([_pasteboard availableTypeFromArray: _known_types] != nil)
@@ -121,12 +286,13 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
/* Pasteboard has data we should proxy; I think it makes
sense to put it on both CLIPBOARD and PRIMARY */
- XSetSelectionOwner (x_dpy, x_atom_clipboard,
+ XSetSelectionOwner (x_dpy, atoms->clipboard,
_selection_window, timestamp);
XSetSelectionOwner (x_dpy, XA_PRIMARY,
_selection_window, timestamp);
}
}
+#endif
}
/* Called when X11 loses key focus */
@@ -135,19 +301,20 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
Window w;
TRACE ();
-
+#if 0
if (_proxied_selection == XA_PRIMARY)
return;
- w = XGetSelectionOwner (x_dpy, x_atom_clipboard);
+ w = XGetSelectionOwner (x_dpy, atoms->clipboard);
if (w != None && w != _selection_window)
{
/* An X client has the selection, proxy it to the pasteboard */
_my_last_change = [_pasteboard declareTypes:_known_types owner:self];
- _proxied_selection = x_atom_clipboard;
+ _proxied_selection = atoms->clipboard;
}
+#endif
}
/* Called when the Edit/Copy item on the main X11 menubar is selected
@@ -156,16 +323,16 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
{
Window w;
- /* Lazily copies the PRIMARY selection to the pasteboard. */
-
- w = XGetSelectionOwner (x_dpy, XA_PRIMARY);
+ w = XGetSelectionOwner (x_dpy, atoms->primary);
- if (w != None && w != _selection_window)
+ if (None != w)
{
- XSetSelectionOwner (x_dpy, x_atom_clipboard,
- _selection_window, timestamp);
- _my_last_change = [_pasteboard declareTypes:_known_types owner:self];
- _proxied_selection = XA_PRIMARY;
+ request_atom = atoms->targets;
+
+ puts("Asking for targets");
+
+ XConvertSelection (x_dpy, atoms->primary, atoms->targets,
+ atoms->primary, _selection_window, CurrentTime);
}
else
{
@@ -173,16 +340,80 @@ IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
}
}
-
-/* X events */
+/*
+ *
+ */
+- (void) set_clipboard_manager
+{
+ if (None != XGetSelectionOwner (x_dpy, atoms->clipboard_manager))
+ {
+ fprintf (stderr, "A clipboard manager is already running!\n"
+ "pbproxy can not continue!\n");
+ exit (EXIT_FAILURE);
+ }
+ XSetSelectionOwner (x_dpy, atoms->clipboard_manager, _selection_window,
+ CurrentTime);
+}
+
+/*
+ * This occurs when we previously owned a selection,
+ * and then lost it from another client.
+ */
- (void) clear_event:(XSelectionClearEvent *)e
{
TRACE ();
+
+ if (atoms->clipboard == e->selection)
+ {
+ /*
+ * We lost ownership of the CLIPBOARD.
+ */
+ [self reclaim_clipboard];
+ }
+ else if (atoms->clipboard_manager == e->selection)
+ {
+ /*TODO/HMM What should we do here? */
+ /* Another CLIPBOARD_MANAGER has set itself as owner.
+ * a) we can call [self set_clipboard_manager] here and risk a war.
+ * b) we can print a message and exit.
+ */
+ fprintf (stderr, "error: another clipboard manager was started!\n");
+ exit (EXIT_FAILURE);
+ }
+}
+
+/*
+ * We greedily acquire the clipboard after it changes, and on startup.
+ */
+- (void) reclaim_clipboard
+{
+ Window owner;
+
+ owner = XGetSelectionOwner (x_dpy, atoms->clipboard);
+ if (None == owner)
+ {
+ /*
+ * The owner probably died or we are just starting up pbproxy.
+ * Set pbproxy's _selection_window as the owner, and continue.
+ */
+ do
+ {
+ XSetSelectionOwner (x_dpy, atoms->clipboard, _selection_window,
+ CurrentTime);
+ } while (_selection_window != XGetSelectionOwner (x_dpy,
+ atoms->clipboard));
- /* Right now we don't care about this. */
+ return;
+ }
+
+ request_atom = atoms->targets;
+ XConvertSelection (x_dpy, atoms->clipboard, atoms->targets,
+ atoms->clipboard, _selection_window, CurrentTime);
+ /* Now we will get a SelectionNotify event in the future. */
}
+/* The preference should be for UTF8_STRING before the XA_STRING*/
static Atom
convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
{
@@ -191,12 +422,12 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
if (data == nil)
return ret;
- if (target == x_atom_text)
- target = x_atom_utf8_string;
+ if (target == atoms->text)
+ target = atoms->utf8_string;
if (target == XA_STRING
- || target == x_atom_cstring
- || target == x_atom_utf8_string)
+ || target == atoms->cstring
+ || target == atoms->utf8_string)
{
const char *bytes;
@@ -221,7 +452,6 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
- (void) request_event:(XSelectionRequestEvent *)e
{
/* Someone's asking us for the data on the pasteboard */
-
XEvent reply;
NSString *data;
Atom target;
@@ -236,12 +466,13 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
reply.xselection.property = None;
target = e->target;
-
- if (target == x_atom_targets)
+
+ if (target == atoms->targets)
{
+ /* This is where we respond to the TARGETS request. */
long data[2];
- data[0] = x_atom_utf8_string;
+ data[0] = atoms->utf8_string;
data[1] = XA_STRING;
XChangeProperty (x_dpy, e->requestor, e->property, target,
@@ -249,7 +480,7 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
sizeof (data));
reply.xselection.property = e->property;
}
- else if (target == x_atom_multiple)
+ else if (target == atoms->multiple)
{
if (e->property != None)
{
@@ -286,165 +517,181 @@ convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
XSendEvent (x_dpy, e->requestor, False, 0, &reply);
}
+/* This handles the events resulting from an XConvertSelection request. */
- (void) notify_event:(XSelectionEvent *)e
{
- /* Someone sent us data we're waiting for. */
-
Atom type;
- int format, r, offset;
- unsigned long nitems, bytes_after;
- unsigned char *data, *buf;
- NSString *string;
-
- TRACE ();
+ struct propdata pdata;
+
+ [self release_pending];
+
+ puts ("NOTIFY EVENT");
+ if (None == e->property) {
+ puts("Nothing");
+ /* Nothing is selected. */
+ return;
+ }
- if (e->target == x_atom_targets)
+ if (is_incr_type (e))
{
- /* Was trying to fetch the TARGETS property; it lists the
- formats supported by the selection owner. */
+ /*
+ * This is an INCR-style transfer, which means that we
+ * will get the data after a series of PropertyNotify events.
+ */
- unsigned long *atoms;
- int natoms;
- int i, utf8_i = -1, string_i = -1;
+ puts("IS INCR");
- if (e->property != None
- && (atoms = read_prop_32 (e->requestor,
- e->property, &natoms)) != NULL)
+ if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
{
- for (i = 0; i < natoms; i++)
- {
- if (atoms[i] == XA_STRING)
- string_i = i;
- else if (atoms[i] == x_atom_utf8_string)
- utf8_i = i;
- }
- XFree (atoms);
+ return;
}
- /* May as well try as STRING if nothing else, it can only
- fail, and it will help broken clients who don't support
- the TARGETS selection.. */
-
- if (utf8_i >= 0)
- type = x_atom_utf8_string;
- else
- type = XA_STRING;
+ free_propdata (&pdata);
- XConvertSelection (x_dpy, e->selection, type,
- e->selection, e->requestor, e->time);
- _pending_notify = YES;
- return;
+ pending.requestor = e->requestor;
+ pending.selection = e->selection;
}
+ else
+ {
+ if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
+ {
+ return;
+ }
- if (e->property == None)
- return; /* FIXME: notify pasteboard? */
-
- /* Should be the data. Find out how big it is and what format it's in. */
-
- r = XGetWindowProperty (x_dpy, e->requestor, e->property,
- 0, 0, False, AnyPropertyType, &type,
- &format, &nitems, &bytes_after, &data);
- if (r != Success)
- return;
-
- XFree (data);
- if (type == None || format != 8)
- return;
-
- bytes_after += nitems;
-
- /* Read it into a buffer. */
+ puts("HANDLING NOW");
+
+ /* We have the complete selection data.*/
+ [self handle_selection: e->selection type:type propdata:&pdata];
+ }
+}
- buf = malloc (bytes_after + 1);
- if (buf == NULL)
- return;
+- (void) property_event:(XPropertyEvent *)e
+{
+ struct propdata pdata;
+ Atom type;
- for (offset = 0; bytes_after > 0; offset += nitems)
+ if (None != pending.requestor && PropertyNewValue == e->state)
{
- r = XGetWindowProperty (x_dpy, e->requestor, e->property,
- offset / 4, (bytes_after / 4) + 1,
- False, AnyPropertyType, &type,
- &format, &nitems, &bytes_after, &data);
- if (r != Success)
+ if (get_property (e->window, e->atom, &pdata, /*Delete*/ True, &type))
{
- free (buf);
return;
}
- memcpy (buf + offset, data, nitems);
- XFree (data);
+ if (0 == pdata.length)
+ {
+ /* We completed the transfer. */
+ [self handle_selection: pending.selection type: type propdata: &pending.propdata];
+ pending.propdata = null_propdata;
+ pending.requestor = None;
+ pending.selection = None;
+ }
+ else
+ {
+ [self append_to_pending: &pdata requestor: e->window];
+ }
}
- buf[offset] = 0;
- XDeleteProperty (x_dpy, e->requestor, e->property);
-
- /* Convert to an NSString and write to the pasteboard. */
+}
- if (type == XA_STRING)
- string = [NSString stringWithCString:(char *) buf];
- else /* if (type == x_atom_utf8_string) */
- string = [NSString stringWithUTF8String:(char *) buf];
+- (void) handle_targets: (Atom)selection propdata:(struct propdata *)pdata
+{
+ Atom preferred = find_preferred (pdata);
- free (buf);
+ if (None == preferred)
+ {
+ /*
+ * This isn't required by the ICCCM, but some apps apparently
+ * don't respond to TARGETS properly.
+ */
+ preferred = XA_STRING;
+ }
- [_pasteboard setString:string forType:NSStringPboardType];
+ request_atom = preferred;
+ XConvertSelection (x_dpy, selection, preferred, selection,
+ _selection_window, CurrentTime);
}
-
-/* NSPasteboard-required methods */
-
-static Bool
-selnotify_pred (Display *dpy, XEvent *e, XPointer arg)
+/* This handles the image/png type of selection (typically in CLIPBOARD). */
+- (void) handle_png: (struct propdata *)pdata
{
- return e->type == SelectionNotify;
+ /* TODO Use the NSPasteboard code I wrote that may work... */
}
-- (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
+/* This handles the UTF8_STRING type of selection. */
+- (void) handle_utf8_string: (struct propdata *)pdata
{
- XEvent e;
- Atom request;
+ size_t i;
+ unsigned char *p = pdata->data;
- TRACE ();
-
- /* Don't ask for the data yet, first find out which formats
- the selection owner supports. */
-
- request = x_atom_targets;
+ puts("HANDLE UTF8_STRING");
+ for (i = 0; i < pdata->length; ++i) {
+ printf("%c", p[i]);
+ }
+ puts("");
-again:
- XConvertSelection (x_dpy, _proxied_selection, request,
- _proxied_selection, _selection_window, CurrentTime);
+ NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSUTF8StringEncoding];
+ [_pasteboard setString:string forType:NSStringPboardType];
+ [string release];
+}
- _pending_notify = YES;
+/* This handles the XA_STRING type, which should be in Latin-1. */
+- (void) handle_string: (struct propdata *)pdata
+{
+ puts("STRING");
- /* Seems like we need to be synchronous here.. Actually, this really
- sucks, since it means we could get deadlocked if people don't
- respond to our request. So we need to implement our own timeout
- code.. */
+ NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSISOLatin1StringEncoding];
+ [_pasteboard setString:string forType:NSStringPboardType];
+ [string release];
+}
- while (_pending_notify
- && IfEventWithTimeout (x_dpy, &e, 1, selnotify_pred, NULL))
+/* Warning: this frees the propdata in most cases. */
+- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata
+{
+ if (request_atom == atoms->targets && type == atoms->atom)
+ {
+ [self handle_targets:selection propdata:pdata];
+ }
+ else if (type == atoms->image_png)
{
- _pending_notify = NO;
- [self notify_event:&e.xselection];
+ [self handle_png:pdata];
+ }
+ else if (type == atoms->utf8_string)
+ {
+ [self handle_utf8_string:pdata];
+ }
+ else if (type == atoms->string)
+ {
+ [self handle_string:pdata];
+ }
+ else
+ {
+ free_propdata(pdata);
}
-
- if (_pending_notify && request == x_atom_targets)
+
+ if (selection == atoms->clipboard)
{
- /* App didn't respond to request for TARGETS selection. Let's
- try the STRING selection as a last resort.. Helps broken
- applications (e.g. nedit, see #3199867) */
-
- request = XA_STRING;
- goto again;
+ free_propdata(&request_data.propdata);
+ request_data.propdata = *pdata;
+ request_data.type = type;
+
+ /* We greedily take the CLIPBOARD selection whenever it changes. */
+ XSetSelectionOwner (x_dpy, atoms->clipboard, _selection_window,
+ CurrentTime);
+ }
+ else
+ {
+ free_propdata(pdata);
}
-
- _pending_notify = NO;
}
+
+/* NSPasteboard-required methods */
+
- (void) pasteboardChangedOwner:(NSPasteboard *)sender
{
TRACE ();
+ puts("PB changed owner");
+
/* Right now we don't care with this. */
}
@@ -467,6 +714,17 @@ again:
_selection_window = XCreateSimpleWindow (x_dpy, DefaultRootWindow (x_dpy),
0, 0, 1, 1, 0, pixel, pixel);
+ XSelectInput (x_dpy, _selection_window, PropertyChangeMask);
+
+ request_atom = None;
+
+ init_propdata (&pending.propdata);
+ pending.requestor = None;
+ pending.selection = None;
+
+ init_propdata (&request_data.propdata);
+ request_data.type = None;
+
return self;
}
@@ -479,12 +737,15 @@ again:
[_known_types release];
_known_types = nil;
- if (_selection_window != 0)
+ if (None != _selection_window)
{
XDestroyWindow (x_dpy, _selection_window);
- _selection_window = 0;
+ _selection_window = None;
}
+ free_propdata (&pending.propdata);
+ free_propdata (&request_data.propdata);
+
[super dealloc];
}