summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Huddleston <jeremyhu@freedesktop.org>2008-09-03 09:16:16 -0700
committerJeremy Huddleston <jeremyhu@freedesktop.org>2008-09-03 09:16:16 -0700
commit1f842c71c35db031a24de646429834d6054adf1d (patch)
tree6bcd389f4b68bdb3ca29593451984d59a1639eff
parent6bca78760951cb5cb57ea66b7631a2dc230dc27a (diff)
XQuartz: Added pasteboard proxy code stripped out of quartz-wm.
-rw-r--r--configure.ac1
-rw-r--r--hw/xquartz/Makefile.am2
-rw-r--r--hw/xquartz/pbproxy/Makefile.am15
-rw-r--r--hw/xquartz/pbproxy/main.m168
-rw-r--r--hw/xquartz/pbproxy/pbproxy.h41
-rw-r--r--hw/xquartz/pbproxy/trick_autotools.c3
-rw-r--r--hw/xquartz/pbproxy/x-input.m113
-rw-r--r--hw/xquartz/pbproxy/x-selection.h69
-rw-r--r--hw/xquartz/pbproxy/x-selection.m491
9 files changed, 902 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 36ebc0749..4188985a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2127,6 +2127,7 @@ hw/xquartz/GL/Makefile
hw/xquartz/bundle/Makefile
hw/xquartz/doc/Makefile
hw/xquartz/mach-startup/Makefile
+hw/xquartz/pbproxy/Makefile
hw/xquartz/xpr/Makefile
hw/kdrive/Makefile
hw/kdrive/ati/Makefile
diff --git a/hw/xquartz/Makefile.am b/hw/xquartz/Makefile.am
index be9ad1c8a..e18d53224 100644
--- a/hw/xquartz/Makefile.am
+++ b/hw/xquartz/Makefile.am
@@ -9,7 +9,7 @@ AM_CPPFLAGS = \
-DXFree86Server \
-I$(top_srcdir)/miext/rootless
-SUBDIRS = bundle . GL xpr mach-startup doc
+SUBDIRS = bundle . GL xpr mach-startup doc pbproxy
libXquartz_la_SOURCES = \
$(top_srcdir)/fb/fbcmap_mi.c \
diff --git a/hw/xquartz/pbproxy/Makefile.am b/hw/xquartz/pbproxy/Makefile.am
new file mode 100644
index 000000000..f3fcba03c
--- /dev/null
+++ b/hw/xquartz/pbproxy/Makefile.am
@@ -0,0 +1,15 @@
+AM_CPPFLAGS=-F/System/Library/Frameworks/ApplicationServices.framework/Frameworks
+AM_LDFLAGS=-L/usr/X11/lib -lX11 -lAppleWM -framework AppKit -framework Foundation -framework ApplicationServices
+
+noinst_PROGRAMS = pbproxy
+
+pbproxy_SOURCES = \
+ trick_autotools.c \
+ main.m \
+ x-input.m \
+ x-selection.m
+
+EXTRA_DIST = \
+ pbproxy.h \
+ x-selection.h
+
diff --git a/hw/xquartz/pbproxy/main.m b/hw/xquartz/pbproxy/main.m
new file mode 100644
index 000000000..eaf14a75e
--- /dev/null
+++ b/hw/xquartz/pbproxy/main.m
@@ -0,0 +1,168 @@
+/* main.m
+ $Id: main.m,v 1.29 2007-04-07 20:39:03 jharper Exp $
+
+ Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */
+
+#include "pbproxy.h"
+#import "x-selection.h"
+
+#include <pthread.h>
+
+#include <X11/extensions/applewm.h>
+#include <HIServices/CoreDockServices.h>
+
+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;
+
+static int x_grab_count;
+static Bool x_grab_synced;
+
+static BOOL _is_active = YES; /* FIXME: should query server */
+
+static x_selection *_selection_object;
+
+/* X11 code */
+static void x_error_shutdown(void);
+
+void x_grab_server (Bool sync) {
+ if (x_grab_count++ == 0) {
+ XGrabServer (x_dpy);
+ }
+
+ if (sync && !x_grab_synced) {
+ XSync (x_dpy, False);
+ x_grab_synced = True;
+ }
+}
+
+void x_ungrab_server (void) {
+ if (--x_grab_count == 0) {
+ XUngrabServer (x_dpy);
+ XFlush (x_dpy);
+ x_grab_synced = False;
+ }
+}
+
+static int x_io_error_handler (Display *dpy) {
+ /* We lost our connection to the server. */
+
+ TRACE ();
+
+ x_error_shutdown ();
+
+ return 0;
+}
+
+static void x_init (void) {
+ x_dpy = XOpenDisplay (NULL);
+ if (x_dpy == NULL) {
+ fprintf (stderr, "can't open default display\n");
+ exit (1);
+ }
+
+ 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);
+
+ 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);
+
+ _selection_object = [[x_selection alloc] init];
+
+ x_input_register ();
+ x_input_run ();
+}
+
+static void x_shutdown (void) {
+ [_selection_object release];
+ _selection_object = nil;
+
+ XCloseDisplay (x_dpy);
+ x_dpy = NULL;
+ exit(0);
+}
+
+static void x_error_shutdown (void) {
+ exit(1);
+}
+
+id x_selection_object (void) {
+ return _selection_object;
+}
+
+Time x_current_timestamp (void) {
+ /* FIXME: may want to fetch a timestamp from the server.. */
+ return CurrentTime;
+}
+
+
+/* Finding things */
+BOOL x_get_is_active (void) {
+ return _is_active;
+}
+
+void x_set_is_active (BOOL state) {
+ if (_is_active == state)
+ return;
+
+ _is_active = state;
+}
+
+/* Startup */
+static void signal_handler (int sig) {
+ x_shutdown ();
+}
+
+int main (int argc, const char *argv[]) {
+ NSAutoreleasePool *pool;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ x_init ();
+
+ signal (SIGINT, signal_handler);
+ signal (SIGTERM, signal_handler);
+ signal (SIGPIPE, SIG_IGN);
+
+ while (1) {
+ NS_DURING
+ CFRunLoopRun ();
+ NS_HANDLER
+ NSString *s = [NSString stringWithFormat:@"%@ - %@",
+ [localException name], [localException reason]];
+ fprintf(stderr, "quartz-wm: caught exception: %s\n", [s UTF8String]);
+ NS_ENDHANDLER
+ }
+
+ return 0;
+}
+
+void debug_printf (const char *fmt, ...) {
+ static int spew = -1;
+
+ if (spew == -1) {
+ char *x = getenv ("DEBUG");
+ spew = (x != NULL && atoi (x) != 0);
+ }
+
+ if (spew) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf (stderr, fmt, args);
+ va_end(args);
+ }
+}
diff --git a/hw/xquartz/pbproxy/pbproxy.h b/hw/xquartz/pbproxy/pbproxy.h
new file mode 100644
index 000000000..ddadbb3eb
--- /dev/null
+++ b/hw/xquartz/pbproxy/pbproxy.h
@@ -0,0 +1,41 @@
+/* pbproxy.h
+ Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */
+
+#ifndef PBPROXY_H
+#define PBPROXY_H 1
+
+#import <Foundation/Foundation.h>
+
+#define Cursor X_Cursor
+#undef _SHAPE_H_
+#include <X11/Xlib.h>
+#include <X11/extensions/shape.h>
+#undef Cursor
+
+#define DEBUG 1
+
+/* from main.m */
+extern void x_set_is_active (BOOL state);
+extern BOOL x_get_is_active (void);
+extern id x_selection_object (void);
+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;
+
+/* from x-input.m */
+extern void x_input_register (void);
+extern void x_input_run (void);
+
+#if DEBUG == 0
+# define DB(msg, args...) do {} while (0)
+#else
+# define DB(msg, args...) debug_printf("%s:%s:%d " msg, __FILE__, __FUNCTION__, __LINE__, ##args)
+#endif
+
+#define TRACE() DB("TRACE\n")
+extern void debug_printf (const char *fmt, ...);
+
+#endif /* PBPROXY_H */
diff --git a/hw/xquartz/pbproxy/trick_autotools.c b/hw/xquartz/pbproxy/trick_autotools.c
new file mode 100644
index 000000000..a38f077b1
--- /dev/null
+++ b/hw/xquartz/pbproxy/trick_autotools.c
@@ -0,0 +1,3 @@
+int this_is_just_here_to_make_automake_work() {
+ return 0;
+}
diff --git a/hw/xquartz/pbproxy/x-input.m b/hw/xquartz/pbproxy/x-input.m
new file mode 100644
index 000000000..2e7725024
--- /dev/null
+++ b/hw/xquartz/pbproxy/x-input.m
@@ -0,0 +1,113 @@
+/* x-input.m -- event handling
+ $Id: x-input.m,v 1.26 2007-04-07 20:39:03 jharper Exp $
+
+ Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */
+
+#include "pbproxy.h"
+#import "x-selection.h"
+
+#include <CoreFoundation/CFSocket.h>
+#include <CoreFoundation/CFRunLoop.h>
+
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+#include <X11/extensions/applewm.h>
+
+#include <unistd.h>
+
+/* FIXME: .. */
+CFRunLoopSourceRef x_dpy_source;
+
+/* Timestamp when the X server last told us it's active */
+static Time last_activation_time;
+
+static void x_event_apple_wm_notify(XAppleWMNotifyEvent *e) {
+ switch (e->type - x_apple_wm_event_base) {
+ case AppleWMActivationNotify:
+ switch (e->kind) {
+ case AppleWMIsActive:
+ last_activation_time = e->time;
+ x_set_is_active (YES);
+ [x_selection_object () x_active:e->time];
+ break;
+
+ case AppleWMIsInactive:
+ x_set_is_active (NO);
+ [x_selection_object () x_inactive:e->time];
+ break;
+ }
+ break;
+
+ case AppleWMPasteboardNotify:
+ switch (e->kind) {
+ case AppleWMCopyToPasteboard:
+ [x_selection_object () x_copy:e->time];
+ }
+ break;
+ }
+}
+
+void x_input_run (void) {
+ while (XPending (x_dpy) != 0) {
+ XEvent e;
+
+ XNextEvent (x_dpy, &e);
+
+ switch (e.type) {
+ case SelectionClear:
+ [x_selection_object () clear_event:&e.xselectionclear];
+ break;
+
+ case SelectionRequest:
+ [x_selection_object () request_event:&e.xselectionrequest];
+ break;
+
+ case SelectionNotify:
+ [x_selection_object () notify_event:&e.xselection];
+ break;
+
+ default:
+ if (e.type - x_apple_wm_event_base >= 0
+ && e.type - x_apple_wm_event_base < AppleWMNumberEvents) {
+ x_event_apple_wm_notify ((XAppleWMNotifyEvent *) &e);
+ }
+ break;
+ }
+ }
+}
+
+static int add_input_socket (int sock, CFOptionFlags callback_types,
+ CFSocketCallBack callback, const CFSocketContext *ctx,
+ CFRunLoopSourceRef *cf_source) {
+ CFSocketRef cf_sock;
+
+ cf_sock = CFSocketCreateWithNative (kCFAllocatorDefault, sock,
+ callback_types, callback, ctx);
+ if (cf_sock == NULL) {
+ close (sock);
+ return FALSE;
+ }
+
+ *cf_source = CFSocketCreateRunLoopSource (kCFAllocatorDefault,
+ cf_sock, 0);
+ CFRelease (cf_sock);
+
+ if (*cf_source == NULL)
+ return FALSE;
+
+ CFRunLoopAddSource (CFRunLoopGetCurrent (),
+ *cf_source, kCFRunLoopDefaultMode);
+ return TRUE;
+}
+
+static void x_input_callback (CFSocketRef sock, CFSocketCallBackType type,
+ CFDataRef address, const void *data, void *info) {
+ x_input_run ();
+}
+
+void x_input_register(void) {
+ if (!add_input_socket (ConnectionNumber (x_dpy), kCFSocketReadCallBack,
+ x_input_callback, NULL, &x_dpy_source)) {
+ exit (1);
+ }
+}
diff --git a/hw/xquartz/pbproxy/x-selection.h b/hw/xquartz/pbproxy/x-selection.h
new file mode 100644
index 000000000..b31bf6344
--- /dev/null
+++ b/hw/xquartz/pbproxy/x-selection.h
@@ -0,0 +1,69 @@
+/* x-selection.h -- proxies between NSPasteboard and X11 selections
+ $Id: x-selection.h,v 1.2 2002-12-13 00:21:00 jharper Exp $
+
+ Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
+ HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+ Except as contained in this notice, the name(s) of the above
+ copyright holders shall not be used in advertising or otherwise to
+ promote the sale, use or other dealings in this Software without
+ prior written authorization. */
+
+#ifndef X_SELECTION_H
+#define X_SELECTION_H 1
+
+#include "pbproxy.h"
+#include <AppKit/NSPasteboard.h>
+
+@interface x_selection : NSObject
+{
+@private
+
+ /* The unmapped window we use for fetching selections. */
+ Window _selection_window;
+
+ /* Cached general pasteboard and array of types we can handle. */
+ NSPasteboard *_pasteboard;
+ NSArray *_known_types;
+
+ /* Last time we declared anything on the pasteboard. */
+ int _my_last_change;
+
+ /* Name of the selection we're proxying onto the pasteboard. */
+ Atom _proxied_selection;
+
+ /* When true, we're expecting a SelectionNotify event. */
+ unsigned int _pending_notify :1;
+}
+
+- (void) x_active:(Time)timestamp;
+- (void) x_inactive:(Time)timestamp;
+
+- (void) x_copy:(Time)timestamp;
+
+- (void) clear_event:(XSelectionClearEvent *)e;
+- (void) request_event:(XSelectionRequestEvent *)e;
+- (void) notify_event:(XSelectionEvent *)e;
+
+@end
+
+#endif /* X_SELECTION_H */
diff --git a/hw/xquartz/pbproxy/x-selection.m b/hw/xquartz/pbproxy/x-selection.m
new file mode 100644
index 000000000..5b2ba9cc0
--- /dev/null
+++ b/hw/xquartz/pbproxy/x-selection.m
@@ -0,0 +1,491 @@
+/* x-selection.m -- proxies between NSPasteboard and X11 selections
+ $Id: x-selection.m,v 1.9 2006-07-07 18:24:28 jharper Exp $
+
+ Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
+ HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+ Except as contained in this notice, the name(s) of the above
+ copyright holders shall not be used in advertising or otherwise to
+ promote the sale, use or other dealings in this Software without
+ prior written authorization. */
+
+#import "x-selection.h"
+
+#include <X11/Xatom.h>
+
+#include <unistd.h>
+
+@implementation x_selection
+
+static unsigned long *
+read_prop_32 (Window id, Atom prop, int *nitems_ret)
+{
+ int r, format;
+ Atom type;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+
+ r = XGetWindowProperty (x_dpy, id, prop, 0, 0,
+ False, AnyPropertyType, &type, &format,
+ &nitems, &bytes_after, &data);
+
+ if (r == Success && bytes_after != 0)
+ {
+ XFree (data);
+ r = XGetWindowProperty (x_dpy, id, prop, 0,
+ (bytes_after / 4) + 1, False,
+ AnyPropertyType, &type, &format,
+ &nitems, &bytes_after, &data);
+ }
+
+ if (r != Success)
+ return NULL;
+
+ if (format != 32)
+ {
+ XFree (data);
+ return NULL;
+ }
+
+ *nitems_ret = nitems;
+ return (unsigned long *) data;
+}
+
+float
+get_time (void)
+{
+ extern void Microseconds ();
+ UnsignedWide usec;
+ long long ll;
+
+ Microseconds (&usec);
+ ll = ((long long) usec.hi << 32) | usec.lo;
+
+ return ll / 1e6;
+}
+
+static Bool
+IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
+ Bool (*pred) (Display *, XEvent *, XPointer),
+ XPointer arg)
+{
+ 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;
+
+ } while (start + timeout > get_time ());
+
+ return False;
+}
+
+/* Called when X11 becomes active (i.e. has key focus) */
+- (void) x_active:(Time)timestamp
+{
+ TRACE ();
+
+ if ([_pasteboard changeCount] != _my_last_change)
+ {
+ if ([_pasteboard availableTypeFromArray: _known_types] != nil)
+ {
+ /* 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,
+ _selection_window, timestamp);
+ XSetSelectionOwner (x_dpy, XA_PRIMARY,
+ _selection_window, timestamp);
+ }
+ }
+}
+
+/* Called when X11 loses key focus */
+- (void) x_inactive:(Time)timestamp
+{
+ Window w;
+
+ TRACE ();
+
+ if (_proxied_selection == XA_PRIMARY)
+ return;
+
+ w = XGetSelectionOwner (x_dpy, x_atom_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;
+ }
+}
+
+/* Called when the Edit/Copy item on the main X11 menubar is selected
+ and no appkit window claims it. */
+- (void) x_copy:(Time)timestamp
+{
+ Window w;
+
+ /* Lazily copies the PRIMARY selection to the pasteboard. */
+
+ w = XGetSelectionOwner (x_dpy, XA_PRIMARY);
+
+ if (w != None && w != _selection_window)
+ {
+ XSetSelectionOwner (x_dpy, x_atom_clipboard,
+ _selection_window, timestamp);
+ _my_last_change = [_pasteboard declareTypes:_known_types owner:self];
+ _proxied_selection = XA_PRIMARY;
+ }
+ else
+ {
+ XBell (x_dpy, 0);
+ }
+}
+
+
+/* X events */
+
+- (void) clear_event:(XSelectionClearEvent *)e
+{
+ TRACE ();
+
+ /* Right now we don't care about this. */
+}
+
+static Atom
+convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
+{
+ Atom ret = None;
+
+ if (data == nil)
+ return ret;
+
+ if (target == x_atom_text)
+ target = x_atom_utf8_string;
+
+ if (target == XA_STRING
+ || target == x_atom_cstring
+ || target == x_atom_utf8_string)
+ {
+ const char *bytes;
+
+ if (target == XA_STRING)
+ bytes = [data lossyCString];
+ else
+ bytes = [data UTF8String];
+
+ if (bytes != NULL)
+ {
+ XChangeProperty (x_dpy, e->requestor, prop, target,
+ 8, PropModeReplace, (unsigned char *) bytes,
+ strlen (bytes));
+ ret = prop;
+ }
+ }
+ /* FIXME: handle COMPOUND_TEXT target */
+
+ return ret;
+}
+
+- (void) request_event:(XSelectionRequestEvent *)e
+{
+ /* Someone's asking us for the data on the pasteboard */
+
+ XEvent reply;
+ NSString *data;
+ Atom target;
+
+ TRACE ();
+
+ reply.xselection.type = SelectionNotify;
+ reply.xselection.selection = e->selection;
+ reply.xselection.target = e->target;
+ reply.xselection.requestor = e->requestor;
+ reply.xselection.time = e->time;
+ reply.xselection.property = None;
+
+ target = e->target;
+
+ if (target == x_atom_targets)
+ {
+ long data[2];
+
+ data[0] = x_atom_utf8_string;
+ data[1] = XA_STRING;
+
+ XChangeProperty (x_dpy, e->requestor, e->property, target,
+ 8, PropModeReplace, (unsigned char *) &data,
+ sizeof (data));
+ reply.xselection.property = e->property;
+ }
+ else if (target == x_atom_multiple)
+ {
+ if (e->property != None)
+ {
+ int i, nitems;
+ unsigned long *atoms;
+
+ atoms = read_prop_32 (e->requestor, e->property, &nitems);
+
+ if (atoms != NULL)
+ {
+ data = [_pasteboard stringForType:NSStringPboardType];
+
+ for (i = 0; i < nitems; i += 2)
+ {
+ Atom target = atoms[i], prop = atoms[i+1];
+
+ atoms[i+1] = convert_1 (e, data, target, prop);
+ }
+
+ XChangeProperty (x_dpy, e->requestor, e->property, target,
+ 32, PropModeReplace, (unsigned char *) atoms,
+ nitems);
+ XFree (atoms);
+ }
+ }
+ }
+
+ data = [_pasteboard stringForType:NSStringPboardType];
+ if (data != nil)
+ {
+ reply.xselection.property = convert_1 (e, data, target, e->property);
+ }
+
+ XSendEvent (x_dpy, e->requestor, False, 0, &reply);
+}
+
+- (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;
+ 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)
+ {
+ 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)
+ {
+ free (buf);
+ return;
+ }
+
+ memcpy (buf + offset, data, nitems);
+ XFree (data);
+ }
+ 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];
+
+ free (buf);
+
+ [_pasteboard setString:string forType:NSStringPboardType];
+}
+
+
+/* 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 ();
+
+ /* Right now we don't care with this. */
+}
+
+
+/* Allocation */
+
+- init
+{
+ unsigned long pixel;
+
+ self = [super init];
+ if (self == nil)
+ return nil;
+
+ _pasteboard = [[NSPasteboard generalPasteboard] retain];
+
+ _known_types = [[NSArray arrayWithObject:NSStringPboardType] retain];
+
+ pixel = BlackPixel (x_dpy, DefaultScreen (x_dpy));
+ _selection_window = XCreateSimpleWindow (x_dpy, DefaultRootWindow (x_dpy),
+ 0, 0, 1, 1, 0, pixel, pixel);
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [_pasteboard releaseGlobally];
+ [_pasteboard release];
+ _pasteboard = nil;
+
+ [_known_types release];
+ _known_types = nil;
+
+ if (_selection_window != 0)
+ {
+ XDestroyWindow (x_dpy, _selection_window);
+ _selection_window = 0;
+ }
+
+ [super dealloc];
+}
+
+@end