diff options
author | Jeremy Huddleston <jeremyhu@freedesktop.org> | 2008-09-03 09:16:16 -0700 |
---|---|---|
committer | Jeremy Huddleston <jeremyhu@freedesktop.org> | 2008-09-03 09:16:16 -0700 |
commit | 1f842c71c35db031a24de646429834d6054adf1d (patch) | |
tree | 6bcd389f4b68bdb3ca29593451984d59a1639eff | |
parent | 6bca78760951cb5cb57ea66b7631a2dc230dc27a (diff) |
XQuartz: Added pasteboard proxy code stripped out of quartz-wm.
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | hw/xquartz/Makefile.am | 2 | ||||
-rw-r--r-- | hw/xquartz/pbproxy/Makefile.am | 15 | ||||
-rw-r--r-- | hw/xquartz/pbproxy/main.m | 168 | ||||
-rw-r--r-- | hw/xquartz/pbproxy/pbproxy.h | 41 | ||||
-rw-r--r-- | hw/xquartz/pbproxy/trick_autotools.c | 3 | ||||
-rw-r--r-- | hw/xquartz/pbproxy/x-input.m | 113 | ||||
-rw-r--r-- | hw/xquartz/pbproxy/x-selection.h | 69 | ||||
-rw-r--r-- | hw/xquartz/pbproxy/x-selection.m | 491 |
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 |