From f7673bb4de3c1f71eb390a3279eed3589efc3df4 Mon Sep 17 00:00:00 2001 From: Jeremy Huddleston Date: Sat, 4 Oct 2008 18:54:15 -0700 Subject: [PATCH] XQuartz: xpbproxy: Support some of the preference toggles from X11.app, cleaned up CLIPBOARD_MANAGER atom management. --- hw/xquartz/pbproxy/app-main.m | 4 +- hw/xquartz/pbproxy/main.m | 4 - hw/xquartz/pbproxy/pbproxy.h | 4 +- hw/xquartz/pbproxy/x-input.m | 60 +++++----- hw/xquartz/pbproxy/x-selection.h | 2 +- hw/xquartz/pbproxy/x-selection.m | 185 ++++++++++++++++++------------- 6 files changed, 144 insertions(+), 115 deletions(-) diff --git a/hw/xquartz/pbproxy/app-main.m b/hw/xquartz/pbproxy/app-main.m index 7611eefea..4847851fa 100644 --- a/hw/xquartz/pbproxy/app-main.m +++ b/hw/xquartz/pbproxy/app-main.m @@ -15,8 +15,10 @@ static void signal_handler (int sig) { } int main (int argc, const char *argv[]) { +#ifdef TEST printf("pid: %u\n", getpid()); - +#endif + x_init (); signal (SIGINT, signal_handler); diff --git a/hw/xquartz/pbproxy/main.m b/hw/xquartz/pbproxy/main.m index e2156a758..448bec4e0 100644 --- a/hw/xquartz/pbproxy/main.m +++ b/hw/xquartz/pbproxy/main.m @@ -55,10 +55,6 @@ void x_init (void) { _selection_object = [[x_selection alloc] init]; x_input_register (); - - [_selection_object set_clipboard_manager]; - [_selection_object claim_clipboard]; - x_input_run (); [pool release]; diff --git a/hw/xquartz/pbproxy/pbproxy.h b/hw/xquartz/pbproxy/pbproxy.h index bd5f65b4f..bfeb8685a 100644 --- a/hw/xquartz/pbproxy/pbproxy.h +++ b/hw/xquartz/pbproxy/pbproxy.h @@ -12,7 +12,9 @@ #include #undef Cursor +#ifndef DEBUG #define DEBUG 0 +#endif /* from main.m */ extern void x_set_is_active (BOOL state); @@ -27,7 +29,7 @@ extern int x_apple_wm_event_base, x_apple_wm_error_base; /* 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 diff --git a/hw/xquartz/pbproxy/x-input.m b/hw/xquartz/pbproxy/x-input.m index c11ffb382..1b2475cb9 100644 --- a/hw/xquartz/pbproxy/x-input.m +++ b/hw/xquartz/pbproxy/x-input.m @@ -21,29 +21,35 @@ static CFRunLoopSourceRef x_dpy_source; static Time last_activation_time; static void x_event_apple_wm_notify(XAppleWMNotifyEvent *e) { + int type = e->type - x_apple_wm_event_base; + int kind = e->kind; - switch (e->type - x_apple_wm_event_base) { + /* We want to reload prefs even if we're not active */ + if(type == AppleWMActivationNotify && + kind == AppleWMReloadPreferences) + [x_selection_object() reload_preferences]; + + if(![x_selection_object() is_active]) + return; + + switch (type) { case AppleWMActivationNotify: - switch (e->kind) { + switch (kind) { case AppleWMIsActive: last_activation_time = e->time; - [x_selection_object () x_active:e->time]; + [x_selection_object() x_active:e->time]; break; case AppleWMIsInactive: - [x_selection_object () x_inactive:e->time]; + [x_selection_object() x_inactive:e->time]; break; - - case AppleWMReloadPreferences: - [x_selection_object () reload_preferences]; - break; } break; case AppleWMPasteboardNotify: - switch (e->kind) { - case AppleWMCopyToPasteboard: - [x_selection_object () x_copy:e->time]; + switch (kind) { + case AppleWMCopyToPasteboard: + [x_selection_object() x_copy:e->time]; } break; } @@ -54,22 +60,18 @@ void x_input_run (void) { if (nil == pool) { - fprintf(stderr, "unable to allocate/init auto release pool!\n"); - return; + fprintf(stderr, "unable to allocate/init auto release pool!\n"); + return; } - + while (XPending (x_dpy) != 0) { - XEvent e; - + XEvent e; XNextEvent (x_dpy, &e); - - /* If pbproxy isn't active (in the preferences), then don't do anything. */ - if (![x_selection_object() is_active]) - continue; - + switch (e.type) { case SelectionClear: - [x_selection_object () clear_event:&e.xselectionclear]; + if([x_selection_object() is_active]) + [x_selection_object () clear_event:&e.xselectionclear]; break; case SelectionRequest: @@ -80,10 +82,10 @@ void x_input_run (void) { [x_selection_object () notify_event:&e.xselection]; break; - case PropertyNotify: - [x_selection_object () property_event:&e.xproperty]; - break; - + case PropertyNotify: + [x_selection_object () property_event:&e.xproperty]; + break; + default: if (e.type - x_apple_wm_event_base >= 0 && e.type - x_apple_wm_event_base < AppleWMNumberEvents) { @@ -91,10 +93,10 @@ void x_input_run (void) { } break; } - - XFlush(x_dpy); + + XFlush(x_dpy); } - + [pool release]; } diff --git a/hw/xquartz/pbproxy/x-selection.h b/hw/xquartz/pbproxy/x-selection.h index 9c408b4ec..c93b6761b 100644 --- a/hw/xquartz/pbproxy/x-selection.h +++ b/hw/xquartz/pbproxy/x-selection.h @@ -97,7 +97,7 @@ struct atom_list { - (void) property_event:(XPropertyEvent *)e; - (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct propdata *)pdata; - (void) claim_clipboard; -- (void) set_clipboard_manager; +- (BOOL) set_clipboard_manager_status:(BOOL)value; - (void) own_clipboard; - (void) copy_completed:(Atom)selection; diff --git a/hw/xquartz/pbproxy/x-selection.m b/hw/xquartz/pbproxy/x-selection.m index 0997b3a22..67de866fe 100644 --- a/hw/xquartz/pbproxy/x-selection.m +++ b/hw/xquartz/pbproxy/x-selection.m @@ -56,22 +56,36 @@ /* * TODO: - * 1. finish handling these pbproxy control knobs. + * 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 + * - Open xterm, run 'cat readme.txt | pbcopy' + * 5. Detect if CLIPBOARD_MANAGER atom belongs to a dead client rather than just None */ -// These will be set by X11Controller.m once this is integrated into a server thread -BOOL pbproxy_active = YES; -BOOL pbproxy_primary_on_grab = NO; // 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 pbproxy_clipboard_to_pasteboard = YES; -BOOL pbproxy_pasteboard_to_primary = YES; -BOOL pbproxy_pasteboard_to_clipboard = YES; +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}; +#define APP_PREFS "org.x.X11" +static BOOL prefs_get_bool (CFStringRef key, BOOL def) { + int ret; + Boolean ok; + + ret = CFPreferencesGetAppBooleanValue (key, CFSTR (APP_PREFS), &ok); + + return ok ? (BOOL) ret : def; +} + static void init_propdata (struct propdata *pdata) { @@ -321,19 +335,17 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato if (countNow != changeCount) { - DB ("changed pasteboard!\n"); - changeCount = countNow; - - if (pbproxy_pasteboard_to_primary) - { - - XSetSelectionOwner (x_dpy, atoms->primary, _selection_window, CurrentTime); - } - - if (pbproxy_pasteboard_to_clipboard) - { - [self own_clipboard]; - } + DB ("changed pasteboard!\n"); + changeCount = countNow; + + if (pbproxy_prefs.pasteboard_to_primary) + { + XSetSelectionOwner (x_dpy, atoms->primary, _selection_window, CurrentTime); + } + + if (pbproxy_prefs.pasteboard_to_clipboard) { + [self own_clipboard]; + } } #if 0 @@ -395,23 +407,36 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato } } -/* - * Set pbproxy as owner of the SELECTION_MANAGER selection. +/* Set pbproxy as owner of the SELECTION_MANAGER selection. * This prevents tools like xclipboard from causing havoc. + * Returns TRUE on success */ -- (void) set_clipboard_manager +- (BOOL) set_clipboard_manager_status:(BOOL)value { TRACE (); - if (None != XGetSelectionOwner (x_dpy, atoms->clipboard_manager)) - { - fprintf (stderr, "A clipboard manager is already running!\n" - "pbproxy can not continue!\n"); - exit (EXIT_FAILURE); - } + Window owner = XGetSelectionOwner (x_dpy, atoms->clipboard_manager); - XSetSelectionOwner (x_dpy, atoms->clipboard_manager, _selection_window, - CurrentTime); + if(value) { + 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"); +// return FALSE; +// } + + XSetSelectionOwner(x_dpy, atoms->clipboard_manager, _selection_window, CurrentTime); + return (_selection_window == XGetSelectionOwner(x_dpy, atoms->clipboard_manager)); + } else { + if(owner != _selection_window) + return TRUE; + + XSetSelectionOwner(x_dpy, atoms->clipboard_manager, None, CurrentTime); + return(None == XGetSelectionOwner(x_dpy, atoms->clipboard_manager)); + } + + return FALSE; } /* @@ -423,28 +448,25 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato TRACE (); DB ("e->selection %s\n", XGetAtomName (x_dpy, e->selection)); - - if (atoms->clipboard == e->selection) - { - /* - * 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 (atoms->clipboard_manager == e->selection) - { - /* Another CLIPBOARD_MANAGER has set itself as owner. - * a) we can call [self set_clipboard_manager] here and risk a war. - * b) we can print a message and exit. Ideally we would popup a message box. - */ - fprintf (stderr, "error: another clipboard manager was started!\n"); - //exit (EXIT_FAILURE); + + 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. + */ + fprintf(stderr, "Another clipboard manager was started! xpbproxy is disabling syncing with clipboard.\n"); + pbproxy_prefs.clipboard_to_pasteboard = NO; + } } } @@ -456,32 +478,29 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato Window owner; TRACE (); - - if (!pbproxy_clipboard_to_pasteboard) - return; + + if (!pbproxy_prefs.clipboard_to_pasteboard) + return; owner = XGetSelectionOwner (x_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. - */ - DB ("No clipboard owner.\n"); - [self copy_completed:atoms->clipboard]; - return; - } - else if (owner == _selection_window) - { - [self copy_completed:atoms->clipboard]; - return; + if (None == owner) { + /* + * The owner probably died or we are just starting up pbproxy. + * Set pbproxy's _selection_window as the owner, and continue. + */ + DB ("No clipboard owner.\n"); + [self copy_completed:atoms->clipboard]; + return; + } else if (owner == _selection_window) { + [self copy_completed:atoms->clipboard]; + return; } DB ("requesting targets\n"); - + request_atom = atoms->targets; XConvertSelection (x_dpy, atoms->clipboard, atoms->targets, - atoms->clipboard, _selection_window, CurrentTime); + atoms->clipboard, _selection_window, CurrentTime); XFlush (x_dpy); /* Now we will get a SelectionNotify event in the future. */ } @@ -1236,18 +1255,25 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato - (void) reload_preferences { - if (pbproxy_clipboard_to_pasteboard) - { - [self claim_clipboard]; - } + 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_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); + + /* 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_active; + return pbproxy_prefs.active; } - /* NSPasteboard-required methods */ - (void) paste:(id)sender @@ -1267,7 +1293,6 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato /* Right now we don't care with this. */ } - /* Allocation */ - init @@ -1310,6 +1335,8 @@ get_property(Window win, Atom property, struct propdata *pdata, Bool delete, Ato pending_copy = 0; pending_clipboard = 0; + [self reload_preferences]; + return self; }