From bcb83eea729a01026d99d1cfc2b77385b5b275fd Mon Sep 17 00:00:00 2001
From: Jeremy Huddleston <jeremyhu@freedesktop.org>
Date: Wed, 15 Oct 2008 14:11:18 -0700
Subject: [PATCH 02/16] XQuartz: implemented primary-on-grab and fixed clipboard-to-pasteboard

---
 hw/xquartz/pbproxy/Makefile.am   |    2 +-
 hw/xquartz/pbproxy/app-main.m    |    3 +-
 hw/xquartz/pbproxy/main.m        |   13 ++++++++--
 hw/xquartz/pbproxy/pbproxy.h     |    4 ++-
 hw/xquartz/pbproxy/x-input.m     |    8 ++++--
 hw/xquartz/pbproxy/x-selection.h |    2 +
 hw/xquartz/pbproxy/x-selection.m |   44 ++++++++++++++++++++++++++++++-------
 7 files changed, 58 insertions(+), 18 deletions(-)

diff --git hw/xquartz/pbproxy/Makefile.am hw/xquartz/pbproxy/Makefile.am
index fd93ce1..2eee766 100644
--- hw/xquartz/pbproxy/Makefile.am
+++ hw/xquartz/pbproxy/Makefile.am
@@ -1,5 +1,5 @@
 AM_CPPFLAGS=-F/System/Library/Frameworks/ApplicationServices.framework/Frameworks
-AM_LDFLAGS=-L/usr/X11/lib -lX11 -lAppleWM -framework AppKit -framework Foundation -framework ApplicationServices
+AM_LDFLAGS=-L/usr/X11/lib -lX11 -lXfixes -lAppleWM -framework AppKit -framework Foundation -framework ApplicationServices
 
 SOURCE_FILES = \
 	trick_autotools.c \
diff --git hw/xquartz/pbproxy/app-main.m hw/xquartz/pbproxy/app-main.m
index 4847851..3dfdcb4 100644
--- hw/xquartz/pbproxy/app-main.m
+++ hw/xquartz/pbproxy/app-main.m
@@ -19,7 +19,8 @@ int main (int argc, const char *argv[]) {
     printf("pid: %u\n", getpid());
 #endif
 
-    x_init ();
+    if(x_init () !=0)
+        return 1;
     
     signal (SIGINT, signal_handler);
     signal (SIGTERM, signal_handler);
diff --git hw/xquartz/pbproxy/main.m hw/xquartz/pbproxy/main.m
index 448bec4..ded18e1 100644
--- hw/xquartz/pbproxy/main.m
+++ hw/xquartz/pbproxy/main.m
@@ -8,9 +8,12 @@
 
 #include <pthread.h>
 #include <X11/extensions/applewm.h>
+#include <X11/extensions/xfixes.h>
 
 Display *x_dpy;
 int x_apple_wm_event_base, x_apple_wm_error_base;
+int x_xfixes_event_base, x_xfixes_error_base;
+BOOL have_xfixes;
 
 x_selection *_selection_object;
 
@@ -31,13 +34,13 @@ static int x_error_handler (Display *dpy, XErrorEvent *errevent) {
     return 0;
 }
 
-void x_init (void) {
+int x_init (void) {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
     x_dpy = XOpenDisplay (NULL);
     if (x_dpy == NULL) {
         fprintf (stderr, "can't open default display\n");
-        exit (1);
+        return 1;
     }
     
     XSetIOErrorHandler (x_io_error_handler);
@@ -46,9 +49,11 @@ void x_init (void) {
     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);
+        return 1;
     }
     
+    have_xfixes = XFixesQueryExtension(x_dpy, &x_xfixes_event_base, &x_xfixes_error_base);
+    
     XAppleWMSelectInput (x_dpy, AppleWMActivationNotifyMask |
                          AppleWMPasteboardNotifyMask);
     
@@ -58,6 +63,8 @@ void x_init (void) {
     x_input_run ();
 
     [pool release];
+    
+    return 0;
 }
 
 id x_selection_object (void) {
diff --git hw/xquartz/pbproxy/pbproxy.h hw/xquartz/pbproxy/pbproxy.h
index bfeb868..5d4652b 100644
--- hw/xquartz/pbproxy/pbproxy.h
+++ hw/xquartz/pbproxy/pbproxy.h
@@ -21,10 +21,12 @@ 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 void x_init (void);
+extern int x_init (void);
 
 extern Display *x_dpy;
 extern int x_apple_wm_event_base, x_apple_wm_error_base;
+extern int x_xfixes_event_base, x_xfixes_error_base;
+extern BOOL have_xfixes;
 
 /* from x-input.m */
 extern void x_input_register (void);
diff --git hw/xquartz/pbproxy/x-input.m hw/xquartz/pbproxy/x-input.m
index 1b2475c..b34c39a 100644
--- hw/xquartz/pbproxy/x-input.m
+++ hw/xquartz/pbproxy/x-input.m
@@ -87,9 +87,11 @@ void x_input_run (void) {
                 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);
+                if(e.type >= x_apple_wm_event_base &&
+                   e.type < x_apple_wm_event_base + AppleWMNumberEvents) {
+                    x_event_apple_wm_notify((XAppleWMNotifyEvent *) &e);
+                } else if(e.type == x_xfixes_event_base + XFixesSelectionNotify) {
+                    [x_selection_object() xfixes_selection_notify:(XFixesSelectionNotifyEvent *)&e];
                 }
                 break;
         }
diff --git hw/xquartz/pbproxy/x-selection.h hw/xquartz/pbproxy/x-selection.h
index c93b676..0be8d81 100644
--- hw/xquartz/pbproxy/x-selection.h
+++ hw/xquartz/pbproxy/x-selection.h
@@ -33,6 +33,7 @@
 
 #include "pbproxy.h"
 #include <AppKit/NSPasteboard.h>
+#include <X11/extensions/xfixes.h>
 
 /* This stores image data or text. */
 struct propdata {
@@ -95,6 +96,7 @@ struct atom_list {
 - (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;
diff --git hw/xquartz/pbproxy/x-selection.m hw/xquartz/pbproxy/x-selection.m
index 6a1a9fd..888c9e8 100644
--- hw/xquartz/pbproxy/x-selection.m
+++ hw/xquartz/pbproxy/x-selection.m
@@ -36,6 +36,7 @@
 #include <X11/Xutil.h>
 #import <AppKit/NSBitmapImageRep.h>
 
+#include <X11/extensions/xfixes.h>
 
 /*
  * The basic design of the pbproxy code is as follows.
@@ -56,12 +57,10 @@
 
 /*
  * TODO:
- * 1. handle primary_on_grab
- * 2. handle  MULTIPLE - I need to study the ICCCM further.
- * 3. Handle PICT images properly.
- * 4. Handle NSPasteboard updates immediately, not on active/inactive
+ * 1. handle  MULTIPLE - I need to study the ICCCM further.
+ * 2. Handle PICT images properly.
+ * 3. Handle NSPasteboard updates immediately, not on active/inactive
  *    - Open xterm, run 'cat readme.txt | pbcopy'
- * 5. Detect if CLIPBOARD_MANAGER atom belongs to a dead client rather than just None
  */
 
 static struct {
@@ -357,7 +356,7 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
 
 	    XSetSelectionOwner (x_dpy, atoms->clipboard,
 				_selection_window, timestamp);
-	    XSetSelectionOwner (x_dpy, XA_PRIMARY,
+	    XSetSelectionOwner (x_dpy, atoms->primary,
 				_selection_window, timestamp);
 	}
 #endif
@@ -421,8 +420,8 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
         if(owner == _selection_window)
             return TRUE;
 
-        if(None != _selection_window) {
-            fprintf (stderr, "A clipboard manager is already running.  pbproxy will not sync clipboard to pasteboard.\n");
+        if(owner != None) {
+            fprintf (stderr, "A clipboard manager is already running on window 0x%x.  pbproxy will not sync clipboard to pasteboard.\n", (int)owner);
             return FALSE;
         }
         
@@ -1008,6 +1007,24 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
     }
 }
 
+- (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:
+            fprintf(stderr, "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. */
@@ -1023,7 +1040,7 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
 	 * This isn't required by the ICCCM, but some apps apparently 
 	 * don't respond to TARGETS properly.
 	 */
-	preferred = XA_STRING;
+	preferred = atoms->string;
     }
 
     DB ("requesting %s\n", XGetAtomName (x_dpy, preferred));
@@ -1260,6 +1277,11 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
     pbproxy_prefs.clipboard_to_pasteboard = prefs_get_bool(CFSTR("sync_clibpoard_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);
+    
+    if(pbproxy_prefs.active && pbproxy_prefs.primary_on_grab && !have_xfixes) {
+        fprintf(stderr, "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)])
@@ -1335,6 +1357,10 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato
     pending_copy = 0;
     pending_clipboard = 0;
 
+    if(have_xfixes)
+        XFixesSelectSelectionInput(x_dpy, _selection_window, atoms->primary, 
+                                   XFixesSetSelectionOwnerNotifyMask);
+
     [self reload_preferences];
     
     return self;
-- 
1.6.1

