XQuartz: pbproxy: First round of updates to pbproxy from George.

This commit is contained in:
Jeremy Huddleston 2008-09-14 09:23:17 -07:00
parent e90dabb5a7
commit 5c8b956f8f
5 changed files with 538 additions and 209 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
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);
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;
} 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, atoms->primary);
w = XGetSelectionOwner (x_dpy, XA_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 ();
/* Right now we don't care about this. */
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));
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 ();
if (e->target == x_atom_targets)
{
/* Was trying to fetch the TARGETS property; it lists the
formats supported by the selection owner. */
unsigned long *atoms;
int natoms;
int i, utf8_i = -1, string_i = -1;
if (e->property != None
&& (atoms = read_prop_32 (e->requestor,
e->property, &natoms)) != NULL)
{
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);
}
/* 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;
XConvertSelection (x_dpy, e->selection, type,
e->selection, e->requestor, e->time);
_pending_notify = YES;
struct propdata pdata;
[self release_pending];
puts ("NOTIFY EVENT");
if (None == e->property) {
puts("Nothing");
/* Nothing is selected. */
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. */
buf = malloc (bytes_after + 1);
if (buf == NULL)
return;
for (offset = 0; bytes_after > 0; offset += nitems)
if (is_incr_type (e))
{
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)
/*
* This is an INCR-style transfer, which means that we
* will get the data after a series of PropertyNotify events.
*/
puts("IS INCR");
if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
{
free (buf);
return;
}
memcpy (buf + offset, data, nitems);
XFree (data);
free_propdata (&pdata);
pending.requestor = e->requestor;
pending.selection = e->selection;
}
buf[offset] = 0;
XDeleteProperty (x_dpy, e->requestor, e->property);
else
{
if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
{
return;
}
/* Convert to an NSString and write to the pasteboard. */
puts("HANDLING NOW");
/* We have the complete selection data.*/
[self handle_selection: e->selection type:type propdata:&pdata];
}
}
if (type == XA_STRING)
string = [NSString stringWithCString:(char *) buf];
else /* if (type == x_atom_utf8_string) */
string = [NSString stringWithUTF8String:(char *) buf];
- (void) property_event:(XPropertyEvent *)e
{
struct propdata pdata;
Atom type;
free (buf);
if (None != pending.requestor && PropertyNewValue == e->state)
{
if (get_property (e->window, e->atom, &pdata, /*Delete*/ True, &type))
{
return;
}
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];
}
}
}
- (void) handle_targets: (Atom)selection propdata:(struct propdata *)pdata
{
Atom preferred = find_preferred (pdata);
if (None == preferred)
{
/*
* This isn't required by the ICCCM, but some apps apparently
* don't respond to TARGETS properly.
*/
preferred = XA_STRING;
}
request_atom = preferred;
XConvertSelection (x_dpy, selection, preferred, selection,
_selection_window, CurrentTime);
}
/* This handles the image/png type of selection (typically in CLIPBOARD). */
- (void) handle_png: (struct propdata *)pdata
{
/* TODO Use the NSPasteboard code I wrote that may work... */
}
/* This handles the UTF8_STRING type of selection. */
- (void) handle_utf8_string: (struct propdata *)pdata
{
size_t i;
unsigned char *p = pdata->data;
puts("HANDLE UTF8_STRING");
for (i = 0; i < pdata->length; ++i) {
printf("%c", p[i]);
}
puts("");
NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSUTF8StringEncoding];
[_pasteboard setString:string forType:NSStringPboardType];
[string release];
}
/* This handles the XA_STRING type, which should be in Latin-1. */
- (void) handle_string: (struct propdata *)pdata
{
puts("STRING");
NSString *string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSISOLatin1StringEncoding];
[_pasteboard setString:string forType:NSStringPboardType];
[string release];
}
/* 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)
{
[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 (selection == atoms->clipboard)
{
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);
}
}
/* NSPasteboard-required methods */
static Bool
selnotify_pred (Display *dpy, XEvent *e, XPointer arg)
{
return e->type == SelectionNotify;
}
- (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
{
XEvent e;
Atom request;
TRACE ();
/* Don't ask for the data yet, first find out which formats
the selection owner supports. */
request = x_atom_targets;
again:
XConvertSelection (x_dpy, _proxied_selection, request,
_proxied_selection, _selection_window, CurrentTime);
_pending_notify = YES;
/* 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.. */
while (_pending_notify
&& IfEventWithTimeout (x_dpy, &e, 1, selnotify_pred, NULL))
{
_pending_notify = NO;
[self notify_event:&e.xselection];
}
if (_pending_notify && request == x_atom_targets)
{
/* 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;
}
_pending_notify = NO;
}
- (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];
}