diff options
Diffstat (limited to 'xorg-server/hw/xquartz/pbproxy')
-rw-r--r-- | xorg-server/hw/xquartz/pbproxy/Makefile.am | 56 | ||||
-rw-r--r-- | xorg-server/hw/xquartz/pbproxy/app-main.m | 228 | ||||
-rw-r--r-- | xorg-server/hw/xquartz/pbproxy/main.m | 298 | ||||
-rw-r--r-- | xorg-server/hw/xquartz/pbproxy/pbproxy.h | 186 | ||||
-rw-r--r-- | xorg-server/hw/xquartz/pbproxy/x-input.m | 346 | ||||
-rw-r--r-- | xorg-server/hw/xquartz/pbproxy/x-selection.h | 220 | ||||
-rw-r--r-- | xorg-server/hw/xquartz/pbproxy/x-selection.m | 3174 |
7 files changed, 2254 insertions, 2254 deletions
diff --git a/xorg-server/hw/xquartz/pbproxy/Makefile.am b/xorg-server/hw/xquartz/pbproxy/Makefile.am index e3782ac61..b8b95d232 100644 --- a/xorg-server/hw/xquartz/pbproxy/Makefile.am +++ b/xorg-server/hw/xquartz/pbproxy/Makefile.am @@ -1,28 +1,28 @@ -AM_CPPFLAGS=-F/System/Library/Frameworks/ApplicationServices.framework/Frameworks \
- -DBUNDLE_ID_PREFIX=\"$(BUNDLE_ID_PREFIX)\"
-
-AM_CFLAGS=$(XPBPROXY_CFLAGS)
-
-noinst_LTLIBRARIES = libxpbproxy.la
-libxpbproxy_la_SOURCES = \
- trick_autotools.c \
- main.m \
- x-input.m \
- x-selection.m
-
-libxpbproxy_la_LDFLAGS=$(XPBPROXY_LIBS)
-
-if STANDALONE_XPBPROXY
-
-bin_PROGRAMS = xpbproxy
-xpbproxy_SOURCES = app-main.m
-xpbproxy_LDADD = libxpbproxy.la
-xpbproxy_LDFLAGS = -Wl,-framework,Cocoa
-
-AM_CPPFLAGS += -DSTANDALONE_XPBPROXY
-
-endif
-
-EXTRA_DIST = \
- pbproxy.h \
- x-selection.h
+AM_CPPFLAGS=-F/System/Library/Frameworks/ApplicationServices.framework/Frameworks \ + -DBUNDLE_ID_PREFIX=\"$(BUNDLE_ID_PREFIX)\" + +AM_CFLAGS=$(XPBPROXY_CFLAGS) + +noinst_LTLIBRARIES = libxpbproxy.la +libxpbproxy_la_SOURCES = \ + trick_autotools.c \ + main.m \ + x-input.m \ + x-selection.m + +libxpbproxy_la_LDFLAGS=$(XPBPROXY_LIBS) + +if STANDALONE_XPBPROXY + +bin_PROGRAMS = xpbproxy +xpbproxy_SOURCES = app-main.m +xpbproxy_LDADD = libxpbproxy.la +xpbproxy_LDFLAGS = -Wl,-framework,Cocoa + +AM_CPPFLAGS += -DSTANDALONE_XPBPROXY + +endif + +EXTRA_DIST = \ + pbproxy.h \ + x-selection.h diff --git a/xorg-server/hw/xquartz/pbproxy/app-main.m b/xorg-server/hw/xquartz/pbproxy/app-main.m index 5758a33a7..772c458a1 100644 --- a/xorg-server/hw/xquartz/pbproxy/app-main.m +++ b/xorg-server/hw/xquartz/pbproxy/app-main.m @@ -1,114 +1,114 @@ -/* app-main.m
- Copyright (c) 2002, 2008 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.
- */
-
-#include "pbproxy.h"
-#import "x-selection.h"
-
-#include <pthread.h>
-#include <unistd.h> /*for getpid*/
-#include <Cocoa/Cocoa.h>
-
-static const char *app_prefs_domain = BUNDLE_ID_PREFIX".xpbproxy";
-CFStringRef app_prefs_domain_cfstr;
-
-/* Stubs */
-char *display = NULL;
-BOOL serverRunning = YES;
-pthread_mutex_t serverRunningMutex = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t serverRunningCond = PTHREAD_COND_INITIALIZER;
-
-static void signal_handler (int sig) {
- switch(sig) {
- case SIGHUP:
- xpbproxy_prefs_reload = YES;
- break;
- default:
- _exit(EXIT_SUCCESS);
- }
-}
-
-void
-ErrorF(const char * f, ...)
-{
- va_list args;
-
- va_start(args, f);
- vfprintf(stderr, f, args);
- va_end(args);
-}
-
-/* TODO: Have this actually log to ASL */
-void xq_asl_log (int level, const char *subsystem, const char *file, const char *function, int line, const char *fmt, ...) {
-#ifdef DEBUG
- va_list args;
-
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
-#endif
-}
-
-int main (int argc, const char *argv[]) {
- const char *s;
- int i;
-
-#ifdef DEBUG
- ErrorF("pid: %u\n", getpid());
-#endif
-
- xpbproxy_is_standalone = YES;
-
- if((s = getenv("X11_PREFS_DOMAIN")))
- app_prefs_domain = s;
-
- for (i = 1; i < argc; i++) {
- if(strcmp (argv[i], "--prefs-domain") == 0 && i+1 < argc) {
- app_prefs_domain = argv[++i];
- } else if (strcmp (argv[i], "--help") == 0) {
- ErrorF("usage: xpbproxy OPTIONS\n"
- "Pasteboard proxying for X11.\n\n"
- "--prefs-domain <domain> Change the domain used for reading preferences\n"
- " (default: %s)\n", app_prefs_domain);
- return 0;
- } else {
- ErrorF("usage: xpbproxy OPTIONS...\n"
- "Try 'xpbproxy --help' for more information.\n");
- return 1;
- }
- }
-
- app_prefs_domain_cfstr = CFStringCreateWithCString(NULL, app_prefs_domain, kCFStringEncodingUTF8);
-
- signal (SIGINT, signal_handler);
- signal (SIGTERM, signal_handler);
- signal (SIGHUP, signal_handler);
- signal (SIGPIPE, SIG_IGN);
-
- return xpbproxy_run();
-}
+/* app-main.m + Copyright (c) 2002, 2008 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. + */ + +#include "pbproxy.h" +#import "x-selection.h" + +#include <pthread.h> +#include <unistd.h> /*for getpid*/ +#include <Cocoa/Cocoa.h> + +static const char *app_prefs_domain = BUNDLE_ID_PREFIX".xpbproxy"; +CFStringRef app_prefs_domain_cfstr; + +/* Stubs */ +char *display = NULL; +BOOL serverRunning = YES; +pthread_mutex_t serverRunningMutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t serverRunningCond = PTHREAD_COND_INITIALIZER; + +static void signal_handler (int sig) { + switch(sig) { + case SIGHUP: + xpbproxy_prefs_reload = YES; + break; + default: + _exit(EXIT_SUCCESS); + } +} + +void +ErrorF(const char * f, ...) +{ + va_list args; + + va_start(args, f); + vfprintf(stderr, f, args); + va_end(args); +} + +/* TODO: Have this actually log to ASL */ +void xq_asl_log (int level, const char *subsystem, const char *file, const char *function, int line, const char *fmt, ...) { +#ifdef DEBUG + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +#endif +} + +int main (int argc, const char *argv[]) { + const char *s; + int i; + +#ifdef DEBUG + ErrorF("pid: %u\n", getpid()); +#endif + + xpbproxy_is_standalone = YES; + + if((s = getenv("X11_PREFS_DOMAIN"))) + app_prefs_domain = s; + + for (i = 1; i < argc; i++) { + if(strcmp (argv[i], "--prefs-domain") == 0 && i+1 < argc) { + app_prefs_domain = argv[++i]; + } else if (strcmp (argv[i], "--help") == 0) { + ErrorF("usage: xpbproxy OPTIONS\n" + "Pasteboard proxying for X11.\n\n" + "--prefs-domain <domain> Change the domain used for reading preferences\n" + " (default: %s)\n", app_prefs_domain); + return 0; + } else { + ErrorF("usage: xpbproxy OPTIONS...\n" + "Try 'xpbproxy --help' for more information.\n"); + return 1; + } + } + + app_prefs_domain_cfstr = CFStringCreateWithCString(NULL, app_prefs_domain, kCFStringEncodingUTF8); + + signal (SIGINT, signal_handler); + signal (SIGTERM, signal_handler); + signal (SIGHUP, signal_handler); + signal (SIGPIPE, SIG_IGN); + + return xpbproxy_run(); +} diff --git a/xorg-server/hw/xquartz/pbproxy/main.m b/xorg-server/hw/xquartz/pbproxy/main.m index f8450587e..29ce83b35 100644 --- a/xorg-server/hw/xquartz/pbproxy/main.m +++ b/xorg-server/hw/xquartz/pbproxy/main.m @@ -1,149 +1,149 @@ -/* main.m
- Copyright (c) 2002, 2008 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.
- */
-
-#include "pbproxy.h"
-#import "x-selection.h"
-
-#include <pthread.h>
-#include <unistd.h>
-#include <X11/extensions/applewm.h>
-
-Display *xpbproxy_dpy;
-int xpbproxy_apple_wm_event_base, xpbproxy_apple_wm_error_base;
-int xpbproxy_xfixes_event_base, xpbproxy_xfixes_error_base;
-BOOL xpbproxy_have_xfixes;
-
-extern char *display;
-
-#ifdef STANDALONE_XPBPROXY
-BOOL xpbproxy_is_standalone = NO;
-#endif
-
-x_selection *_selection_object;
-
-extern BOOL serverRunning;
-extern pthread_mutex_t serverRunningMutex;
-extern pthread_cond_t serverRunningCond;
-
-static inline void wait_for_server_init(void) {
- /* If the server hasn't finished initializing, wait for it... */
- if(!serverRunning) {
- pthread_mutex_lock(&serverRunningMutex);
- while(!serverRunning)
- pthread_cond_wait(&serverRunningCond, &serverRunningMutex);
- pthread_mutex_unlock(&serverRunningMutex);
- }
-}
-
-static int x_io_error_handler (Display *dpy) {
- /* We lost our connection to the server. */
-
- TRACE ();
-
- /* trigger the thread to restart?
- * NO - this would be to a "deeper" problem, and restarts would just
- * make things worse...
- */
-#ifdef STANDALONE_XPBPROXY
- if(xpbproxy_is_standalone)
- exit(EXIT_FAILURE);
-#endif
-
- /* Prevent _XIOError from calling exit() */
- pthread_exit(NULL);
- return 0;
-}
-
-static int x_error_handler (Display *dpy, XErrorEvent *errevent) {
- return 0;
-}
-
-int xpbproxy_run (void) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- size_t i;
-
- wait_for_server_init();
-
- for(i=0, xpbproxy_dpy=NULL; !xpbproxy_dpy && i<5; i++) {
- xpbproxy_dpy = XOpenDisplay(NULL);
-
- if(!xpbproxy_dpy && display) {
- char _display[32];
- snprintf(_display, sizeof(_display), ":%s", display);
- setenv("DISPLAY", _display, TRUE);
-
- xpbproxy_dpy=XOpenDisplay(_display);
- }
- if(!xpbproxy_dpy)
- sleep(1);
- }
-
- if (xpbproxy_dpy == NULL) {
- ErrorF("xpbproxy: can't open default display\n");
- [pool release];
- return EXIT_FAILURE;
- }
-
- XSetIOErrorHandler (x_io_error_handler);
- XSetErrorHandler (x_error_handler);
-
- if (!XAppleWMQueryExtension (xpbproxy_dpy, &xpbproxy_apple_wm_event_base,
- &xpbproxy_apple_wm_error_base)) {
- ErrorF("xpbproxy: can't open AppleWM server extension\n");
- [pool release];
- return EXIT_FAILURE;
- }
-
- xpbproxy_have_xfixes = XFixesQueryExtension(xpbproxy_dpy, &xpbproxy_xfixes_event_base, &xpbproxy_xfixes_error_base);
-
- XAppleWMSelectInput (xpbproxy_dpy, AppleWMActivationNotifyMask |
- AppleWMPasteboardNotifyMask);
-
- _selection_object = [[x_selection alloc] init];
-
- if(!xpbproxy_input_register()) {
- [pool release];
- return EXIT_FAILURE;
- }
-
- [pool release];
-
- CFRunLoopRun();
-
- return EXIT_SUCCESS;
-}
-
-id xpbproxy_selection_object (void) {
- return _selection_object;
-}
-
-Time xpbproxy_current_timestamp (void) {
- /* FIXME: may want to fetch a timestamp from the server.. */
- return CurrentTime;
-}
+/* main.m + Copyright (c) 2002, 2008 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. + */ + +#include "pbproxy.h" +#import "x-selection.h" + +#include <pthread.h> +#include <unistd.h> +#include <X11/extensions/applewm.h> + +Display *xpbproxy_dpy; +int xpbproxy_apple_wm_event_base, xpbproxy_apple_wm_error_base; +int xpbproxy_xfixes_event_base, xpbproxy_xfixes_error_base; +BOOL xpbproxy_have_xfixes; + +extern char *display; + +#ifdef STANDALONE_XPBPROXY +BOOL xpbproxy_is_standalone = NO; +#endif + +x_selection *_selection_object; + +extern BOOL serverRunning; +extern pthread_mutex_t serverRunningMutex; +extern pthread_cond_t serverRunningCond; + +static inline void wait_for_server_init(void) { + /* If the server hasn't finished initializing, wait for it... */ + if(!serverRunning) { + pthread_mutex_lock(&serverRunningMutex); + while(!serverRunning) + pthread_cond_wait(&serverRunningCond, &serverRunningMutex); + pthread_mutex_unlock(&serverRunningMutex); + } +} + +static int x_io_error_handler (Display *dpy) { + /* We lost our connection to the server. */ + + TRACE (); + + /* trigger the thread to restart? + * NO - this would be to a "deeper" problem, and restarts would just + * make things worse... + */ +#ifdef STANDALONE_XPBPROXY + if(xpbproxy_is_standalone) + exit(EXIT_FAILURE); +#endif + + /* Prevent _XIOError from calling exit() */ + pthread_exit(NULL); + return 0; +} + +static int x_error_handler (Display *dpy, XErrorEvent *errevent) { + return 0; +} + +int xpbproxy_run (void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + size_t i; + + wait_for_server_init(); + + for(i=0, xpbproxy_dpy=NULL; !xpbproxy_dpy && i<5; i++) { + xpbproxy_dpy = XOpenDisplay(NULL); + + if(!xpbproxy_dpy && display) { + char _display[32]; + snprintf(_display, sizeof(_display), ":%s", display); + setenv("DISPLAY", _display, TRUE); + + xpbproxy_dpy=XOpenDisplay(_display); + } + if(!xpbproxy_dpy) + sleep(1); + } + + if (xpbproxy_dpy == NULL) { + ErrorF("xpbproxy: can't open default display\n"); + [pool release]; + return EXIT_FAILURE; + } + + XSetIOErrorHandler (x_io_error_handler); + XSetErrorHandler (x_error_handler); + + if (!XAppleWMQueryExtension (xpbproxy_dpy, &xpbproxy_apple_wm_event_base, + &xpbproxy_apple_wm_error_base)) { + ErrorF("xpbproxy: can't open AppleWM server extension\n"); + [pool release]; + return EXIT_FAILURE; + } + + xpbproxy_have_xfixes = XFixesQueryExtension(xpbproxy_dpy, &xpbproxy_xfixes_event_base, &xpbproxy_xfixes_error_base); + + XAppleWMSelectInput (xpbproxy_dpy, AppleWMActivationNotifyMask | + AppleWMPasteboardNotifyMask); + + _selection_object = [[x_selection alloc] init]; + + if(!xpbproxy_input_register()) { + [pool release]; + return EXIT_FAILURE; + } + + [pool release]; + + CFRunLoopRun(); + + return EXIT_SUCCESS; +} + +id xpbproxy_selection_object (void) { + return _selection_object; +} + +Time xpbproxy_current_timestamp (void) { + /* FIXME: may want to fetch a timestamp from the server.. */ + return CurrentTime; +} diff --git a/xorg-server/hw/xquartz/pbproxy/pbproxy.h b/xorg-server/hw/xquartz/pbproxy/pbproxy.h index 9d729531f..c80a660cc 100644 --- a/xorg-server/hw/xquartz/pbproxy/pbproxy.h +++ b/xorg-server/hw/xquartz/pbproxy/pbproxy.h @@ -1,93 +1,93 @@ -/* pbproxy.h
- Copyright (c) 2002, 2008 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 PBPROXY_H
-#define PBPROXY_H 1
-
-#import <Foundation/Foundation.h>
-
-#include <asl.h>
-
-#include <AvailabilityMacros.h>
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
-#if __LP64__ || NS_BUILD_32_LIKE_64
-typedef long NSInteger;
-typedef unsigned long NSUInteger;
-#else
-typedef int NSInteger;
-typedef unsigned int NSUInteger;
-#endif
-#endif
-
-#define Cursor X_Cursor
-#undef _SHAPE_H_
-#include <X11/Xlib.h>
-#include <X11/extensions/shape.h>
-#undef Cursor
-
-#ifdef STANDALONE_XPBPROXY
-/* Just used for the standalone to respond to SIGHUP to reload prefs */
-extern BOOL xpbproxy_prefs_reload;
-
-/* Setting this to YES (for the standalone app) causes us to ignore the
- * 'sync_pasteboard' defaults preference since we assume it to be on... this is
- * mainly useful for debugging/developing xpbproxy with XQuartz still running.
- * Just disable the one in the server with X11's preference pane, then run
- * the standalone app.
- */
-extern BOOL xpbproxy_is_standalone;
-#endif
-
-/* from main.m */
-extern void xpbproxy_set_is_active (BOOL state);
-extern BOOL xpbproxy_get_is_active (void);
-extern id xpbproxy_selection_object (void);
-extern Time xpbproxy_current_timestamp (void);
-extern int xpbproxy_run (void);
-
-extern Display *xpbproxy_dpy;
-extern int xpbproxy_apple_wm_event_base, xpbproxy_apple_wm_error_base;
-extern int xpbproxy_xfixes_event_base, xpbproxy_xfixes_error_base;
-extern BOOL xpbproxy_have_xfixes;
-
-/* from x-input.m */
-extern BOOL xpbproxy_input_register (void);
-
-/* os/log.c or app-main.m */
-extern void ErrorF(const char *f, ...) _X_ATTRIBUTE_PRINTF(1,2);
-
-/* from darwin.h */
-_X_ATTRIBUTE_PRINTF(6,7)
-extern void xq_asl_log (int level, const char *subsystem, const char *file, const char *function, int line, const char *fmt, ...);
-
-#define ASL_LOG(level, subsystem, msg, args...) xq_asl_log(level, subsystem, __FILE__, __FUNCTION__, __LINE__, msg, ##args)
-#define DebugF(msg, args...) ASL_LOG(ASL_LEVEL_DEBUG, "xpbproxy", msg, ##args)
-#define TRACE() DebugF("TRACE")
-
-#endif /* PBPROXY_H */
+/* pbproxy.h + Copyright (c) 2002, 2008 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 PBPROXY_H +#define PBPROXY_H 1 + +#import <Foundation/Foundation.h> + +#include <asl.h> + +#include <AvailabilityMacros.h> +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +#if __LP64__ || NS_BUILD_32_LIKE_64 +typedef long NSInteger; +typedef unsigned long NSUInteger; +#else +typedef int NSInteger; +typedef unsigned int NSUInteger; +#endif +#endif + +#define Cursor X_Cursor +#undef _SHAPE_H_ +#include <X11/Xlib.h> +#include <X11/extensions/shape.h> +#undef Cursor + +#ifdef STANDALONE_XPBPROXY +/* Just used for the standalone to respond to SIGHUP to reload prefs */ +extern BOOL xpbproxy_prefs_reload; + +/* Setting this to YES (for the standalone app) causes us to ignore the + * 'sync_pasteboard' defaults preference since we assume it to be on... this is + * mainly useful for debugging/developing xpbproxy with XQuartz still running. + * Just disable the one in the server with X11's preference pane, then run + * the standalone app. + */ +extern BOOL xpbproxy_is_standalone; +#endif + +/* from main.m */ +extern void xpbproxy_set_is_active (BOOL state); +extern BOOL xpbproxy_get_is_active (void); +extern id xpbproxy_selection_object (void); +extern Time xpbproxy_current_timestamp (void); +extern int xpbproxy_run (void); + +extern Display *xpbproxy_dpy; +extern int xpbproxy_apple_wm_event_base, xpbproxy_apple_wm_error_base; +extern int xpbproxy_xfixes_event_base, xpbproxy_xfixes_error_base; +extern BOOL xpbproxy_have_xfixes; + +/* from x-input.m */ +extern BOOL xpbproxy_input_register (void); + +/* os/log.c or app-main.m */ +extern void ErrorF(const char *f, ...) _X_ATTRIBUTE_PRINTF(1,2); + +/* from darwin.h */ +_X_ATTRIBUTE_PRINTF(6,7) +extern void xq_asl_log (int level, const char *subsystem, const char *file, const char *function, int line, const char *fmt, ...); + +#define ASL_LOG(level, subsystem, msg, args...) xq_asl_log(level, subsystem, __FILE__, __FUNCTION__, __LINE__, msg, ##args) +#define DebugF(msg, args...) ASL_LOG(ASL_LEVEL_DEBUG, "xpbproxy", msg, ##args) +#define TRACE() DebugF("TRACE") + +#endif /* PBPROXY_H */ diff --git a/xorg-server/hw/xquartz/pbproxy/x-input.m b/xorg-server/hw/xquartz/pbproxy/x-input.m index 7ecc90896..ebb89980f 100644 --- a/xorg-server/hw/xquartz/pbproxy/x-input.m +++ b/xorg-server/hw/xquartz/pbproxy/x-input.m @@ -1,173 +1,173 @@ -/* x-input.m -- event handling
- Copyright (c) 2002, 2008 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.
- */
-
-#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>
-
-static CFRunLoopSourceRef xpbproxy_dpy_source;
-
-#ifdef STANDALONE_XPBPROXY
-BOOL xpbproxy_prefs_reload = NO;
-#endif
-
-/* 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) {
- int type = e->type - xpbproxy_apple_wm_event_base;
- int kind = e->kind;
-
- /* We want to reload prefs even if we're not active */
- if(type == AppleWMActivationNotify &&
- kind == AppleWMReloadPreferences)
- [xpbproxy_selection_object() reload_preferences];
-
- if(![xpbproxy_selection_object() is_active])
- return;
-
- switch (type) {
- case AppleWMActivationNotify:
- switch (kind) {
- case AppleWMIsActive:
- last_activation_time = e->time;
- [xpbproxy_selection_object() x_active:e->time];
- break;
-
- case AppleWMIsInactive:
- [xpbproxy_selection_object() x_inactive:e->time];
- break;
- }
- break;
-
- case AppleWMPasteboardNotify:
- switch (kind) {
- case AppleWMCopyToPasteboard:
- [xpbproxy_selection_object() x_copy:e->time];
- }
- break;
- }
-}
-
-static void xpbproxy_process_xevents(void) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- if(pool == nil) {
- ErrorF("unable to allocate/init auto release pool!\n");
- return;
- }
-
- while (XPending(xpbproxy_dpy) != 0) {
- XEvent e;
-
- XNextEvent (xpbproxy_dpy, &e);
-
- switch (e.type) {
- case SelectionClear:
- if([xpbproxy_selection_object() is_active])
- [xpbproxy_selection_object () clear_event:&e.xselectionclear];
- break;
-
- case SelectionRequest:
- [xpbproxy_selection_object () request_event:&e.xselectionrequest];
- break;
-
- case SelectionNotify:
- [xpbproxy_selection_object () notify_event:&e.xselection];
- break;
-
- case PropertyNotify:
- [xpbproxy_selection_object () property_event:&e.xproperty];
- break;
-
- default:
- if(e.type >= xpbproxy_apple_wm_event_base &&
- e.type < xpbproxy_apple_wm_event_base + AppleWMNumberEvents) {
- x_event_apple_wm_notify((XAppleWMNotifyEvent *) &e);
- } else if(e.type == xpbproxy_xfixes_event_base + XFixesSelectionNotify) {
- [xpbproxy_selection_object() xfixes_selection_notify:(XFixesSelectionNotifyEvent *)&e];
- }
- break;
- }
-
- XFlush(xpbproxy_dpy);
- }
-
- [pool release];
-}
-
-static BOOL 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) {
-
-#ifdef STANDALONE_XPBPROXY
- if(xpbproxy_prefs_reload) {
- [xpbproxy_selection_object() reload_preferences];
- xpbproxy_prefs_reload = NO;
- }
-#endif
-
- xpbproxy_process_xevents();
-}
-
-BOOL xpbproxy_input_register(void) {
- return add_input_socket(ConnectionNumber(xpbproxy_dpy), kCFSocketReadCallBack,
- x_input_callback, NULL, &xpbproxy_dpy_source);
-}
+/* x-input.m -- event handling + Copyright (c) 2002, 2008 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. + */ + +#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> + +static CFRunLoopSourceRef xpbproxy_dpy_source; + +#ifdef STANDALONE_XPBPROXY +BOOL xpbproxy_prefs_reload = NO; +#endif + +/* 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) { + int type = e->type - xpbproxy_apple_wm_event_base; + int kind = e->kind; + + /* We want to reload prefs even if we're not active */ + if(type == AppleWMActivationNotify && + kind == AppleWMReloadPreferences) + [xpbproxy_selection_object() reload_preferences]; + + if(![xpbproxy_selection_object() is_active]) + return; + + switch (type) { + case AppleWMActivationNotify: + switch (kind) { + case AppleWMIsActive: + last_activation_time = e->time; + [xpbproxy_selection_object() x_active:e->time]; + break; + + case AppleWMIsInactive: + [xpbproxy_selection_object() x_inactive:e->time]; + break; + } + break; + + case AppleWMPasteboardNotify: + switch (kind) { + case AppleWMCopyToPasteboard: + [xpbproxy_selection_object() x_copy:e->time]; + } + break; + } +} + +static void xpbproxy_process_xevents(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if(pool == nil) { + ErrorF("unable to allocate/init auto release pool!\n"); + return; + } + + while (XPending(xpbproxy_dpy) != 0) { + XEvent e; + + XNextEvent (xpbproxy_dpy, &e); + + switch (e.type) { + case SelectionClear: + if([xpbproxy_selection_object() is_active]) + [xpbproxy_selection_object () clear_event:&e.xselectionclear]; + break; + + case SelectionRequest: + [xpbproxy_selection_object () request_event:&e.xselectionrequest]; + break; + + case SelectionNotify: + [xpbproxy_selection_object () notify_event:&e.xselection]; + break; + + case PropertyNotify: + [xpbproxy_selection_object () property_event:&e.xproperty]; + break; + + default: + if(e.type >= xpbproxy_apple_wm_event_base && + e.type < xpbproxy_apple_wm_event_base + AppleWMNumberEvents) { + x_event_apple_wm_notify((XAppleWMNotifyEvent *) &e); + } else if(e.type == xpbproxy_xfixes_event_base + XFixesSelectionNotify) { + [xpbproxy_selection_object() xfixes_selection_notify:(XFixesSelectionNotifyEvent *)&e]; + } + break; + } + + XFlush(xpbproxy_dpy); + } + + [pool release]; +} + +static BOOL 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) { + +#ifdef STANDALONE_XPBPROXY + if(xpbproxy_prefs_reload) { + [xpbproxy_selection_object() reload_preferences]; + xpbproxy_prefs_reload = NO; + } +#endif + + xpbproxy_process_xevents(); +} + +BOOL xpbproxy_input_register(void) { + return add_input_socket(ConnectionNumber(xpbproxy_dpy), kCFSocketReadCallBack, + x_input_callback, NULL, &xpbproxy_dpy_source); +} diff --git a/xorg-server/hw/xquartz/pbproxy/x-selection.h b/xorg-server/hw/xquartz/pbproxy/x-selection.h index b31d7515e..d4a39e58f 100644 --- a/xorg-server/hw/xquartz/pbproxy/x-selection.h +++ b/xorg-server/hw/xquartz/pbproxy/x-selection.h @@ -1,110 +1,110 @@ -/* x-selection.h -- proxies between NSPasteboard and X11 selections
-
- Copyright (c) 2002, 2008 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"
-
-#define Cursor X_Cursor
-#include <X11/extensions/Xfixes.h>
-#undef Cursor
-
-#include <AppKit/NSPasteboard.h>
-
-/* This stores image data or text. */
-struct propdata {
- unsigned char *data;
- size_t length;
- int format;
-};
-
-struct atom_list {
- Atom primary, clipboard, text, utf8_string, string, targets, multiple,
- cstring, image_png, image_jpeg, incr, atom, clipboard_manager,
- compound_text, atom_pair;
-};
-
-
-@interface x_selection : NSObject
-{
-@private
-
- /* The unmapped window we use for fetching selections. */
- Window _selection_window;
-
- Atom request_atom;
-
- struct {
- struct propdata propdata;
- Window requestor;
- Atom selection;
- } pending;
-
- /*
- * This is the number of times the user has requested a copy.
- * Once the copy is completed, we --pending_copy, and if the
- * pending_copy is > 0 we do it again.
- */
- int pending_copy;
- /*
- * This is used for the same purpose as pending_copy, but for the
- * CLIPBOARD. It also prevents a race with INCR transfers.
- */
- int pending_clipboard;
-
- struct atom_list atoms[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;
-- (void) property_event:(XPropertyEvent *)e;
-- (void) xfixes_selection_notify:(XFixesSelectionNotifyEvent *)e;
-- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata;
-- (void) claim_clipboard;
-- (BOOL) set_clipboard_manager_status:(BOOL)value;
-- (void) own_clipboard;
-- (void) copy_completed:(Atom)selection;
-
-- (void) reload_preferences;
-- (BOOL) is_active;
-- (void) send_none:(XSelectionRequestEvent *)e;
-@end
-
-/* main.m */
-extern x_selection *_selection_object;
-
-#endif /* X_SELECTION_H */
+/* x-selection.h -- proxies between NSPasteboard and X11 selections + + Copyright (c) 2002, 2008 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" + +#define Cursor X_Cursor +#include <X11/extensions/Xfixes.h> +#undef Cursor + +#include <AppKit/NSPasteboard.h> + +/* This stores image data or text. */ +struct propdata { + unsigned char *data; + size_t length; + int format; +}; + +struct atom_list { + Atom primary, clipboard, text, utf8_string, string, targets, multiple, + cstring, image_png, image_jpeg, incr, atom, clipboard_manager, + compound_text, atom_pair; +}; + + +@interface x_selection : NSObject +{ +@private + + /* The unmapped window we use for fetching selections. */ + Window _selection_window; + + Atom request_atom; + + struct { + struct propdata propdata; + Window requestor; + Atom selection; + } pending; + + /* + * This is the number of times the user has requested a copy. + * Once the copy is completed, we --pending_copy, and if the + * pending_copy is > 0 we do it again. + */ + int pending_copy; + /* + * This is used for the same purpose as pending_copy, but for the + * CLIPBOARD. It also prevents a race with INCR transfers. + */ + int pending_clipboard; + + struct atom_list atoms[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; +- (void) property_event:(XPropertyEvent *)e; +- (void) xfixes_selection_notify:(XFixesSelectionNotifyEvent *)e; +- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata; +- (void) claim_clipboard; +- (BOOL) set_clipboard_manager_status:(BOOL)value; +- (void) own_clipboard; +- (void) copy_completed:(Atom)selection; + +- (void) reload_preferences; +- (BOOL) is_active; +- (void) send_none:(XSelectionRequestEvent *)e; +@end + +/* main.m */ +extern x_selection *_selection_object; + +#endif /* X_SELECTION_H */ diff --git a/xorg-server/hw/xquartz/pbproxy/x-selection.m b/xorg-server/hw/xquartz/pbproxy/x-selection.m index 0f48371d0..b5f4dde5c 100644 --- a/xorg-server/hw/xquartz/pbproxy/x-selection.m +++ b/xorg-server/hw/xquartz/pbproxy/x-selection.m @@ -1,1587 +1,1587 @@ -/* x-selection.m -- proxies between NSPasteboard and X11 selections
-
- Copyright (c) 2002, 2008 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 <stdio.h>
-#include <stdlib.h>
-#include <X11/Xatom.h>
-#include <X11/Xutil.h>
-#import <AppKit/NSGraphics.h>
-#import <AppKit/NSImage.h>
-#import <AppKit/NSBitmapImageRep.h>
-
-/*
- * The basic design of the pbproxy code is as follows.
- *
- * When a client selects text, say from an xterm - we only copy it when the
- * X11 Edit->Copy menu item is pressed or the shortcut activated. In this
- * case we take the PRIMARY selection, and set it as the NSPasteboard data.
- *
- * When an X11 client copies something to the CLIPBOARD, pbproxy greedily grabs
- * the data, sets it as the NSPasteboard data, and finally sets itself as
- * owner of the CLIPBOARD.
- *
- * When an X11 window is activated we check to see if the NSPasteboard has
- * changed. If the NSPasteboard has changed, then we set pbproxy as owner
- * of the PRIMARY and CLIPBOARD and respond to requests for text and images.
- *
- * The behavior is now dynamic since the information above was written.
- * The behavior is now dependent on the pbproxy_prefs below.
- */
-
-/*
- * TODO:
- * 1. handle MULTIPLE - I need to study the ICCCM further, and find a test app.
- * 2. Handle NSPasteboard updates immediately, not on active/inactive
- * - Open xterm, run 'cat readme.txt | pbcopy'
- */
-
-static struct {
- BOOL active ;
- BOOL primary_on_grab; /* This is provided as an option for people who
- * want it and has issues that won't ever be
- * addressed to make it *always* work.
- */
- BOOL clipboard_to_pasteboard;
- BOOL pasteboard_to_primary;
- BOOL pasteboard_to_clipboard;
-} pbproxy_prefs = { YES, NO, YES, YES, YES };
-
-@implementation x_selection
-
-static struct propdata null_propdata = {NULL, 0, 0};
-
-#ifdef DEBUG
-static void
-dump_prefs() {
- ErrorF(fp,
- "pbproxy preferences:\n"
- "\tactive %u\n"
- "\tprimary_on_grab %u\n"
- "\tclipboard_to_pasteboard %u\n"
- "\tpasteboard_to_primary %u\n"
- "\tpasteboard_to_clipboard %u\n",
- pbproxy_prefs.active,
- pbproxy_prefs.primary_on_grab,
- pbproxy_prefs.clipboard_to_pasteboard,
- pbproxy_prefs.pasteboard_to_primary,
- pbproxy_prefs.pasteboard_to_clipboard);
-}
-#endif
-
-extern CFStringRef app_prefs_domain_cfstr;
-
-static BOOL
-prefs_get_bool (CFStringRef key, BOOL defaultValue) {
- Boolean value, ok;
-
- value = CFPreferencesGetAppBooleanValue (key, app_prefs_domain_cfstr, &ok);
-
- return ok ? (BOOL) value : defaultValue;
-}
-
-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 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;
-
- TRACE ();
-
- if(None == property)
- return True;
-
- do
- {
- unsigned long newbuflen = 0;
- unsigned char *newbuf = NULL;
-
-#ifdef TEST
- ErrorF("bytesleft %lu\n", bytesleft);
-#endif
-
- if (Success != XGetWindowProperty (xpbproxy_dpy, win, property,
- offset, length, delete,
- AnyPropertyType,
- type, &format, &numitems,
- &bytesleft, &chunk))
- {
- DebugF ("Error while getting window property.\n");
- *pdata = null_propdata;
- free (buf);
- return True;
- }
-
-#ifdef TEST
- ErrorF("format %d numitems %lu bytesleft %lu\n",
- format, numitems, bytesleft);
-
- ErrorF("type %s\n", XGetAtomName (xpbproxy_dpy, *type));
-#endif
-
- /* Format is the number of bits. */
- if (format == 8)
- chunkbytesize = numitems;
- else if (format == 16)
- chunkbytesize = numitems * sizeof(short);
- else if (format == 32)
- chunkbytesize = numitems * sizeof(long);
-
-#ifdef TEST
- ErrorF("chunkbytesize %zu\n", chunkbytesize);
-#endif
- newbuflen = buflen + chunkbytesize;
- if (newbuflen > 0)
- {
- 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;
- }
- else
- {
- if (chunk)
- XFree (chunk);
- }
-
-#ifdef TEST
- ErrorF("bytesleft %lu\n", bytesleft);
-#endif
- } while (bytesleft > 0);
-
- pdata->data = buf;
- pdata->length = buflen;
- pdata->format = format;
-
- return /*success*/ False;
-}
-
-
-/* Implementation methods */
-
-/* This finds the preferred type from a TARGETS list.*/
-- (Atom) find_preferred:(struct propdata *)pdata
-{
- Atom a = None;
- size_t i, step;
- Bool png = False, jpeg = False, utf8 = False, string = False;
-
- TRACE ();
-
- if (pdata->format != 32)
- {
- ErrorF("Atom list is expected to be formatted as an array of 32bit values.\n");
- return None;
- }
-
- for (i = 0, step = sizeof(long); i < pdata->length; i += step)
- {
- a = (Atom)*(long *)(pdata->data + i);
-
- if (a == atoms->image_png)
- {
- png = True;
- }
- else if (a == atoms->image_jpeg)
- {
- jpeg = True;
- }
- else if (a == atoms->utf8_string)
- {
- utf8 = True;
- }
- else if (a == atoms->string)
- {
- string = True;
- }
- else
- {
- char *type = XGetAtomName(xpbproxy_dpy, a);
- if (type)
- {
- DebugF("Unhandled X11 mime type: %s", type);
- XFree(type);
- }
- }
- }
-
- /*We prefer PNG over strings, and UTF8 over a Latin-1 string.*/
- if (png)
- return atoms->image_png;
-
- if (jpeg)
- return atoms->image_jpeg;
-
- 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 this is an INCR-style transfer. */
-- (Bool) is_incr_type:(XSelectionEvent *)e
-{
- Atom seltype;
- int format;
- unsigned long numitems = 0UL, bytesleft = 0UL;
- unsigned char *chunk;
-
- TRACE ();
-
- if (Success != XGetWindowProperty (xpbproxy_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 should be called after a selection has been copied,
- * or when the selection is unfinished before a transfer completes.
- */
-- (void) release_pending
-{
- TRACE ();
-
- free_propdata (&pending.propdata);
- pending.requestor = None;
- pending.selection = None;
-}
-
-/* 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
-{
- unsigned char *newdata;
- size_t newlength;
-
- TRACE ();
-
- if (requestor != pending.requestor)
- {
- [self release_pending];
- pending.requestor = requestor;
- }
-
- newlength = pending.propdata.length + pdata->length;
- newdata = realloc(pending.propdata.data, newlength);
-
- 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
-{
- static NSInteger changeCount;
- NSInteger countNow;
- NSPasteboard *pb;
-
- TRACE ();
-
- pb = [NSPasteboard generalPasteboard];
-
- if (nil == pb)
- return;
-
- countNow = [pb changeCount];
-
- if (countNow != changeCount)
- {
- DebugF ("changed pasteboard!\n");
- changeCount = countNow;
-
- if (pbproxy_prefs.pasteboard_to_primary)
- {
- XSetSelectionOwner (xpbproxy_dpy, atoms->primary, _selection_window, CurrentTime);
- }
-
- if (pbproxy_prefs.pasteboard_to_clipboard) {
- [self own_clipboard];
- }
- }
-
-#if 0
- /*gstaplin: we should perhaps investigate something like this branch above...*/
- 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 (xpbproxy_dpy, atoms->clipboard,
- _selection_window, timestamp);
- XSetSelectionOwner (xpbproxy_dpy, atoms->primary,
- _selection_window, timestamp);
- }
-#endif
-}
-
-/* Called when X11 loses key focus */
-- (void) x_inactive:(Time)timestamp
-{
- TRACE ();
-}
-
-/* This requests the TARGETS list from the PRIMARY selection owner. */
-- (void) x_copy_request_targets
-{
- TRACE ();
-
- request_atom = atoms->targets;
- XConvertSelection (xpbproxy_dpy, atoms->primary, atoms->targets,
- atoms->primary, _selection_window, CurrentTime);
-}
-
-/* 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;
-
- TRACE ();
-
- w = XGetSelectionOwner (xpbproxy_dpy, atoms->primary);
-
- if (None != w)
- {
- ++pending_copy;
-
- if (1 == pending_copy) {
- /*
- * There are no other copy operations in progress, so we
- * can proceed safely. Otherwise the copy_completed method
- * will see that the pending_copy is > 1, and do another copy.
- */
- [self x_copy_request_targets];
- }
- }
-}
-
-/* Set pbproxy as owner of the SELECTION_MANAGER selection.
- * This prevents tools like xclipboard from causing havoc.
- * Returns TRUE on success
- */
-- (BOOL) set_clipboard_manager_status:(BOOL)value
-{
- TRACE ();
-
- Window owner = XGetSelectionOwner (xpbproxy_dpy, atoms->clipboard_manager);
-
- if(value) {
- if(owner == _selection_window)
- return TRUE;
-
- if(owner != None) {
- ErrorF("A clipboard manager using window 0x%lx already owns the clipboard selection. "
- "pbproxy will not sync clipboard to pasteboard.\n", owner);
- return FALSE;
- }
-
- XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager, _selection_window, CurrentTime);
- return (_selection_window == XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager));
- } else {
- if(owner != _selection_window)
- return TRUE;
-
- XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager, None, CurrentTime);
- return(None == XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager));
- }
-
- return FALSE;
-}
-
-/*
- * This occurs when we previously owned a selection,
- * and then lost it from another client.
- */
-- (void) clear_event:(XSelectionClearEvent *)e
-{
-
-
- TRACE ();
-
- DebugF ("e->selection %s\n", XGetAtomName (xpbproxy_dpy, e->selection));
-
- if(e->selection == atoms->clipboard) {
- /*
- * We lost ownership of the CLIPBOARD.
- */
- ++pending_clipboard;
-
- if (1 == pending_clipboard) {
- /* Claim the clipboard contents from the new owner. */
- [self claim_clipboard];
- }
- } else if(e->selection == atoms->clipboard_manager) {
- if(pbproxy_prefs.clipboard_to_pasteboard) {
- /* Another CLIPBOARD_MANAGER has set itself as owner. Disable syncing
- * to avoid a race.
- */
- ErrorF("Another clipboard manager was started! "
- "xpbproxy is disabling syncing with clipboard.\n");
- pbproxy_prefs.clipboard_to_pasteboard = NO;
- }
- }
-}
-
-/*
- * We greedily acquire the clipboard after it changes, and on startup.
- */
-- (void) claim_clipboard
-{
- Window owner;
-
- TRACE ();
-
- if (!pbproxy_prefs.clipboard_to_pasteboard)
- return;
-
- owner = XGetSelectionOwner (xpbproxy_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.
- */
- DebugF ("No clipboard owner.\n");
- [self copy_completed:atoms->clipboard];
- return;
- } else if (owner == _selection_window) {
- [self copy_completed:atoms->clipboard];
- return;
- }
-
- DebugF ("requesting targets\n");
-
- request_atom = atoms->targets;
- XConvertSelection (xpbproxy_dpy, atoms->clipboard, atoms->targets,
- atoms->clipboard, _selection_window, CurrentTime);
- XFlush (xpbproxy_dpy);
- /* Now we will get a SelectionNotify event in the future. */
-}
-
-/* Greedily acquire the clipboard. */
-- (void) own_clipboard
-{
-
- TRACE ();
-
- /* We should perhaps have a boundary limit on the number of iterations... */
- do
- {
- XSetSelectionOwner (xpbproxy_dpy, atoms->clipboard, _selection_window,
- CurrentTime);
- } while (_selection_window != XGetSelectionOwner (xpbproxy_dpy,
- atoms->clipboard));
-}
-
-- (void) init_reply:(XEvent *)reply request:(XSelectionRequestEvent *)e
-{
- 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;
-}
-
-- (void) send_reply:(XEvent *)reply
-{
- /*
- * We are supposed to use an empty event mask, and not propagate
- * the event, according to the ICCCM.
- */
- DebugF ("reply->xselection.requestor 0x%lx\n", reply->xselection.requestor);
-
- XSendEvent (xpbproxy_dpy, reply->xselection.requestor, False, 0, reply);
- XFlush (xpbproxy_dpy);
-}
-
-/*
- * This responds to a TARGETS request.
- * The result is a list of a ATOMs that correspond to the types available
- * for a selection.
- * For instance an application might provide a UTF8_STRING and a STRING
- * (in Latin-1 encoding). The requestor can then make the choice based on
- * the list.
- */
-- (void) send_targets:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)pb
-{
- XEvent reply;
- NSArray *pbtypes;
-
- [self init_reply:&reply request:e];
-
- pbtypes = [pb types];
- if (pbtypes)
- {
- long list[7]; /* Don't forget to increase this if we handle more types! */
- long count = 0;
-
- /*
- * I'm not sure if this is needed, but some toolkits/clients list
- * TARGETS in response to targets.
- */
- list[count] = atoms->targets;
- ++count;
-
- if ([pbtypes containsObject:NSStringPboardType])
- {
- /* We have a string type that we can convert to UTF8, or Latin-1... */
- DebugF ("NSStringPboardType\n");
- list[count] = atoms->utf8_string;
- ++count;
- list[count] = atoms->string;
- ++count;
- list[count] = atoms->compound_text;
- ++count;
- }
-
- /* TODO add the NSPICTPboardType back again, once we have conversion
- * functionality in send_image.
- */
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations" // NSPICTPboardType
-#endif
-
- if ([pbtypes containsObject:NSPICTPboardType]
- || [pbtypes containsObject:NSTIFFPboardType])
- {
- /* We can convert a TIFF to a PNG or JPEG. */
- DebugF ("NSTIFFPboardType\n");
- list[count] = atoms->image_png;
- ++count;
- list[count] = atoms->image_jpeg;
- ++count;
- }
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
- if (count)
- {
- /* We have a list of ATOMs to send. */
- XChangeProperty (xpbproxy_dpy, e->requestor, e->property, atoms->atom, 32,
- PropModeReplace, (unsigned char *) list, count);
-
- reply.xselection.property = e->property;
- }
- }
-
- [self send_reply:&reply];
-}
-
-
-- (void) send_string:(XSelectionRequestEvent *)e utf8:(BOOL)utf8 pasteboard:(NSPasteboard *)pb
-{
- XEvent reply;
- NSArray *pbtypes;
- NSString *data;
- const char *bytes;
- NSUInteger length;
-
- TRACE ();
-
- [self init_reply:&reply request:e];
-
- pbtypes = [pb types];
-
- if (![pbtypes containsObject:NSStringPboardType])
- {
- [self send_reply:&reply];
- return;
- }
-
-#ifdef __LP64__
- DebugF ("pbtypes retainCount after containsObject: %lu\n", [pbtypes retainCount]);
-#else
- DebugF ("pbtypes retainCount after containsObject: %u\n", [pbtypes retainCount]);
-#endif
-
- data = [pb stringForType:NSStringPboardType];
-
- if (nil == data)
- {
- [self send_reply:&reply];
- return;
- }
-
- if (utf8)
- {
- bytes = [data UTF8String];
- /*
- * We don't want the UTF-8 string length here.
- * We want the length in bytes.
- */
- length = strlen (bytes);
-
- if (length < 50) {
- DebugF ("UTF-8: %s\n", bytes);
-#ifdef __LP64__
- DebugF ("UTF-8 length: %lu\n", length);
-#else
- DebugF ("UTF-8 length: %u\n", length);
-#endif
- }
- }
- else
- {
- DebugF ("Latin-1\n");
- bytes = [data cStringUsingEncoding:NSISOLatin1StringEncoding];
- /*WARNING: bytes is not NUL-terminated. */
- length = [data lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
- }
-
- DebugF ("e->target %s\n", XGetAtomName (xpbproxy_dpy, e->target));
-
- XChangeProperty (xpbproxy_dpy, e->requestor, e->property, e->target,
- 8, PropModeReplace, (unsigned char *) bytes, length);
-
- reply.xselection.property = e->property;
-
- [self send_reply:&reply];
-}
-
-- (void) send_compound_text:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)pb
-{
- XEvent reply;
- NSArray *pbtypes;
-
- TRACE ();
-
- [self init_reply:&reply request:e];
-
- pbtypes = [pb types];
-
- if ([pbtypes containsObject: NSStringPboardType])
- {
- NSString *data = [pb stringForType:NSStringPboardType];
- if (nil != data)
- {
- /*
- * Cast to (void *) to avoid a const warning.
- * AFAIK Xutf8TextListToTextProperty does not modify the input memory.
- */
- void *utf8 = (void *)[data UTF8String];
- char *list[] = { utf8, NULL };
- XTextProperty textprop;
-
- textprop.value = NULL;
-
- if (Success == Xutf8TextListToTextProperty (xpbproxy_dpy, list, 1,
- XCompoundTextStyle,
- &textprop))
- {
-
- if (8 != textprop.format)
- DebugF ("textprop.format is unexpectedly not 8 - it's %d instead\n",
- textprop.format);
-
- XChangeProperty (xpbproxy_dpy, e->requestor, e->property,
- atoms->compound_text, textprop.format,
- PropModeReplace, textprop.value,
- textprop.nitems);
-
- reply.xselection.property = e->property;
- }
-
- if (textprop.value)
- XFree (textprop.value);
-
- }
- }
-
- [self send_reply:&reply];
-}
-
-/* Finding a test application that uses MULTIPLE has proven to be difficult. */
-- (void) send_multiple:(XSelectionRequestEvent *)e
-{
- XEvent reply;
-
- TRACE ();
-
- [self init_reply:&reply request:e];
-
- if (None != e->property)
- {
-
- }
-
- [self send_reply:&reply];
-}
-
-/* Return nil if an error occured. */
-/* DO NOT retain the encdata for longer than the length of an event response.
- * The autorelease pool will reuse/free it.
- */
-- (NSData *) encode_image_data:(NSData *)data type:(NSBitmapImageFileType)enctype
-{
- NSBitmapImageRep *bmimage = nil;
- NSData *encdata = nil;
- NSDictionary *dict = nil;
-
- bmimage = [[NSBitmapImageRep alloc] initWithData:data];
-
- if (nil == bmimage)
- return nil;
-
- dict = [[NSDictionary alloc] init];
- encdata = [bmimage representationUsingType:enctype properties:dict];
-
- if (nil == encdata)
- {
- [dict autorelease];
- [bmimage autorelease];
- return nil;
- }
-
- [dict autorelease];
- [bmimage autorelease];
-
- return encdata;
-}
-
-/* Return YES when an error has occured when trying to send the PICT. */
-/* The caller should send a default reponse with a property of None when an error occurs. */
-- (BOOL) send_image_pict_reply:(XSelectionRequestEvent *)e
- pasteboard:(NSPasteboard *)pb
- type:(NSBitmapImageFileType)imagetype
-{
- XEvent reply;
- NSImage *img = nil;
- NSData *data = nil, *encdata = nil;
- NSUInteger length;
- const void *bytes = NULL;
-
- img = [[NSImage alloc] initWithPasteboard:pb];
-
- if (nil == img)
- {
- return YES;
- }
-
- data = [img TIFFRepresentation];
-
- if (nil == data)
- {
- [img autorelease];
- ErrorF("unable to convert PICT to TIFF!\n");
- return YES;
- }
-
- encdata = [self encode_image_data:data type:imagetype];
- if(nil == encdata)
- {
- [img autorelease];
- return YES;
- }
-
- [self init_reply:&reply request:e];
-
- length = [encdata length];
- bytes = [encdata bytes];
-
- XChangeProperty (xpbproxy_dpy, e->requestor, e->property, e->target,
- 8, PropModeReplace, bytes, length);
- reply.xselection.property = e->property;
-
- [self send_reply:&reply];
-
- [img autorelease];
-
- return NO; /*no error*/
-}
-
-/* Return YES if an error occured. */
-/* The caller should send a reply with a property of None when an error occurs. */
-- (BOOL) send_image_tiff_reply:(XSelectionRequestEvent *)e
- pasteboard:(NSPasteboard *)pb
- type:(NSBitmapImageFileType)imagetype
-{
- XEvent reply;
- NSData *data = nil;
- NSData *encdata = nil;
- NSUInteger length;
- const void *bytes = NULL;
-
- data = [pb dataForType:NSTIFFPboardType];
-
- if (nil == data)
- return YES;
-
- encdata = [self encode_image_data:data type:imagetype];
-
- if(nil == encdata)
- return YES;
-
- [self init_reply:&reply request:e];
-
- length = [encdata length];
- bytes = [encdata bytes];
-
- XChangeProperty (xpbproxy_dpy, e->requestor, e->property, e->target,
- 8, PropModeReplace, bytes, length);
- reply.xselection.property = e->property;
-
- [self send_reply:&reply];
-
- return NO; /*no error*/
-}
-
-- (void) send_image:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)pb
-{
- NSArray *pbtypes = nil;
- NSBitmapImageFileType imagetype = NSPNGFileType;
-
- TRACE ();
-
- if (e->target == atoms->image_png)
- imagetype = NSPNGFileType;
- else if (e->target == atoms->image_jpeg)
- imagetype = NSJPEGFileType;
- else
- {
- ErrorF("internal failure in xpbproxy! imagetype being sent isn't PNG or JPEG.\n");
- }
-
- pbtypes = [pb types];
-
- if (pbtypes)
- {
- if ([pbtypes containsObject:NSTIFFPboardType])
- {
- if (NO == [self send_image_tiff_reply:e pasteboard:pb type:imagetype])
- return;
- }
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations" // NSPICTPboardType
-#endif
- else if ([pbtypes containsObject:NSPICTPboardType])
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- {
- if (NO == [self send_image_pict_reply:e pasteboard:pb type:imagetype])
- return;
-
- /* Fall through intentionally to the send_none: */
- }
- }
-
- [self send_none:e];
-}
-
-- (void)send_none:(XSelectionRequestEvent *)e
-{
- XEvent reply;
-
- TRACE ();
-
- [self init_reply:&reply request:e];
- [self send_reply:&reply];
-}
-
-
-/* Another client requested the data or targets of data available from the clipboard. */
-- (void)request_event:(XSelectionRequestEvent *)e
-{
- NSPasteboard *pb;
-
- TRACE ();
-
- /* TODO We should also keep track of the time of the selection, and
- * according to the ICCCM "refuse the request" if the event timestamp
- * is before we owned it.
- * What should we base the time on? How can we get the current time just
- * before an XSetSelectionOwner? Is it the server's time, or the clients?
- * According to the XSelectionRequestEvent manual page, the Time value
- * may be set to CurrentTime or a time, so that makes it a bit different.
- * Perhaps we should just punt and ignore races.
- */
-
- /*TODO we need a COMPOUND_TEXT test app*/
- /*TODO we need a MULTIPLE test app*/
-
- pb = [NSPasteboard generalPasteboard];
- if (nil == pb)
- {
- [self send_none:e];
- return;
- }
-
-
- if (None != e->target)
- DebugF ("e->target %s\n", XGetAtomName (xpbproxy_dpy, e->target));
-
- if (e->target == atoms->targets)
- {
- /* The paste requestor wants to know what TARGETS we support. */
- [self send_targets:e pasteboard:pb];
- }
- else if (e->target == atoms->multiple)
- {
- /*
- * This isn't finished, and may never be, unless I can find
- * a good test app.
- */
- [self send_multiple:e];
- }
- else if (e->target == atoms->utf8_string)
- {
- [self send_string:e utf8:YES pasteboard:pb];
- }
- else if (e->target == atoms->string)
- {
- [self send_string:e utf8:NO pasteboard:pb];
- }
- else if (e->target == atoms->compound_text)
- {
- [self send_compound_text:e pasteboard:pb];
- }
- else if (e->target == atoms->multiple)
- {
- [self send_multiple:e];
- }
- else if (e->target == atoms->image_png || e->target == atoms->image_jpeg)
- {
- [self send_image:e pasteboard:pb];
- }
- else
- {
- [self send_none:e];
- }
-}
-
-/* This handles the events resulting from an XConvertSelection request. */
-- (void) notify_event:(XSelectionEvent *)e
-{
- Atom type;
- struct propdata pdata;
-
- TRACE ();
-
- [self release_pending];
-
- if (None == e->property) {
- DebugF ("e->property is None.\n");
- [self copy_completed:e->selection];
- /* Nothing is selected. */
- return;
- }
-
-#if 0
- ErrorF("e->selection %s\n", XGetAtomName (xpbproxy_dpy, e->selection));
- ErrorF("e->property %s\n", XGetAtomName (xpbproxy_dpy, e->property));
-#endif
-
- if ([self is_incr_type:e])
- {
- /*
- * This is an INCR-style transfer, which means that we
- * will get the data after a series of PropertyNotify events.
- */
- DebugF ("is INCR\n");
-
- if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
- {
- /*
- * An error occured, so we should invoke the copy_completed:, but
- * not handle_selection:type:propdata:
- */
- [self copy_completed:e->selection];
- return;
- }
-
- free_propdata (&pdata);
-
- pending.requestor = e->requestor;
- pending.selection = e->selection;
-
- DebugF ("set pending.requestor to 0x%lx\n", pending.requestor);
- }
- else
- {
- if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type))
- {
- [self copy_completed:e->selection];
- return;
- }
-
- /* We have the complete selection data.*/
- [self handle_selection:e->selection type:type propdata:&pdata];
-
- DebugF ("handled selection with the first notify_event\n");
- }
-}
-
-/* This is used for INCR transfers. See the ICCCM for the details. */
-/* This is used to retrieve PRIMARY and CLIPBOARD selections. */
-- (void) property_event:(XPropertyEvent *)e
-{
- struct propdata pdata;
- Atom type;
-
- TRACE ();
-
- if (None != e->atom)
- {
-#ifdef DEBUG
- char *name = XGetAtomName (xpbproxy_dpy, e->atom);
-
- if (name)
- {
- DebugF ("e->atom %s\n", name);
- XFree(name);
- }
-#endif
- }
-
- if (None != pending.requestor && PropertyNewValue == e->state)
- {
- DebugF ("pending.requestor 0x%lx\n", pending.requestor);
-
- if (get_property (e->window, e->atom, &pdata, /*Delete*/ True, &type))
- {
- [self copy_completed:pending.selection];
- [self release_pending];
- return;
- }
-
- if (0 == pdata.length)
- {
- /*
- * We completed the transfer.
- * handle_selection will call copy_completed: for us.
- */
- [self handle_selection:pending.selection type:type propdata:&pending.propdata];
- free_propdata(&pdata);
- pending.propdata = null_propdata;
- pending.requestor = None;
- pending.selection = None;
- }
- else
- {
- [self append_to_pending:&pdata requestor:e->window];
- free_propdata (&pdata);
- }
- }
-}
-
-- (void) xfixes_selection_notify:(XFixesSelectionNotifyEvent *)e {
- if(!pbproxy_prefs.active)
- return;
-
- switch(e->subtype) {
- case XFixesSetSelectionOwnerNotify:
- if(e->selection == atoms->primary && pbproxy_prefs.primary_on_grab)
- [self x_copy:e->timestamp];
- break;
-
- case XFixesSelectionWindowDestroyNotify:
- case XFixesSelectionClientCloseNotify:
- default:
- ErrorF("Unhandled XFixesSelectionNotifyEvent: subtype=%d\n", e->subtype);
- break;
- }
-}
-
-- (void) handle_targets: (Atom)selection propdata:(struct propdata *)pdata
-{
- /* Find a type we can handle and prefer from the list of ATOMs. */
- Atom preferred;
- char *name;
-
- TRACE ();
-
- preferred = [self find_preferred:pdata];
-
- if (None == preferred)
- {
- /*
- * This isn't required by the ICCCM, but some apps apparently
- * don't respond to TARGETS properly.
- */
- preferred = atoms->string;
- }
-
- (void)name; /* Avoid a warning with non-debug compiles. */
-#ifdef DEBUG
- name = XGetAtomName (xpbproxy_dpy, preferred);
-
- if (name)
- {
- DebugF ("requesting %s\n", name);
- }
-#endif
- request_atom = preferred;
- XConvertSelection (xpbproxy_dpy, selection, preferred, selection,
- _selection_window, CurrentTime);
-}
-
-/* This handles the image type of selection (typically in CLIPBOARD). */
-/* We convert to a TIFF, so that other applications can paste more easily. */
-- (void) handle_image: (struct propdata *)pdata pasteboard:(NSPasteboard *)pb
-{
- NSArray *pbtypes;
- NSUInteger length;
- NSData *data, *tiff;
- NSBitmapImageRep *bmimage;
-
- TRACE ();
-
- length = pdata->length;
- data = [[NSData alloc] initWithBytes:pdata->data length:length];
-
- if (nil == data)
- {
- DebugF ("unable to create NSData object!\n");
- return;
- }
-
-#ifdef __LP64__
- DebugF ("data retainCount before NSBitmapImageRep initWithData: %lu\n",
- [data retainCount]);
-#else
- DebugF ("data retainCount before NSBitmapImageRep initWithData: %u\n",
- [data retainCount]);
-#endif
-
- bmimage = [[NSBitmapImageRep alloc] initWithData:data];
-
- if (nil == bmimage)
- {
- [data autorelease];
- DebugF ("unable to create NSBitmapImageRep!\n");
- return;
- }
-
-#ifdef __LP64__
- DebugF ("data retainCount after NSBitmapImageRep initWithData: %lu\n",
- [data retainCount]);
-#else
- DebugF ("data retainCount after NSBitmapImageRep initWithData: %u\n",
- [data retainCount]);
-#endif
-
- @try
- {
- tiff = [bmimage TIFFRepresentation];
- }
-
- @catch (NSException *e)
- {
- DebugF ("NSTIFFException!\n");
- [data autorelease];
- [bmimage autorelease];
- return;
- }
-
-#ifdef __LP64__
- DebugF ("bmimage retainCount after TIFFRepresentation %lu\n", [bmimage retainCount]);
-#else
- DebugF ("bmimage retainCount after TIFFRepresentation %u\n", [bmimage retainCount]);
-#endif
-
- pbtypes = [NSArray arrayWithObjects:NSTIFFPboardType, nil];
-
- if (nil == pbtypes)
- {
- [data autorelease];
- [bmimage autorelease];
- return;
- }
-
- [pb declareTypes:pbtypes owner:nil];
- if (YES != [pb setData:tiff forType:NSTIFFPboardType])
- {
- DebugF ("writing pasteboard data failed!\n");
- }
-
- [data autorelease];
-
-#ifdef __LP64__
- DebugF ("bmimage retainCount before release %lu\n", [bmimage retainCount]);
-#else
- DebugF ("bmimage retainCount before release %u\n", [bmimage retainCount]);
-#endif
-
- [bmimage autorelease];
-}
-
-/* This handles the UTF8_STRING type of selection. */
-- (void) handle_utf8_string:(struct propdata *)pdata pasteboard:(NSPasteboard *)pb
-{
- NSString *string;
- NSArray *pbtypes;
-
- TRACE ();
-
- string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSUTF8StringEncoding];
-
- if (nil == string)
- return;
-
- pbtypes = [NSArray arrayWithObjects:NSStringPboardType, nil];
-
- if (nil == pbtypes)
- {
- [string autorelease];
- return;
- }
-
- [pb declareTypes:pbtypes owner:nil];
-
- if (YES != [pb setString:string forType:NSStringPboardType]) {
- ErrorF("pasteboard setString:forType: failed!\n");
- }
- [string autorelease];
- DebugF ("done handling utf8 string\n");
-}
-
-/* This handles the STRING type, which should be in Latin-1. */
-- (void) handle_string: (struct propdata *)pdata pasteboard:(NSPasteboard *)pb
-{
- NSString *string;
- NSArray *pbtypes;
-
- TRACE ();
-
- string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSISOLatin1StringEncoding];
-
- if (nil == string)
- return;
-
- pbtypes = [NSArray arrayWithObjects:NSStringPboardType, nil];
-
- if (nil == pbtypes)
- {
- [string autorelease];
- return;
- }
-
- [pb declareTypes:pbtypes owner:nil];
- if (YES != [pb setString:string forType:NSStringPboardType]) {
- ErrorF("pasteboard setString:forType failed in handle_string!\n");
- }
- [string autorelease];
-}
-
-/* This is called when the selection is completely retrieved from another client. */
-/* Warning: this frees the propdata. */
-- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata
-{
- NSPasteboard *pb;
-
- TRACE ();
-
- pb = [NSPasteboard generalPasteboard];
-
- if (nil == pb)
- {
- [self copy_completed:selection];
- free_propdata (pdata);
- return;
- }
-
- /*
- * Some apps it seems set the type to TARGETS instead of ATOM, such as Eterm.
- * These aren't ICCCM compliant apps, but we need these to work...
- */
- if (request_atom == atoms->targets
- && (type == atoms->atom || type == atoms->targets))
- {
- [self handle_targets:selection propdata:pdata];
- free_propdata(pdata);
- return;
- }
- else if (type == atoms->image_png)
- {
- [self handle_image:pdata pasteboard:pb];
- }
- else if (type == atoms->image_jpeg)
- {
- [self handle_image:pdata pasteboard:pb];
- }
- else if (type == atoms->utf8_string)
- {
- [self handle_utf8_string:pdata pasteboard:pb];
- }
- else if (type == atoms->string)
- {
- [self handle_string:pdata pasteboard:pb];
- }
-
- free_propdata(pdata);
-
- [self copy_completed:selection];
-}
-
-
-- (void) copy_completed:(Atom)selection
-{
- TRACE ();
- char *name;
-
- (void)name; /* Avoid warning with non-debug compiles. */
-#ifdef DEBUG
- name = XGetAtomName (xpbproxy_dpy, selection);
- if (name)
- {
- DebugF ("copy_completed: %s\n", name);
- XFree (name);
- }
-#endif
-
- if (selection == atoms->primary && pending_copy > 0)
- {
- --pending_copy;
- if (pending_copy > 0)
- {
- /* Copy PRIMARY again. */
- [self x_copy_request_targets];
- return;
- }
- }
- else if (selection == atoms->clipboard && pending_clipboard > 0)
- {
- --pending_clipboard;
- if (pending_clipboard > 0)
- {
- /* Copy CLIPBOARD. */
- [self claim_clipboard];
- return;
- }
- else
- {
- /* We got the final data. Now set pbproxy as the owner. */
- [self own_clipboard];
- return;
- }
- }
-
- /*
- * We had 1 or more primary in progress, and the clipboard arrived
- * while we were busy.
- */
- if (pending_clipboard > 0)
- {
- [self claim_clipboard];
- }
-}
-
-- (void) reload_preferences
-{
- /*
- * It's uncertain how we could handle the synchronization failing, so cast to void.
- * The prefs_get_bool should fall back to defaults if the org.x.X11 plist doesn't exist or is invalid.
- */
- (void)CFPreferencesAppSynchronize(app_prefs_domain_cfstr);
-#ifdef STANDALONE_XPBPROXY
- if(xpbproxy_is_standalone)
- pbproxy_prefs.active = YES;
- else
-#endif
- pbproxy_prefs.active = prefs_get_bool(CFSTR("sync_pasteboard"), pbproxy_prefs.active);
- pbproxy_prefs.primary_on_grab = prefs_get_bool(CFSTR("sync_primary_on_select"), pbproxy_prefs.primary_on_grab);
- pbproxy_prefs.clipboard_to_pasteboard = prefs_get_bool(CFSTR("sync_clipboard_to_pasteboard"), pbproxy_prefs.clipboard_to_pasteboard);
- pbproxy_prefs.pasteboard_to_primary = prefs_get_bool(CFSTR("sync_pasteboard_to_primary"), pbproxy_prefs.pasteboard_to_primary);
- pbproxy_prefs.pasteboard_to_clipboard = prefs_get_bool(CFSTR("sync_pasteboard_to_clipboard"), pbproxy_prefs.pasteboard_to_clipboard);
-
- /* This is used for debugging. */
- //dump_prefs();
-
- if(pbproxy_prefs.active && pbproxy_prefs.primary_on_grab && !xpbproxy_have_xfixes) {
- ErrorF("Disabling sync_primary_on_select functionality due to missing XFixes extension.\n");
- pbproxy_prefs.primary_on_grab = NO;
- }
-
- /* Claim or release the CLIPBOARD_MANAGER atom */
- if(![self set_clipboard_manager_status:(pbproxy_prefs.active && pbproxy_prefs.clipboard_to_pasteboard)])
- pbproxy_prefs.clipboard_to_pasteboard = NO;
-
- if(pbproxy_prefs.active && pbproxy_prefs.clipboard_to_pasteboard)
- [self claim_clipboard];
-}
-
-- (BOOL) is_active
-{
- return pbproxy_prefs.active;
-}
-
-/* NSPasteboard-required methods */
-
-- (void) paste:(id)sender
-{
- TRACE ();
-}
-
-- (void) pasteboard:(NSPasteboard *)pb provideDataForType:(NSString *)type
-{
- TRACE ();
-}
-
-- (void) pasteboardChangedOwner:(NSPasteboard *)pb
-{
- TRACE ();
-
- /* Right now we don't care with this. */
-}
-
-/* Allocation */
-
-- init
-{
- unsigned long pixel;
-
- self = [super init];
- if (self == nil)
- return nil;
-
- atoms->primary = XInternAtom (xpbproxy_dpy, "PRIMARY", False);
- atoms->clipboard = XInternAtom (xpbproxy_dpy, "CLIPBOARD", False);
- atoms->text = XInternAtom (xpbproxy_dpy, "TEXT", False);
- atoms->utf8_string = XInternAtom (xpbproxy_dpy, "UTF8_STRING", False);
- atoms->string = XInternAtom (xpbproxy_dpy, "STRING", False);
- atoms->targets = XInternAtom (xpbproxy_dpy, "TARGETS", False);
- atoms->multiple = XInternAtom (xpbproxy_dpy, "MULTIPLE", False);
- atoms->cstring = XInternAtom (xpbproxy_dpy, "CSTRING", False);
- atoms->image_png = XInternAtom (xpbproxy_dpy, "image/png", False);
- atoms->image_jpeg = XInternAtom (xpbproxy_dpy, "image/jpeg", False);
- atoms->incr = XInternAtom (xpbproxy_dpy, "INCR", False);
- atoms->atom = XInternAtom (xpbproxy_dpy, "ATOM", False);
- atoms->clipboard_manager = XInternAtom (xpbproxy_dpy, "CLIPBOARD_MANAGER", False);
- atoms->compound_text = XInternAtom (xpbproxy_dpy, "COMPOUND_TEXT", False);
- atoms->atom_pair = XInternAtom (xpbproxy_dpy, "ATOM_PAIR", False);
-
- pixel = BlackPixel (xpbproxy_dpy, DefaultScreen (xpbproxy_dpy));
- _selection_window = XCreateSimpleWindow (xpbproxy_dpy, DefaultRootWindow (xpbproxy_dpy),
- 0, 0, 1, 1, 0, pixel, pixel);
-
- /* This is used to get PropertyNotify events when doing INCR transfers. */
- XSelectInput (xpbproxy_dpy, _selection_window, PropertyChangeMask);
-
- request_atom = None;
-
- init_propdata (&pending.propdata);
- pending.requestor = None;
- pending.selection = None;
-
- pending_copy = 0;
- pending_clipboard = 0;
-
- if(xpbproxy_have_xfixes)
- XFixesSelectSelectionInput(xpbproxy_dpy, _selection_window, atoms->primary,
- XFixesSetSelectionOwnerNotifyMask);
-
- [self reload_preferences];
-
- return self;
-}
-
-- (void) dealloc
-{
- if (None != _selection_window)
- {
- XDestroyWindow (xpbproxy_dpy, _selection_window);
- _selection_window = None;
- }
-
- free_propdata (&pending.propdata);
-
- [super dealloc];
-}
-
-@end
+/* x-selection.m -- proxies between NSPasteboard and X11 selections + + Copyright (c) 2002, 2008 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 <stdio.h> +#include <stdlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#import <AppKit/NSGraphics.h> +#import <AppKit/NSImage.h> +#import <AppKit/NSBitmapImageRep.h> + +/* + * The basic design of the pbproxy code is as follows. + * + * When a client selects text, say from an xterm - we only copy it when the + * X11 Edit->Copy menu item is pressed or the shortcut activated. In this + * case we take the PRIMARY selection, and set it as the NSPasteboard data. + * + * When an X11 client copies something to the CLIPBOARD, pbproxy greedily grabs + * the data, sets it as the NSPasteboard data, and finally sets itself as + * owner of the CLIPBOARD. + * + * When an X11 window is activated we check to see if the NSPasteboard has + * changed. If the NSPasteboard has changed, then we set pbproxy as owner + * of the PRIMARY and CLIPBOARD and respond to requests for text and images. + * + * The behavior is now dynamic since the information above was written. + * The behavior is now dependent on the pbproxy_prefs below. + */ + +/* + * TODO: + * 1. handle MULTIPLE - I need to study the ICCCM further, and find a test app. + * 2. Handle NSPasteboard updates immediately, not on active/inactive + * - Open xterm, run 'cat readme.txt | pbcopy' + */ + +static struct { + BOOL active ; + BOOL primary_on_grab; /* This is provided as an option for people who + * want it and has issues that won't ever be + * addressed to make it *always* work. + */ + BOOL clipboard_to_pasteboard; + BOOL pasteboard_to_primary; + BOOL pasteboard_to_clipboard; +} pbproxy_prefs = { YES, NO, YES, YES, YES }; + +@implementation x_selection + +static struct propdata null_propdata = {NULL, 0, 0}; + +#ifdef DEBUG +static void +dump_prefs() { + ErrorF(fp, + "pbproxy preferences:\n" + "\tactive %u\n" + "\tprimary_on_grab %u\n" + "\tclipboard_to_pasteboard %u\n" + "\tpasteboard_to_primary %u\n" + "\tpasteboard_to_clipboard %u\n", + pbproxy_prefs.active, + pbproxy_prefs.primary_on_grab, + pbproxy_prefs.clipboard_to_pasteboard, + pbproxy_prefs.pasteboard_to_primary, + pbproxy_prefs.pasteboard_to_clipboard); +} +#endif + +extern CFStringRef app_prefs_domain_cfstr; + +static BOOL +prefs_get_bool (CFStringRef key, BOOL defaultValue) { + Boolean value, ok; + + value = CFPreferencesGetAppBooleanValue (key, app_prefs_domain_cfstr, &ok); + + return ok ? (BOOL) value : defaultValue; +} + +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 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; + + TRACE (); + + if(None == property) + return True; + + do + { + unsigned long newbuflen = 0; + unsigned char *newbuf = NULL; + +#ifdef TEST + ErrorF("bytesleft %lu\n", bytesleft); +#endif + + if (Success != XGetWindowProperty (xpbproxy_dpy, win, property, + offset, length, delete, + AnyPropertyType, + type, &format, &numitems, + &bytesleft, &chunk)) + { + DebugF ("Error while getting window property.\n"); + *pdata = null_propdata; + free (buf); + return True; + } + +#ifdef TEST + ErrorF("format %d numitems %lu bytesleft %lu\n", + format, numitems, bytesleft); + + ErrorF("type %s\n", XGetAtomName (xpbproxy_dpy, *type)); +#endif + + /* Format is the number of bits. */ + if (format == 8) + chunkbytesize = numitems; + else if (format == 16) + chunkbytesize = numitems * sizeof(short); + else if (format == 32) + chunkbytesize = numitems * sizeof(long); + +#ifdef TEST + ErrorF("chunkbytesize %zu\n", chunkbytesize); +#endif + newbuflen = buflen + chunkbytesize; + if (newbuflen > 0) + { + 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; + } + else + { + if (chunk) + XFree (chunk); + } + +#ifdef TEST + ErrorF("bytesleft %lu\n", bytesleft); +#endif + } while (bytesleft > 0); + + pdata->data = buf; + pdata->length = buflen; + pdata->format = format; + + return /*success*/ False; +} + + +/* Implementation methods */ + +/* This finds the preferred type from a TARGETS list.*/ +- (Atom) find_preferred:(struct propdata *)pdata +{ + Atom a = None; + size_t i, step; + Bool png = False, jpeg = False, utf8 = False, string = False; + + TRACE (); + + if (pdata->format != 32) + { + ErrorF("Atom list is expected to be formatted as an array of 32bit values.\n"); + return None; + } + + for (i = 0, step = sizeof(long); i < pdata->length; i += step) + { + a = (Atom)*(long *)(pdata->data + i); + + if (a == atoms->image_png) + { + png = True; + } + else if (a == atoms->image_jpeg) + { + jpeg = True; + } + else if (a == atoms->utf8_string) + { + utf8 = True; + } + else if (a == atoms->string) + { + string = True; + } + else + { + char *type = XGetAtomName(xpbproxy_dpy, a); + if (type) + { + DebugF("Unhandled X11 mime type: %s", type); + XFree(type); + } + } + } + + /*We prefer PNG over strings, and UTF8 over a Latin-1 string.*/ + if (png) + return atoms->image_png; + + if (jpeg) + return atoms->image_jpeg; + + 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 this is an INCR-style transfer. */ +- (Bool) is_incr_type:(XSelectionEvent *)e +{ + Atom seltype; + int format; + unsigned long numitems = 0UL, bytesleft = 0UL; + unsigned char *chunk; + + TRACE (); + + if (Success != XGetWindowProperty (xpbproxy_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 should be called after a selection has been copied, + * or when the selection is unfinished before a transfer completes. + */ +- (void) release_pending +{ + TRACE (); + + free_propdata (&pending.propdata); + pending.requestor = None; + pending.selection = None; +} + +/* 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 +{ + unsigned char *newdata; + size_t newlength; + + TRACE (); + + if (requestor != pending.requestor) + { + [self release_pending]; + pending.requestor = requestor; + } + + newlength = pending.propdata.length + pdata->length; + newdata = realloc(pending.propdata.data, newlength); + + 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 +{ + static NSInteger changeCount; + NSInteger countNow; + NSPasteboard *pb; + + TRACE (); + + pb = [NSPasteboard generalPasteboard]; + + if (nil == pb) + return; + + countNow = [pb changeCount]; + + if (countNow != changeCount) + { + DebugF ("changed pasteboard!\n"); + changeCount = countNow; + + if (pbproxy_prefs.pasteboard_to_primary) + { + XSetSelectionOwner (xpbproxy_dpy, atoms->primary, _selection_window, CurrentTime); + } + + if (pbproxy_prefs.pasteboard_to_clipboard) { + [self own_clipboard]; + } + } + +#if 0 + /*gstaplin: we should perhaps investigate something like this branch above...*/ + 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 (xpbproxy_dpy, atoms->clipboard, + _selection_window, timestamp); + XSetSelectionOwner (xpbproxy_dpy, atoms->primary, + _selection_window, timestamp); + } +#endif +} + +/* Called when X11 loses key focus */ +- (void) x_inactive:(Time)timestamp +{ + TRACE (); +} + +/* This requests the TARGETS list from the PRIMARY selection owner. */ +- (void) x_copy_request_targets +{ + TRACE (); + + request_atom = atoms->targets; + XConvertSelection (xpbproxy_dpy, atoms->primary, atoms->targets, + atoms->primary, _selection_window, CurrentTime); +} + +/* 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; + + TRACE (); + + w = XGetSelectionOwner (xpbproxy_dpy, atoms->primary); + + if (None != w) + { + ++pending_copy; + + if (1 == pending_copy) { + /* + * There are no other copy operations in progress, so we + * can proceed safely. Otherwise the copy_completed method + * will see that the pending_copy is > 1, and do another copy. + */ + [self x_copy_request_targets]; + } + } +} + +/* Set pbproxy as owner of the SELECTION_MANAGER selection. + * This prevents tools like xclipboard from causing havoc. + * Returns TRUE on success + */ +- (BOOL) set_clipboard_manager_status:(BOOL)value +{ + TRACE (); + + Window owner = XGetSelectionOwner (xpbproxy_dpy, atoms->clipboard_manager); + + if(value) { + if(owner == _selection_window) + return TRUE; + + if(owner != None) { + ErrorF("A clipboard manager using window 0x%lx already owns the clipboard selection. " + "pbproxy will not sync clipboard to pasteboard.\n", owner); + return FALSE; + } + + XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager, _selection_window, CurrentTime); + return (_selection_window == XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager)); + } else { + if(owner != _selection_window) + return TRUE; + + XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager, None, CurrentTime); + return(None == XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager)); + } + + return FALSE; +} + +/* + * This occurs when we previously owned a selection, + * and then lost it from another client. + */ +- (void) clear_event:(XSelectionClearEvent *)e +{ + + + TRACE (); + + DebugF ("e->selection %s\n", XGetAtomName (xpbproxy_dpy, e->selection)); + + if(e->selection == atoms->clipboard) { + /* + * We lost ownership of the CLIPBOARD. + */ + ++pending_clipboard; + + if (1 == pending_clipboard) { + /* Claim the clipboard contents from the new owner. */ + [self claim_clipboard]; + } + } else if(e->selection == atoms->clipboard_manager) { + if(pbproxy_prefs.clipboard_to_pasteboard) { + /* Another CLIPBOARD_MANAGER has set itself as owner. Disable syncing + * to avoid a race. + */ + ErrorF("Another clipboard manager was started! " + "xpbproxy is disabling syncing with clipboard.\n"); + pbproxy_prefs.clipboard_to_pasteboard = NO; + } + } +} + +/* + * We greedily acquire the clipboard after it changes, and on startup. + */ +- (void) claim_clipboard +{ + Window owner; + + TRACE (); + + if (!pbproxy_prefs.clipboard_to_pasteboard) + return; + + owner = XGetSelectionOwner (xpbproxy_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. + */ + DebugF ("No clipboard owner.\n"); + [self copy_completed:atoms->clipboard]; + return; + } else if (owner == _selection_window) { + [self copy_completed:atoms->clipboard]; + return; + } + + DebugF ("requesting targets\n"); + + request_atom = atoms->targets; + XConvertSelection (xpbproxy_dpy, atoms->clipboard, atoms->targets, + atoms->clipboard, _selection_window, CurrentTime); + XFlush (xpbproxy_dpy); + /* Now we will get a SelectionNotify event in the future. */ +} + +/* Greedily acquire the clipboard. */ +- (void) own_clipboard +{ + + TRACE (); + + /* We should perhaps have a boundary limit on the number of iterations... */ + do + { + XSetSelectionOwner (xpbproxy_dpy, atoms->clipboard, _selection_window, + CurrentTime); + } while (_selection_window != XGetSelectionOwner (xpbproxy_dpy, + atoms->clipboard)); +} + +- (void) init_reply:(XEvent *)reply request:(XSelectionRequestEvent *)e +{ + 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; +} + +- (void) send_reply:(XEvent *)reply +{ + /* + * We are supposed to use an empty event mask, and not propagate + * the event, according to the ICCCM. + */ + DebugF ("reply->xselection.requestor 0x%lx\n", reply->xselection.requestor); + + XSendEvent (xpbproxy_dpy, reply->xselection.requestor, False, 0, reply); + XFlush (xpbproxy_dpy); +} + +/* + * This responds to a TARGETS request. + * The result is a list of a ATOMs that correspond to the types available + * for a selection. + * For instance an application might provide a UTF8_STRING and a STRING + * (in Latin-1 encoding). The requestor can then make the choice based on + * the list. + */ +- (void) send_targets:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)pb +{ + XEvent reply; + NSArray *pbtypes; + + [self init_reply:&reply request:e]; + + pbtypes = [pb types]; + if (pbtypes) + { + long list[7]; /* Don't forget to increase this if we handle more types! */ + long count = 0; + + /* + * I'm not sure if this is needed, but some toolkits/clients list + * TARGETS in response to targets. + */ + list[count] = atoms->targets; + ++count; + + if ([pbtypes containsObject:NSStringPboardType]) + { + /* We have a string type that we can convert to UTF8, or Latin-1... */ + DebugF ("NSStringPboardType\n"); + list[count] = atoms->utf8_string; + ++count; + list[count] = atoms->string; + ++count; + list[count] = atoms->compound_text; + ++count; + } + + /* TODO add the NSPICTPboardType back again, once we have conversion + * functionality in send_image. + */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // NSPICTPboardType +#endif + + if ([pbtypes containsObject:NSPICTPboardType] + || [pbtypes containsObject:NSTIFFPboardType]) + { + /* We can convert a TIFF to a PNG or JPEG. */ + DebugF ("NSTIFFPboardType\n"); + list[count] = atoms->image_png; + ++count; + list[count] = atoms->image_jpeg; + ++count; + } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + if (count) + { + /* We have a list of ATOMs to send. */ + XChangeProperty (xpbproxy_dpy, e->requestor, e->property, atoms->atom, 32, + PropModeReplace, (unsigned char *) list, count); + + reply.xselection.property = e->property; + } + } + + [self send_reply:&reply]; +} + + +- (void) send_string:(XSelectionRequestEvent *)e utf8:(BOOL)utf8 pasteboard:(NSPasteboard *)pb +{ + XEvent reply; + NSArray *pbtypes; + NSString *data; + const char *bytes; + NSUInteger length; + + TRACE (); + + [self init_reply:&reply request:e]; + + pbtypes = [pb types]; + + if (![pbtypes containsObject:NSStringPboardType]) + { + [self send_reply:&reply]; + return; + } + +#ifdef __LP64__ + DebugF ("pbtypes retainCount after containsObject: %lu\n", [pbtypes retainCount]); +#else + DebugF ("pbtypes retainCount after containsObject: %u\n", [pbtypes retainCount]); +#endif + + data = [pb stringForType:NSStringPboardType]; + + if (nil == data) + { + [self send_reply:&reply]; + return; + } + + if (utf8) + { + bytes = [data UTF8String]; + /* + * We don't want the UTF-8 string length here. + * We want the length in bytes. + */ + length = strlen (bytes); + + if (length < 50) { + DebugF ("UTF-8: %s\n", bytes); +#ifdef __LP64__ + DebugF ("UTF-8 length: %lu\n", length); +#else + DebugF ("UTF-8 length: %u\n", length); +#endif + } + } + else + { + DebugF ("Latin-1\n"); + bytes = [data cStringUsingEncoding:NSISOLatin1StringEncoding]; + /*WARNING: bytes is not NUL-terminated. */ + length = [data lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding]; + } + + DebugF ("e->target %s\n", XGetAtomName (xpbproxy_dpy, e->target)); + + XChangeProperty (xpbproxy_dpy, e->requestor, e->property, e->target, + 8, PropModeReplace, (unsigned char *) bytes, length); + + reply.xselection.property = e->property; + + [self send_reply:&reply]; +} + +- (void) send_compound_text:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)pb +{ + XEvent reply; + NSArray *pbtypes; + + TRACE (); + + [self init_reply:&reply request:e]; + + pbtypes = [pb types]; + + if ([pbtypes containsObject: NSStringPboardType]) + { + NSString *data = [pb stringForType:NSStringPboardType]; + if (nil != data) + { + /* + * Cast to (void *) to avoid a const warning. + * AFAIK Xutf8TextListToTextProperty does not modify the input memory. + */ + void *utf8 = (void *)[data UTF8String]; + char *list[] = { utf8, NULL }; + XTextProperty textprop; + + textprop.value = NULL; + + if (Success == Xutf8TextListToTextProperty (xpbproxy_dpy, list, 1, + XCompoundTextStyle, + &textprop)) + { + + if (8 != textprop.format) + DebugF ("textprop.format is unexpectedly not 8 - it's %d instead\n", + textprop.format); + + XChangeProperty (xpbproxy_dpy, e->requestor, e->property, + atoms->compound_text, textprop.format, + PropModeReplace, textprop.value, + textprop.nitems); + + reply.xselection.property = e->property; + } + + if (textprop.value) + XFree (textprop.value); + + } + } + + [self send_reply:&reply]; +} + +/* Finding a test application that uses MULTIPLE has proven to be difficult. */ +- (void) send_multiple:(XSelectionRequestEvent *)e +{ + XEvent reply; + + TRACE (); + + [self init_reply:&reply request:e]; + + if (None != e->property) + { + + } + + [self send_reply:&reply]; +} + +/* Return nil if an error occured. */ +/* DO NOT retain the encdata for longer than the length of an event response. + * The autorelease pool will reuse/free it. + */ +- (NSData *) encode_image_data:(NSData *)data type:(NSBitmapImageFileType)enctype +{ + NSBitmapImageRep *bmimage = nil; + NSData *encdata = nil; + NSDictionary *dict = nil; + + bmimage = [[NSBitmapImageRep alloc] initWithData:data]; + + if (nil == bmimage) + return nil; + + dict = [[NSDictionary alloc] init]; + encdata = [bmimage representationUsingType:enctype properties:dict]; + + if (nil == encdata) + { + [dict autorelease]; + [bmimage autorelease]; + return nil; + } + + [dict autorelease]; + [bmimage autorelease]; + + return encdata; +} + +/* Return YES when an error has occured when trying to send the PICT. */ +/* The caller should send a default reponse with a property of None when an error occurs. */ +- (BOOL) send_image_pict_reply:(XSelectionRequestEvent *)e + pasteboard:(NSPasteboard *)pb + type:(NSBitmapImageFileType)imagetype +{ + XEvent reply; + NSImage *img = nil; + NSData *data = nil, *encdata = nil; + NSUInteger length; + const void *bytes = NULL; + + img = [[NSImage alloc] initWithPasteboard:pb]; + + if (nil == img) + { + return YES; + } + + data = [img TIFFRepresentation]; + + if (nil == data) + { + [img autorelease]; + ErrorF("unable to convert PICT to TIFF!\n"); + return YES; + } + + encdata = [self encode_image_data:data type:imagetype]; + if(nil == encdata) + { + [img autorelease]; + return YES; + } + + [self init_reply:&reply request:e]; + + length = [encdata length]; + bytes = [encdata bytes]; + + XChangeProperty (xpbproxy_dpy, e->requestor, e->property, e->target, + 8, PropModeReplace, bytes, length); + reply.xselection.property = e->property; + + [self send_reply:&reply]; + + [img autorelease]; + + return NO; /*no error*/ +} + +/* Return YES if an error occured. */ +/* The caller should send a reply with a property of None when an error occurs. */ +- (BOOL) send_image_tiff_reply:(XSelectionRequestEvent *)e + pasteboard:(NSPasteboard *)pb + type:(NSBitmapImageFileType)imagetype +{ + XEvent reply; + NSData *data = nil; + NSData *encdata = nil; + NSUInteger length; + const void *bytes = NULL; + + data = [pb dataForType:NSTIFFPboardType]; + + if (nil == data) + return YES; + + encdata = [self encode_image_data:data type:imagetype]; + + if(nil == encdata) + return YES; + + [self init_reply:&reply request:e]; + + length = [encdata length]; + bytes = [encdata bytes]; + + XChangeProperty (xpbproxy_dpy, e->requestor, e->property, e->target, + 8, PropModeReplace, bytes, length); + reply.xselection.property = e->property; + + [self send_reply:&reply]; + + return NO; /*no error*/ +} + +- (void) send_image:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)pb +{ + NSArray *pbtypes = nil; + NSBitmapImageFileType imagetype = NSPNGFileType; + + TRACE (); + + if (e->target == atoms->image_png) + imagetype = NSPNGFileType; + else if (e->target == atoms->image_jpeg) + imagetype = NSJPEGFileType; + else + { + ErrorF("internal failure in xpbproxy! imagetype being sent isn't PNG or JPEG.\n"); + } + + pbtypes = [pb types]; + + if (pbtypes) + { + if ([pbtypes containsObject:NSTIFFPboardType]) + { + if (NO == [self send_image_tiff_reply:e pasteboard:pb type:imagetype]) + return; + } +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // NSPICTPboardType +#endif + else if ([pbtypes containsObject:NSPICTPboardType]) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + { + if (NO == [self send_image_pict_reply:e pasteboard:pb type:imagetype]) + return; + + /* Fall through intentionally to the send_none: */ + } + } + + [self send_none:e]; +} + +- (void)send_none:(XSelectionRequestEvent *)e +{ + XEvent reply; + + TRACE (); + + [self init_reply:&reply request:e]; + [self send_reply:&reply]; +} + + +/* Another client requested the data or targets of data available from the clipboard. */ +- (void)request_event:(XSelectionRequestEvent *)e +{ + NSPasteboard *pb; + + TRACE (); + + /* TODO We should also keep track of the time of the selection, and + * according to the ICCCM "refuse the request" if the event timestamp + * is before we owned it. + * What should we base the time on? How can we get the current time just + * before an XSetSelectionOwner? Is it the server's time, or the clients? + * According to the XSelectionRequestEvent manual page, the Time value + * may be set to CurrentTime or a time, so that makes it a bit different. + * Perhaps we should just punt and ignore races. + */ + + /*TODO we need a COMPOUND_TEXT test app*/ + /*TODO we need a MULTIPLE test app*/ + + pb = [NSPasteboard generalPasteboard]; + if (nil == pb) + { + [self send_none:e]; + return; + } + + + if (None != e->target) + DebugF ("e->target %s\n", XGetAtomName (xpbproxy_dpy, e->target)); + + if (e->target == atoms->targets) + { + /* The paste requestor wants to know what TARGETS we support. */ + [self send_targets:e pasteboard:pb]; + } + else if (e->target == atoms->multiple) + { + /* + * This isn't finished, and may never be, unless I can find + * a good test app. + */ + [self send_multiple:e]; + } + else if (e->target == atoms->utf8_string) + { + [self send_string:e utf8:YES pasteboard:pb]; + } + else if (e->target == atoms->string) + { + [self send_string:e utf8:NO pasteboard:pb]; + } + else if (e->target == atoms->compound_text) + { + [self send_compound_text:e pasteboard:pb]; + } + else if (e->target == atoms->multiple) + { + [self send_multiple:e]; + } + else if (e->target == atoms->image_png || e->target == atoms->image_jpeg) + { + [self send_image:e pasteboard:pb]; + } + else + { + [self send_none:e]; + } +} + +/* This handles the events resulting from an XConvertSelection request. */ +- (void) notify_event:(XSelectionEvent *)e +{ + Atom type; + struct propdata pdata; + + TRACE (); + + [self release_pending]; + + if (None == e->property) { + DebugF ("e->property is None.\n"); + [self copy_completed:e->selection]; + /* Nothing is selected. */ + return; + } + +#if 0 + ErrorF("e->selection %s\n", XGetAtomName (xpbproxy_dpy, e->selection)); + ErrorF("e->property %s\n", XGetAtomName (xpbproxy_dpy, e->property)); +#endif + + if ([self is_incr_type:e]) + { + /* + * This is an INCR-style transfer, which means that we + * will get the data after a series of PropertyNotify events. + */ + DebugF ("is INCR\n"); + + if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type)) + { + /* + * An error occured, so we should invoke the copy_completed:, but + * not handle_selection:type:propdata: + */ + [self copy_completed:e->selection]; + return; + } + + free_propdata (&pdata); + + pending.requestor = e->requestor; + pending.selection = e->selection; + + DebugF ("set pending.requestor to 0x%lx\n", pending.requestor); + } + else + { + if (get_property (e->requestor, e->property, &pdata, /*Delete*/ True, &type)) + { + [self copy_completed:e->selection]; + return; + } + + /* We have the complete selection data.*/ + [self handle_selection:e->selection type:type propdata:&pdata]; + + DebugF ("handled selection with the first notify_event\n"); + } +} + +/* This is used for INCR transfers. See the ICCCM for the details. */ +/* This is used to retrieve PRIMARY and CLIPBOARD selections. */ +- (void) property_event:(XPropertyEvent *)e +{ + struct propdata pdata; + Atom type; + + TRACE (); + + if (None != e->atom) + { +#ifdef DEBUG + char *name = XGetAtomName (xpbproxy_dpy, e->atom); + + if (name) + { + DebugF ("e->atom %s\n", name); + XFree(name); + } +#endif + } + + if (None != pending.requestor && PropertyNewValue == e->state) + { + DebugF ("pending.requestor 0x%lx\n", pending.requestor); + + if (get_property (e->window, e->atom, &pdata, /*Delete*/ True, &type)) + { + [self copy_completed:pending.selection]; + [self release_pending]; + return; + } + + if (0 == pdata.length) + { + /* + * We completed the transfer. + * handle_selection will call copy_completed: for us. + */ + [self handle_selection:pending.selection type:type propdata:&pending.propdata]; + free_propdata(&pdata); + pending.propdata = null_propdata; + pending.requestor = None; + pending.selection = None; + } + else + { + [self append_to_pending:&pdata requestor:e->window]; + free_propdata (&pdata); + } + } +} + +- (void) xfixes_selection_notify:(XFixesSelectionNotifyEvent *)e { + if(!pbproxy_prefs.active) + return; + + switch(e->subtype) { + case XFixesSetSelectionOwnerNotify: + if(e->selection == atoms->primary && pbproxy_prefs.primary_on_grab) + [self x_copy:e->timestamp]; + break; + + case XFixesSelectionWindowDestroyNotify: + case XFixesSelectionClientCloseNotify: + default: + ErrorF("Unhandled XFixesSelectionNotifyEvent: subtype=%d\n", e->subtype); + break; + } +} + +- (void) handle_targets: (Atom)selection propdata:(struct propdata *)pdata +{ + /* Find a type we can handle and prefer from the list of ATOMs. */ + Atom preferred; + char *name; + + TRACE (); + + preferred = [self find_preferred:pdata]; + + if (None == preferred) + { + /* + * This isn't required by the ICCCM, but some apps apparently + * don't respond to TARGETS properly. + */ + preferred = atoms->string; + } + + (void)name; /* Avoid a warning with non-debug compiles. */ +#ifdef DEBUG + name = XGetAtomName (xpbproxy_dpy, preferred); + + if (name) + { + DebugF ("requesting %s\n", name); + } +#endif + request_atom = preferred; + XConvertSelection (xpbproxy_dpy, selection, preferred, selection, + _selection_window, CurrentTime); +} + +/* This handles the image type of selection (typically in CLIPBOARD). */ +/* We convert to a TIFF, so that other applications can paste more easily. */ +- (void) handle_image: (struct propdata *)pdata pasteboard:(NSPasteboard *)pb +{ + NSArray *pbtypes; + NSUInteger length; + NSData *data, *tiff; + NSBitmapImageRep *bmimage; + + TRACE (); + + length = pdata->length; + data = [[NSData alloc] initWithBytes:pdata->data length:length]; + + if (nil == data) + { + DebugF ("unable to create NSData object!\n"); + return; + } + +#ifdef __LP64__ + DebugF ("data retainCount before NSBitmapImageRep initWithData: %lu\n", + [data retainCount]); +#else + DebugF ("data retainCount before NSBitmapImageRep initWithData: %u\n", + [data retainCount]); +#endif + + bmimage = [[NSBitmapImageRep alloc] initWithData:data]; + + if (nil == bmimage) + { + [data autorelease]; + DebugF ("unable to create NSBitmapImageRep!\n"); + return; + } + +#ifdef __LP64__ + DebugF ("data retainCount after NSBitmapImageRep initWithData: %lu\n", + [data retainCount]); +#else + DebugF ("data retainCount after NSBitmapImageRep initWithData: %u\n", + [data retainCount]); +#endif + + @try + { + tiff = [bmimage TIFFRepresentation]; + } + + @catch (NSException *e) + { + DebugF ("NSTIFFException!\n"); + [data autorelease]; + [bmimage autorelease]; + return; + } + +#ifdef __LP64__ + DebugF ("bmimage retainCount after TIFFRepresentation %lu\n", [bmimage retainCount]); +#else + DebugF ("bmimage retainCount after TIFFRepresentation %u\n", [bmimage retainCount]); +#endif + + pbtypes = [NSArray arrayWithObjects:NSTIFFPboardType, nil]; + + if (nil == pbtypes) + { + [data autorelease]; + [bmimage autorelease]; + return; + } + + [pb declareTypes:pbtypes owner:nil]; + if (YES != [pb setData:tiff forType:NSTIFFPboardType]) + { + DebugF ("writing pasteboard data failed!\n"); + } + + [data autorelease]; + +#ifdef __LP64__ + DebugF ("bmimage retainCount before release %lu\n", [bmimage retainCount]); +#else + DebugF ("bmimage retainCount before release %u\n", [bmimage retainCount]); +#endif + + [bmimage autorelease]; +} + +/* This handles the UTF8_STRING type of selection. */ +- (void) handle_utf8_string:(struct propdata *)pdata pasteboard:(NSPasteboard *)pb +{ + NSString *string; + NSArray *pbtypes; + + TRACE (); + + string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSUTF8StringEncoding]; + + if (nil == string) + return; + + pbtypes = [NSArray arrayWithObjects:NSStringPboardType, nil]; + + if (nil == pbtypes) + { + [string autorelease]; + return; + } + + [pb declareTypes:pbtypes owner:nil]; + + if (YES != [pb setString:string forType:NSStringPboardType]) { + ErrorF("pasteboard setString:forType: failed!\n"); + } + [string autorelease]; + DebugF ("done handling utf8 string\n"); +} + +/* This handles the STRING type, which should be in Latin-1. */ +- (void) handle_string: (struct propdata *)pdata pasteboard:(NSPasteboard *)pb +{ + NSString *string; + NSArray *pbtypes; + + TRACE (); + + string = [[NSString alloc] initWithBytes:pdata->data length:pdata->length encoding:NSISOLatin1StringEncoding]; + + if (nil == string) + return; + + pbtypes = [NSArray arrayWithObjects:NSStringPboardType, nil]; + + if (nil == pbtypes) + { + [string autorelease]; + return; + } + + [pb declareTypes:pbtypes owner:nil]; + if (YES != [pb setString:string forType:NSStringPboardType]) { + ErrorF("pasteboard setString:forType failed in handle_string!\n"); + } + [string autorelease]; +} + +/* This is called when the selection is completely retrieved from another client. */ +/* Warning: this frees the propdata. */ +- (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata +{ + NSPasteboard *pb; + + TRACE (); + + pb = [NSPasteboard generalPasteboard]; + + if (nil == pb) + { + [self copy_completed:selection]; + free_propdata (pdata); + return; + } + + /* + * Some apps it seems set the type to TARGETS instead of ATOM, such as Eterm. + * These aren't ICCCM compliant apps, but we need these to work... + */ + if (request_atom == atoms->targets + && (type == atoms->atom || type == atoms->targets)) + { + [self handle_targets:selection propdata:pdata]; + free_propdata(pdata); + return; + } + else if (type == atoms->image_png) + { + [self handle_image:pdata pasteboard:pb]; + } + else if (type == atoms->image_jpeg) + { + [self handle_image:pdata pasteboard:pb]; + } + else if (type == atoms->utf8_string) + { + [self handle_utf8_string:pdata pasteboard:pb]; + } + else if (type == atoms->string) + { + [self handle_string:pdata pasteboard:pb]; + } + + free_propdata(pdata); + + [self copy_completed:selection]; +} + + +- (void) copy_completed:(Atom)selection +{ + TRACE (); + char *name; + + (void)name; /* Avoid warning with non-debug compiles. */ +#ifdef DEBUG + name = XGetAtomName (xpbproxy_dpy, selection); + if (name) + { + DebugF ("copy_completed: %s\n", name); + XFree (name); + } +#endif + + if (selection == atoms->primary && pending_copy > 0) + { + --pending_copy; + if (pending_copy > 0) + { + /* Copy PRIMARY again. */ + [self x_copy_request_targets]; + return; + } + } + else if (selection == atoms->clipboard && pending_clipboard > 0) + { + --pending_clipboard; + if (pending_clipboard > 0) + { + /* Copy CLIPBOARD. */ + [self claim_clipboard]; + return; + } + else + { + /* We got the final data. Now set pbproxy as the owner. */ + [self own_clipboard]; + return; + } + } + + /* + * We had 1 or more primary in progress, and the clipboard arrived + * while we were busy. + */ + if (pending_clipboard > 0) + { + [self claim_clipboard]; + } +} + +- (void) reload_preferences +{ + /* + * It's uncertain how we could handle the synchronization failing, so cast to void. + * The prefs_get_bool should fall back to defaults if the org.x.X11 plist doesn't exist or is invalid. + */ + (void)CFPreferencesAppSynchronize(app_prefs_domain_cfstr); +#ifdef STANDALONE_XPBPROXY + if(xpbproxy_is_standalone) + pbproxy_prefs.active = YES; + else +#endif + pbproxy_prefs.active = prefs_get_bool(CFSTR("sync_pasteboard"), pbproxy_prefs.active); + pbproxy_prefs.primary_on_grab = prefs_get_bool(CFSTR("sync_primary_on_select"), pbproxy_prefs.primary_on_grab); + pbproxy_prefs.clipboard_to_pasteboard = prefs_get_bool(CFSTR("sync_clipboard_to_pasteboard"), pbproxy_prefs.clipboard_to_pasteboard); + pbproxy_prefs.pasteboard_to_primary = prefs_get_bool(CFSTR("sync_pasteboard_to_primary"), pbproxy_prefs.pasteboard_to_primary); + pbproxy_prefs.pasteboard_to_clipboard = prefs_get_bool(CFSTR("sync_pasteboard_to_clipboard"), pbproxy_prefs.pasteboard_to_clipboard); + + /* This is used for debugging. */ + //dump_prefs(); + + if(pbproxy_prefs.active && pbproxy_prefs.primary_on_grab && !xpbproxy_have_xfixes) { + ErrorF("Disabling sync_primary_on_select functionality due to missing XFixes extension.\n"); + pbproxy_prefs.primary_on_grab = NO; + } + + /* Claim or release the CLIPBOARD_MANAGER atom */ + if(![self set_clipboard_manager_status:(pbproxy_prefs.active && pbproxy_prefs.clipboard_to_pasteboard)]) + pbproxy_prefs.clipboard_to_pasteboard = NO; + + if(pbproxy_prefs.active && pbproxy_prefs.clipboard_to_pasteboard) + [self claim_clipboard]; +} + +- (BOOL) is_active +{ + return pbproxy_prefs.active; +} + +/* NSPasteboard-required methods */ + +- (void) paste:(id)sender +{ + TRACE (); +} + +- (void) pasteboard:(NSPasteboard *)pb provideDataForType:(NSString *)type +{ + TRACE (); +} + +- (void) pasteboardChangedOwner:(NSPasteboard *)pb +{ + TRACE (); + + /* Right now we don't care with this. */ +} + +/* Allocation */ + +- init +{ + unsigned long pixel; + + self = [super init]; + if (self == nil) + return nil; + + atoms->primary = XInternAtom (xpbproxy_dpy, "PRIMARY", False); + atoms->clipboard = XInternAtom (xpbproxy_dpy, "CLIPBOARD", False); + atoms->text = XInternAtom (xpbproxy_dpy, "TEXT", False); + atoms->utf8_string = XInternAtom (xpbproxy_dpy, "UTF8_STRING", False); + atoms->string = XInternAtom (xpbproxy_dpy, "STRING", False); + atoms->targets = XInternAtom (xpbproxy_dpy, "TARGETS", False); + atoms->multiple = XInternAtom (xpbproxy_dpy, "MULTIPLE", False); + atoms->cstring = XInternAtom (xpbproxy_dpy, "CSTRING", False); + atoms->image_png = XInternAtom (xpbproxy_dpy, "image/png", False); + atoms->image_jpeg = XInternAtom (xpbproxy_dpy, "image/jpeg", False); + atoms->incr = XInternAtom (xpbproxy_dpy, "INCR", False); + atoms->atom = XInternAtom (xpbproxy_dpy, "ATOM", False); + atoms->clipboard_manager = XInternAtom (xpbproxy_dpy, "CLIPBOARD_MANAGER", False); + atoms->compound_text = XInternAtom (xpbproxy_dpy, "COMPOUND_TEXT", False); + atoms->atom_pair = XInternAtom (xpbproxy_dpy, "ATOM_PAIR", False); + + pixel = BlackPixel (xpbproxy_dpy, DefaultScreen (xpbproxy_dpy)); + _selection_window = XCreateSimpleWindow (xpbproxy_dpy, DefaultRootWindow (xpbproxy_dpy), + 0, 0, 1, 1, 0, pixel, pixel); + + /* This is used to get PropertyNotify events when doing INCR transfers. */ + XSelectInput (xpbproxy_dpy, _selection_window, PropertyChangeMask); + + request_atom = None; + + init_propdata (&pending.propdata); + pending.requestor = None; + pending.selection = None; + + pending_copy = 0; + pending_clipboard = 0; + + if(xpbproxy_have_xfixes) + XFixesSelectSelectionInput(xpbproxy_dpy, _selection_window, atoms->primary, + XFixesSetSelectionOwnerNotifyMask); + + [self reload_preferences]; + + return self; +} + +- (void) dealloc +{ + if (None != _selection_window) + { + XDestroyWindow (xpbproxy_dpy, _selection_window); + _selection_window = None; + } + + free_propdata (&pending.propdata); + + [super dealloc]; +} + +@end |