XQuartz: Added pasteboard proxy code stripped out of quartz-wm.
(cherry picked from commit 1f842c71c3
)
This commit is contained in:
parent
fafcafd6de
commit
144746223d
|
@ -2171,6 +2171,7 @@ hw/xquartz/GL/Makefile
|
|||
hw/xquartz/bundle/Makefile
|
||||
hw/xquartz/doc/Makefile
|
||||
hw/xquartz/mach-startup/Makefile
|
||||
hw/xquartz/pbproxy/Makefile
|
||||
hw/xquartz/xpr/Makefile
|
||||
hw/kdrive/Makefile
|
||||
hw/kdrive/ati/Makefile
|
||||
|
|
|
@ -10,7 +10,7 @@ AM_CPPFLAGS = \
|
|||
-I$(top_srcdir)/miext/rootless
|
||||
|
||||
# GL
|
||||
SUBDIRS = bundle . xpr mach-startup doc
|
||||
SUBDIRS = bundle . xpr mach-startup doc pbproxy
|
||||
|
||||
libXquartz_la_SOURCES = \
|
||||
$(top_srcdir)/fb/fbcmap_mi.c \
|
||||
|
|
15
hw/xquartz/pbproxy/Makefile.am
Normal file
15
hw/xquartz/pbproxy/Makefile.am
Normal file
|
@ -0,0 +1,15 @@
|
|||
AM_CPPFLAGS=-F/System/Library/Frameworks/ApplicationServices.framework/Frameworks
|
||||
AM_LDFLAGS=-L/usr/X11/lib -lX11 -lAppleWM -framework AppKit -framework Foundation -framework ApplicationServices
|
||||
|
||||
noinst_PROGRAMS = pbproxy
|
||||
|
||||
pbproxy_SOURCES = \
|
||||
trick_autotools.c \
|
||||
main.m \
|
||||
x-input.m \
|
||||
x-selection.m
|
||||
|
||||
EXTRA_DIST = \
|
||||
pbproxy.h \
|
||||
x-selection.h
|
||||
|
168
hw/xquartz/pbproxy/main.m
Normal file
168
hw/xquartz/pbproxy/main.m
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* main.m
|
||||
$Id: main.m,v 1.29 2007-04-07 20:39:03 jharper Exp $
|
||||
|
||||
Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */
|
||||
|
||||
#include "pbproxy.h"
|
||||
#import "x-selection.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <X11/extensions/applewm.h>
|
||||
#include <HIServices/CoreDockServices.h>
|
||||
|
||||
Display *x_dpy;
|
||||
int x_apple_wm_event_base, x_apple_wm_error_base;
|
||||
|
||||
Atom x_atom_wm_state, x_atom_wm_protocols, x_atom_wm_delete_window;
|
||||
Atom x_atom_clipboard, x_atom_text, x_atom_utf8_string;
|
||||
Atom x_atom_targets, x_atom_multiple, x_atom_cstring;
|
||||
|
||||
static int x_grab_count;
|
||||
static Bool x_grab_synced;
|
||||
|
||||
static BOOL _is_active = YES; /* FIXME: should query server */
|
||||
|
||||
static x_selection *_selection_object;
|
||||
|
||||
/* X11 code */
|
||||
static void x_error_shutdown(void);
|
||||
|
||||
void x_grab_server (Bool sync) {
|
||||
if (x_grab_count++ == 0) {
|
||||
XGrabServer (x_dpy);
|
||||
}
|
||||
|
||||
if (sync && !x_grab_synced) {
|
||||
XSync (x_dpy, False);
|
||||
x_grab_synced = True;
|
||||
}
|
||||
}
|
||||
|
||||
void x_ungrab_server (void) {
|
||||
if (--x_grab_count == 0) {
|
||||
XUngrabServer (x_dpy);
|
||||
XFlush (x_dpy);
|
||||
x_grab_synced = False;
|
||||
}
|
||||
}
|
||||
|
||||
static int x_io_error_handler (Display *dpy) {
|
||||
/* We lost our connection to the server. */
|
||||
|
||||
TRACE ();
|
||||
|
||||
x_error_shutdown ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void x_init (void) {
|
||||
x_dpy = XOpenDisplay (NULL);
|
||||
if (x_dpy == NULL) {
|
||||
fprintf (stderr, "can't open default display\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
XSetIOErrorHandler (x_io_error_handler);
|
||||
x_atom_clipboard = XInternAtom (x_dpy, "CLIPBOARD", False);
|
||||
x_atom_text = XInternAtom (x_dpy, "TEXT", False);
|
||||
x_atom_utf8_string = XInternAtom (x_dpy, "UTF8_STRING", False);
|
||||
x_atom_targets = XInternAtom (x_dpy, "TARGETS", False);
|
||||
x_atom_multiple = XInternAtom (x_dpy, "MULTIPLE", False);
|
||||
x_atom_cstring = XInternAtom (x_dpy, "CSTRING", False);
|
||||
|
||||
if (!XAppleWMQueryExtension (x_dpy, &x_apple_wm_event_base,
|
||||
&x_apple_wm_error_base)) {
|
||||
fprintf (stderr, "can't open AppleWM server extension\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
XAppleWMSelectInput (x_dpy, AppleWMActivationNotifyMask |
|
||||
AppleWMPasteboardNotifyMask);
|
||||
|
||||
_selection_object = [[x_selection alloc] init];
|
||||
|
||||
x_input_register ();
|
||||
x_input_run ();
|
||||
}
|
||||
|
||||
static void x_shutdown (void) {
|
||||
[_selection_object release];
|
||||
_selection_object = nil;
|
||||
|
||||
XCloseDisplay (x_dpy);
|
||||
x_dpy = NULL;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void x_error_shutdown (void) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
id x_selection_object (void) {
|
||||
return _selection_object;
|
||||
}
|
||||
|
||||
Time x_current_timestamp (void) {
|
||||
/* FIXME: may want to fetch a timestamp from the server.. */
|
||||
return CurrentTime;
|
||||
}
|
||||
|
||||
|
||||
/* Finding things */
|
||||
BOOL x_get_is_active (void) {
|
||||
return _is_active;
|
||||
}
|
||||
|
||||
void x_set_is_active (BOOL state) {
|
||||
if (_is_active == state)
|
||||
return;
|
||||
|
||||
_is_active = state;
|
||||
}
|
||||
|
||||
/* Startup */
|
||||
static void signal_handler (int sig) {
|
||||
x_shutdown ();
|
||||
}
|
||||
|
||||
int main (int argc, const char *argv[]) {
|
||||
NSAutoreleasePool *pool;
|
||||
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
x_init ();
|
||||
|
||||
signal (SIGINT, signal_handler);
|
||||
signal (SIGTERM, signal_handler);
|
||||
signal (SIGPIPE, SIG_IGN);
|
||||
|
||||
while (1) {
|
||||
NS_DURING
|
||||
CFRunLoopRun ();
|
||||
NS_HANDLER
|
||||
NSString *s = [NSString stringWithFormat:@"%@ - %@",
|
||||
[localException name], [localException reason]];
|
||||
fprintf(stderr, "quartz-wm: caught exception: %s\n", [s UTF8String]);
|
||||
NS_ENDHANDLER
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void debug_printf (const char *fmt, ...) {
|
||||
static int spew = -1;
|
||||
|
||||
if (spew == -1) {
|
||||
char *x = getenv ("DEBUG");
|
||||
spew = (x != NULL && atoi (x) != 0);
|
||||
}
|
||||
|
||||
if (spew) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf (stderr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
41
hw/xquartz/pbproxy/pbproxy.h
Normal file
41
hw/xquartz/pbproxy/pbproxy.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* pbproxy.h
|
||||
Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */
|
||||
|
||||
#ifndef PBPROXY_H
|
||||
#define PBPROXY_H 1
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define Cursor X_Cursor
|
||||
#undef _SHAPE_H_
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/shape.h>
|
||||
#undef Cursor
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
/* from main.m */
|
||||
extern void x_set_is_active (BOOL state);
|
||||
extern BOOL x_get_is_active (void);
|
||||
extern id x_selection_object (void);
|
||||
extern Time x_current_timestamp (void);
|
||||
|
||||
extern Display *x_dpy;
|
||||
extern int x_apple_wm_event_base, x_apple_wm_error_base;
|
||||
extern Atom x_atom_clipboard, x_atom_text, x_atom_utf8_string;
|
||||
extern Atom x_atom_targets, x_atom_multiple, x_atom_cstring;
|
||||
|
||||
/* from x-input.m */
|
||||
extern void x_input_register (void);
|
||||
extern void x_input_run (void);
|
||||
|
||||
#if DEBUG == 0
|
||||
# define DB(msg, args...) do {} while (0)
|
||||
#else
|
||||
# define DB(msg, args...) debug_printf("%s:%s:%d " msg, __FILE__, __FUNCTION__, __LINE__, ##args)
|
||||
#endif
|
||||
|
||||
#define TRACE() DB("TRACE\n")
|
||||
extern void debug_printf (const char *fmt, ...);
|
||||
|
||||
#endif /* PBPROXY_H */
|
3
hw/xquartz/pbproxy/trick_autotools.c
Normal file
3
hw/xquartz/pbproxy/trick_autotools.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
int this_is_just_here_to_make_automake_work() {
|
||||
return 0;
|
||||
}
|
113
hw/xquartz/pbproxy/x-input.m
Normal file
113
hw/xquartz/pbproxy/x-input.m
Normal file
|
@ -0,0 +1,113 @@
|
|||
/* x-input.m -- event handling
|
||||
$Id: x-input.m,v 1.26 2007-04-07 20:39:03 jharper Exp $
|
||||
|
||||
Copyright (c) 2002 Apple Computer, Inc. All rights reserved. */
|
||||
|
||||
#include "pbproxy.h"
|
||||
#import "x-selection.h"
|
||||
|
||||
#include <CoreFoundation/CFSocket.h>
|
||||
#include <CoreFoundation/CFRunLoop.h>
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/extensions/applewm.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/* FIXME: .. */
|
||||
CFRunLoopSourceRef x_dpy_source;
|
||||
|
||||
/* Timestamp when the X server last told us it's active */
|
||||
static Time last_activation_time;
|
||||
|
||||
static void x_event_apple_wm_notify(XAppleWMNotifyEvent *e) {
|
||||
switch (e->type - x_apple_wm_event_base) {
|
||||
case AppleWMActivationNotify:
|
||||
switch (e->kind) {
|
||||
case AppleWMIsActive:
|
||||
last_activation_time = e->time;
|
||||
x_set_is_active (YES);
|
||||
[x_selection_object () x_active:e->time];
|
||||
break;
|
||||
|
||||
case AppleWMIsInactive:
|
||||
x_set_is_active (NO);
|
||||
[x_selection_object () x_inactive:e->time];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case AppleWMPasteboardNotify:
|
||||
switch (e->kind) {
|
||||
case AppleWMCopyToPasteboard:
|
||||
[x_selection_object () x_copy:e->time];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void x_input_run (void) {
|
||||
while (XPending (x_dpy) != 0) {
|
||||
XEvent e;
|
||||
|
||||
XNextEvent (x_dpy, &e);
|
||||
|
||||
switch (e.type) {
|
||||
case SelectionClear:
|
||||
[x_selection_object () clear_event:&e.xselectionclear];
|
||||
break;
|
||||
|
||||
case SelectionRequest:
|
||||
[x_selection_object () request_event:&e.xselectionrequest];
|
||||
break;
|
||||
|
||||
case SelectionNotify:
|
||||
[x_selection_object () notify_event:&e.xselection];
|
||||
break;
|
||||
|
||||
default:
|
||||
if (e.type - x_apple_wm_event_base >= 0
|
||||
&& e.type - x_apple_wm_event_base < AppleWMNumberEvents) {
|
||||
x_event_apple_wm_notify ((XAppleWMNotifyEvent *) &e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int add_input_socket (int sock, CFOptionFlags callback_types,
|
||||
CFSocketCallBack callback, const CFSocketContext *ctx,
|
||||
CFRunLoopSourceRef *cf_source) {
|
||||
CFSocketRef cf_sock;
|
||||
|
||||
cf_sock = CFSocketCreateWithNative (kCFAllocatorDefault, sock,
|
||||
callback_types, callback, ctx);
|
||||
if (cf_sock == NULL) {
|
||||
close (sock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*cf_source = CFSocketCreateRunLoopSource (kCFAllocatorDefault,
|
||||
cf_sock, 0);
|
||||
CFRelease (cf_sock);
|
||||
|
||||
if (*cf_source == NULL)
|
||||
return FALSE;
|
||||
|
||||
CFRunLoopAddSource (CFRunLoopGetCurrent (),
|
||||
*cf_source, kCFRunLoopDefaultMode);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void x_input_callback (CFSocketRef sock, CFSocketCallBackType type,
|
||||
CFDataRef address, const void *data, void *info) {
|
||||
x_input_run ();
|
||||
}
|
||||
|
||||
void x_input_register(void) {
|
||||
if (!add_input_socket (ConnectionNumber (x_dpy), kCFSocketReadCallBack,
|
||||
x_input_callback, NULL, &x_dpy_source)) {
|
||||
exit (1);
|
||||
}
|
||||
}
|
69
hw/xquartz/pbproxy/x-selection.h
Normal file
69
hw/xquartz/pbproxy/x-selection.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/* x-selection.h -- proxies between NSPasteboard and X11 selections
|
||||
$Id: x-selection.h,v 1.2 2002-12-13 00:21:00 jharper Exp $
|
||||
|
||||
Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
|
||||
HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name(s) of the above
|
||||
copyright holders shall not be used in advertising or otherwise to
|
||||
promote the sale, use or other dealings in this Software without
|
||||
prior written authorization. */
|
||||
|
||||
#ifndef X_SELECTION_H
|
||||
#define X_SELECTION_H 1
|
||||
|
||||
#include "pbproxy.h"
|
||||
#include <AppKit/NSPasteboard.h>
|
||||
|
||||
@interface x_selection : NSObject
|
||||
{
|
||||
@private
|
||||
|
||||
/* The unmapped window we use for fetching selections. */
|
||||
Window _selection_window;
|
||||
|
||||
/* Cached general pasteboard and array of types we can handle. */
|
||||
NSPasteboard *_pasteboard;
|
||||
NSArray *_known_types;
|
||||
|
||||
/* Last time we declared anything on the pasteboard. */
|
||||
int _my_last_change;
|
||||
|
||||
/* Name of the selection we're proxying onto the pasteboard. */
|
||||
Atom _proxied_selection;
|
||||
|
||||
/* When true, we're expecting a SelectionNotify event. */
|
||||
unsigned int _pending_notify :1;
|
||||
}
|
||||
|
||||
- (void) x_active:(Time)timestamp;
|
||||
- (void) x_inactive:(Time)timestamp;
|
||||
|
||||
- (void) x_copy:(Time)timestamp;
|
||||
|
||||
- (void) clear_event:(XSelectionClearEvent *)e;
|
||||
- (void) request_event:(XSelectionRequestEvent *)e;
|
||||
- (void) notify_event:(XSelectionEvent *)e;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* X_SELECTION_H */
|
491
hw/xquartz/pbproxy/x-selection.m
Normal file
491
hw/xquartz/pbproxy/x-selection.m
Normal file
|
@ -0,0 +1,491 @@
|
|||
/* x-selection.m -- proxies between NSPasteboard and X11 selections
|
||||
$Id: x-selection.m,v 1.9 2006-07-07 18:24:28 jharper Exp $
|
||||
|
||||
Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
|
||||
HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name(s) of the above
|
||||
copyright holders shall not be used in advertising or otherwise to
|
||||
promote the sale, use or other dealings in this Software without
|
||||
prior written authorization. */
|
||||
|
||||
#import "x-selection.h"
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@implementation x_selection
|
||||
|
||||
static unsigned long *
|
||||
read_prop_32 (Window id, Atom prop, int *nitems_ret)
|
||||
{
|
||||
int r, format;
|
||||
Atom type;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char *data;
|
||||
|
||||
r = XGetWindowProperty (x_dpy, id, prop, 0, 0,
|
||||
False, AnyPropertyType, &type, &format,
|
||||
&nitems, &bytes_after, &data);
|
||||
|
||||
if (r == Success && bytes_after != 0)
|
||||
{
|
||||
XFree (data);
|
||||
r = XGetWindowProperty (x_dpy, id, prop, 0,
|
||||
(bytes_after / 4) + 1, False,
|
||||
AnyPropertyType, &type, &format,
|
||||
&nitems, &bytes_after, &data);
|
||||
}
|
||||
|
||||
if (r != Success)
|
||||
return NULL;
|
||||
|
||||
if (format != 32)
|
||||
{
|
||||
XFree (data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*nitems_ret = nitems;
|
||||
return (unsigned long *) data;
|
||||
}
|
||||
|
||||
float
|
||||
get_time (void)
|
||||
{
|
||||
extern void Microseconds ();
|
||||
UnsignedWide usec;
|
||||
long long ll;
|
||||
|
||||
Microseconds (&usec);
|
||||
ll = ((long long) usec.hi << 32) | usec.lo;
|
||||
|
||||
return ll / 1e6;
|
||||
}
|
||||
|
||||
static Bool
|
||||
IfEventWithTimeout (Display *dpy, XEvent *e, int timeout,
|
||||
Bool (*pred) (Display *, XEvent *, XPointer),
|
||||
XPointer arg)
|
||||
{
|
||||
float start = get_time ();
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
do {
|
||||
if (XCheckIfEvent (x_dpy, e, pred, arg))
|
||||
return True;
|
||||
|
||||
FD_ZERO (&fds);
|
||||
FD_SET (ConnectionNumber (x_dpy), &fds);
|
||||
tv.tv_usec = 0;
|
||||
tv.tv_sec = timeout;
|
||||
|
||||
if (select (FD_SETSIZE, &fds, NULL, NULL, &tv) != 1)
|
||||
break;
|
||||
|
||||
} while (start + timeout > get_time ());
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Called when X11 becomes active (i.e. has key focus) */
|
||||
- (void) x_active:(Time)timestamp
|
||||
{
|
||||
TRACE ();
|
||||
|
||||
if ([_pasteboard changeCount] != _my_last_change)
|
||||
{
|
||||
if ([_pasteboard availableTypeFromArray: _known_types] != nil)
|
||||
{
|
||||
/* Pasteboard has data we should proxy; I think it makes
|
||||
sense to put it on both CLIPBOARD and PRIMARY */
|
||||
|
||||
XSetSelectionOwner (x_dpy, x_atom_clipboard,
|
||||
_selection_window, timestamp);
|
||||
XSetSelectionOwner (x_dpy, XA_PRIMARY,
|
||||
_selection_window, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when X11 loses key focus */
|
||||
- (void) x_inactive:(Time)timestamp
|
||||
{
|
||||
Window w;
|
||||
|
||||
TRACE ();
|
||||
|
||||
if (_proxied_selection == XA_PRIMARY)
|
||||
return;
|
||||
|
||||
w = XGetSelectionOwner (x_dpy, x_atom_clipboard);
|
||||
|
||||
if (w != None && w != _selection_window)
|
||||
{
|
||||
/* An X client has the selection, proxy it to the pasteboard */
|
||||
|
||||
_my_last_change = [_pasteboard declareTypes:_known_types owner:self];
|
||||
_proxied_selection = x_atom_clipboard;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when the Edit/Copy item on the main X11 menubar is selected
|
||||
and no appkit window claims it. */
|
||||
- (void) x_copy:(Time)timestamp
|
||||
{
|
||||
Window w;
|
||||
|
||||
/* Lazily copies the PRIMARY selection to the pasteboard. */
|
||||
|
||||
w = XGetSelectionOwner (x_dpy, XA_PRIMARY);
|
||||
|
||||
if (w != None && w != _selection_window)
|
||||
{
|
||||
XSetSelectionOwner (x_dpy, x_atom_clipboard,
|
||||
_selection_window, timestamp);
|
||||
_my_last_change = [_pasteboard declareTypes:_known_types owner:self];
|
||||
_proxied_selection = XA_PRIMARY;
|
||||
}
|
||||
else
|
||||
{
|
||||
XBell (x_dpy, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* X events */
|
||||
|
||||
- (void) clear_event:(XSelectionClearEvent *)e
|
||||
{
|
||||
TRACE ();
|
||||
|
||||
/* Right now we don't care about this. */
|
||||
}
|
||||
|
||||
static Atom
|
||||
convert_1 (XSelectionRequestEvent *e, NSString *data, Atom target, Atom prop)
|
||||
{
|
||||
Atom ret = None;
|
||||
|
||||
if (data == nil)
|
||||
return ret;
|
||||
|
||||
if (target == x_atom_text)
|
||||
target = x_atom_utf8_string;
|
||||
|
||||
if (target == XA_STRING
|
||||
|| target == x_atom_cstring
|
||||
|| target == x_atom_utf8_string)
|
||||
{
|
||||
const char *bytes;
|
||||
|
||||
if (target == XA_STRING)
|
||||
bytes = [data lossyCString];
|
||||
else
|
||||
bytes = [data UTF8String];
|
||||
|
||||
if (bytes != NULL)
|
||||
{
|
||||
XChangeProperty (x_dpy, e->requestor, prop, target,
|
||||
8, PropModeReplace, (unsigned char *) bytes,
|
||||
strlen (bytes));
|
||||
ret = prop;
|
||||
}
|
||||
}
|
||||
/* FIXME: handle COMPOUND_TEXT target */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void) request_event:(XSelectionRequestEvent *)e
|
||||
{
|
||||
/* Someone's asking us for the data on the pasteboard */
|
||||
|
||||
XEvent reply;
|
||||
NSString *data;
|
||||
Atom target;
|
||||
|
||||
TRACE ();
|
||||
|
||||
reply.xselection.type = SelectionNotify;
|
||||
reply.xselection.selection = e->selection;
|
||||
reply.xselection.target = e->target;
|
||||
reply.xselection.requestor = e->requestor;
|
||||
reply.xselection.time = e->time;
|
||||
reply.xselection.property = None;
|
||||
|
||||
target = e->target;
|
||||
|
||||
if (target == x_atom_targets)
|
||||
{
|
||||
long data[2];
|
||||
|
||||
data[0] = x_atom_utf8_string;
|
||||
data[1] = XA_STRING;
|
||||
|
||||
XChangeProperty (x_dpy, e->requestor, e->property, target,
|
||||
8, PropModeReplace, (unsigned char *) &data,
|
||||
sizeof (data));
|
||||
reply.xselection.property = e->property;
|
||||
}
|
||||
else if (target == x_atom_multiple)
|
||||
{
|
||||
if (e->property != None)
|
||||
{
|
||||
int i, nitems;
|
||||
unsigned long *atoms;
|
||||
|
||||
atoms = read_prop_32 (e->requestor, e->property, &nitems);
|
||||
|
||||
if (atoms != NULL)
|
||||
{
|
||||
data = [_pasteboard stringForType:NSStringPboardType];
|
||||
|
||||
for (i = 0; i < nitems; i += 2)
|
||||
{
|
||||
Atom target = atoms[i], prop = atoms[i+1];
|
||||
|
||||
atoms[i+1] = convert_1 (e, data, target, prop);
|
||||
}
|
||||
|
||||
XChangeProperty (x_dpy, e->requestor, e->property, target,
|
||||
32, PropModeReplace, (unsigned char *) atoms,
|
||||
nitems);
|
||||
XFree (atoms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data = [_pasteboard stringForType:NSStringPboardType];
|
||||
if (data != nil)
|
||||
{
|
||||
reply.xselection.property = convert_1 (e, data, target, e->property);
|
||||
}
|
||||
|
||||
XSendEvent (x_dpy, e->requestor, False, 0, &reply);
|
||||
}
|
||||
|
||||
- (void) notify_event:(XSelectionEvent *)e
|
||||
{
|
||||
/* Someone sent us data we're waiting for. */
|
||||
|
||||
Atom type;
|
||||
int format, r, offset;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char *data, *buf;
|
||||
NSString *string;
|
||||
|
||||
TRACE ();
|
||||
|
||||
if (e->target == x_atom_targets)
|
||||
{
|
||||
/* Was trying to fetch the TARGETS property; it lists the
|
||||
formats supported by the selection owner. */
|
||||
|
||||
unsigned long *atoms;
|
||||
int natoms;
|
||||
int i, utf8_i = -1, string_i = -1;
|
||||
|
||||
if (e->property != None
|
||||
&& (atoms = read_prop_32 (e->requestor,
|
||||
e->property, &natoms)) != NULL)
|
||||
{
|
||||
for (i = 0; i < natoms; i++)
|
||||
{
|
||||
if (atoms[i] == XA_STRING)
|
||||
string_i = i;
|
||||
else if (atoms[i] == x_atom_utf8_string)
|
||||
utf8_i = i;
|
||||
}
|
||||
XFree (atoms);
|
||||
}
|
||||
|
||||
/* May as well try as STRING if nothing else, it can only
|
||||
fail, and it will help broken clients who don't support
|
||||
the TARGETS selection.. */
|
||||
|
||||
if (utf8_i >= 0)
|
||||
type = x_atom_utf8_string;
|
||||
else
|
||||
type = XA_STRING;
|
||||
|
||||
XConvertSelection (x_dpy, e->selection, type,
|
||||
e->selection, e->requestor, e->time);
|
||||
_pending_notify = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->property == None)
|
||||
return; /* FIXME: notify pasteboard? */
|
||||
|
||||
/* Should be the data. Find out how big it is and what format it's in. */
|
||||
|
||||
r = XGetWindowProperty (x_dpy, e->requestor, e->property,
|
||||
0, 0, False, AnyPropertyType, &type,
|
||||
&format, &nitems, &bytes_after, &data);
|
||||
if (r != Success)
|
||||
return;
|
||||
|
||||
XFree (data);
|
||||
if (type == None || format != 8)
|
||||
return;
|
||||
|
||||
bytes_after += nitems;
|
||||
|
||||
/* Read it into a buffer. */
|
||||
|
||||
buf = malloc (bytes_after + 1);
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
for (offset = 0; bytes_after > 0; offset += nitems)
|
||||
{
|
||||
r = XGetWindowProperty (x_dpy, e->requestor, e->property,
|
||||
offset / 4, (bytes_after / 4) + 1,
|
||||
False, AnyPropertyType, &type,
|
||||
&format, &nitems, &bytes_after, &data);
|
||||
if (r != Success)
|
||||
{
|
||||
free (buf);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy (buf + offset, data, nitems);
|
||||
XFree (data);
|
||||
}
|
||||
buf[offset] = 0;
|
||||
XDeleteProperty (x_dpy, e->requestor, e->property);
|
||||
|
||||
/* Convert to an NSString and write to the pasteboard. */
|
||||
|
||||
if (type == XA_STRING)
|
||||
string = [NSString stringWithCString:(char *) buf];
|
||||
else /* if (type == x_atom_utf8_string) */
|
||||
string = [NSString stringWithUTF8String:(char *) buf];
|
||||
|
||||
free (buf);
|
||||
|
||||
[_pasteboard setString:string forType:NSStringPboardType];
|
||||
}
|
||||
|
||||
|
||||
/* NSPasteboard-required methods */
|
||||
|
||||
static Bool
|
||||
selnotify_pred (Display *dpy, XEvent *e, XPointer arg)
|
||||
{
|
||||
return e->type == SelectionNotify;
|
||||
}
|
||||
|
||||
- (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
|
||||
{
|
||||
XEvent e;
|
||||
Atom request;
|
||||
|
||||
TRACE ();
|
||||
|
||||
/* Don't ask for the data yet, first find out which formats
|
||||
the selection owner supports. */
|
||||
|
||||
request = x_atom_targets;
|
||||
|
||||
again:
|
||||
XConvertSelection (x_dpy, _proxied_selection, request,
|
||||
_proxied_selection, _selection_window, CurrentTime);
|
||||
|
||||
_pending_notify = YES;
|
||||
|
||||
/* Seems like we need to be synchronous here.. Actually, this really
|
||||
sucks, since it means we could get deadlocked if people don't
|
||||
respond to our request. So we need to implement our own timeout
|
||||
code.. */
|
||||
|
||||
while (_pending_notify
|
||||
&& IfEventWithTimeout (x_dpy, &e, 1, selnotify_pred, NULL))
|
||||
{
|
||||
_pending_notify = NO;
|
||||
[self notify_event:&e.xselection];
|
||||
}
|
||||
|
||||
if (_pending_notify && request == x_atom_targets)
|
||||
{
|
||||
/* App didn't respond to request for TARGETS selection. Let's
|
||||
try the STRING selection as a last resort.. Helps broken
|
||||
applications (e.g. nedit, see #3199867) */
|
||||
|
||||
request = XA_STRING;
|
||||
goto again;
|
||||
}
|
||||
|
||||
_pending_notify = NO;
|
||||
}
|
||||
|
||||
- (void) pasteboardChangedOwner:(NSPasteboard *)sender
|
||||
{
|
||||
TRACE ();
|
||||
|
||||
/* Right now we don't care with this. */
|
||||
}
|
||||
|
||||
|
||||
/* Allocation */
|
||||
|
||||
- init
|
||||
{
|
||||
unsigned long pixel;
|
||||
|
||||
self = [super init];
|
||||
if (self == nil)
|
||||
return nil;
|
||||
|
||||
_pasteboard = [[NSPasteboard generalPasteboard] retain];
|
||||
|
||||
_known_types = [[NSArray arrayWithObject:NSStringPboardType] retain];
|
||||
|
||||
pixel = BlackPixel (x_dpy, DefaultScreen (x_dpy));
|
||||
_selection_window = XCreateSimpleWindow (x_dpy, DefaultRootWindow (x_dpy),
|
||||
0, 0, 1, 1, 0, pixel, pixel);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[_pasteboard releaseGlobally];
|
||||
[_pasteboard release];
|
||||
_pasteboard = nil;
|
||||
|
||||
[_known_types release];
|
||||
_known_types = nil;
|
||||
|
||||
if (_selection_window != 0)
|
||||
{
|
||||
XDestroyWindow (x_dpy, _selection_window);
|
||||
_selection_window = 0;
|
||||
}
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user