xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

x-selection.m (43447B)


      1 /* x-selection.m -- proxies between NSPasteboard and X11 selections
      2  *
      3  * Copyright (c) 2002-2012 Apple Inc. All rights reserved.
      4  *
      5  * Permission is hereby granted, free of charge, to any person
      6  * obtaining a copy of this software and associated documentation files
      7  * (the "Software"), to deal in the Software without restriction,
      8  * including without limitation the rights to use, copy, modify, merge,
      9  * publish, distribute, sublicense, and/or sell copies of the Software,
     10  * and to permit persons to whom the Software is furnished to do so,
     11  * subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be
     14  * included in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
     20  * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     21  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     23  * DEALINGS IN THE SOFTWARE.
     24  *
     25  * Except as contained in this notice, the name(s) of the above
     26  * copyright holders shall not be used in advertising or otherwise to
     27  * promote the sale, use or other dealings in this Software without
     28  * prior written authorization.
     29  */
     30 
     31 #import "x-selection.h"
     32 
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <X11/Xatom.h>
     36 #include <X11/Xutil.h>
     37 #import <AppKit/NSGraphics.h>
     38 #import <AppKit/NSImage.h>
     39 #import <AppKit/NSBitmapImageRep.h>
     40 
     41 /*
     42  * The basic design of the pbproxy code is as follows.
     43  *
     44  * When a client selects text, say from an xterm - we only copy it when the
     45  * X11 Edit->Copy menu item is pressed or the shortcut activated.  In this
     46  * case we take the PRIMARY selection, and set it as the NSPasteboard data.
     47  *
     48  * When an X11 client copies something to the CLIPBOARD, pbproxy greedily grabs
     49  * the data, sets it as the NSPasteboard data, and finally sets itself as
     50  * owner of the CLIPBOARD.
     51  *
     52  * When an X11 window is activated we check to see if the NSPasteboard has
     53  * changed.  If the NSPasteboard has changed, then we set pbproxy as owner
     54  * of the PRIMARY and CLIPBOARD and respond to requests for text and images.
     55  *
     56  * The behavior is now dynamic since the information above was written.
     57  * The behavior is now dependent on the pbproxy_prefs below.
     58  */
     59 
     60 /*
     61  * TODO:
     62  * 1. handle MULTIPLE - I need to study the ICCCM further, and find a test app.
     63  * 2. Handle NSPasteboard updates immediately, not on active/inactive
     64  *    - Open xterm, run 'cat readme.txt | pbcopy'
     65  */
     66 
     67 static struct {
     68     BOOL active;
     69     BOOL primary_on_grab; /* This is provided as an option for people who
     70                            * want it and has issues that won't ever be
     71                            * addressed to make it *always* work.
     72                            */
     73     BOOL clipboard_to_pasteboard;
     74     BOOL pasteboard_to_primary;
     75     BOOL pasteboard_to_clipboard;
     76 } pbproxy_prefs = { YES, NO, YES, YES, YES };
     77 
     78 @implementation x_selection
     79 
     80 static struct propdata null_propdata = {
     81     NULL, 0, 0
     82 };
     83 
     84 #ifdef DEBUG
     85 static void
     86 dump_prefs()
     87 {
     88     ErrorF("pbproxy preferences:\n"
     89            "\tactive %u\n"
     90            "\tprimary_on_grab %u\n"
     91            "\tclipboard_to_pasteboard %u\n"
     92            "\tpasteboard_to_primary %u\n"
     93            "\tpasteboard_to_clipboard %u\n",
     94            pbproxy_prefs.active,
     95            pbproxy_prefs.primary_on_grab,
     96            pbproxy_prefs.clipboard_to_pasteboard,
     97            pbproxy_prefs.pasteboard_to_primary,
     98            pbproxy_prefs.pasteboard_to_clipboard);
     99 }
    100 #endif
    101 
    102 extern CFStringRef app_prefs_domain_cfstr;
    103 
    104 static BOOL
    105 prefs_get_bool(CFStringRef key, BOOL defaultValue)
    106 {
    107     Boolean value, ok;
    108 
    109     value = CFPreferencesGetAppBooleanValue(key, app_prefs_domain_cfstr, &ok);
    110 
    111     return ok ? (BOOL)value : defaultValue;
    112 }
    113 
    114 static void
    115 init_propdata(struct propdata *pdata)
    116 {
    117     *pdata = null_propdata;
    118 }
    119 
    120 static void
    121 free_propdata(struct propdata *pdata)
    122 {
    123     free(pdata->data);
    124     *pdata = null_propdata;
    125 }
    126 
    127 /*
    128  * Return True if an error occurs.  Return False if pdata has data
    129  * and we finished.
    130  * The property is only deleted when bytesleft is 0 if delete is True.
    131  */
    132 static Bool
    133 get_property(Window win, Atom property, struct propdata *pdata, Bool delete,
    134              Atom *type)
    135 {
    136     long offset = 0;
    137     unsigned long numitems, bytesleft = 0;
    138 #ifdef TEST
    139     /* This is used to test the growth handling. */
    140     unsigned long length = 4UL;
    141 #else
    142     unsigned long length = (100000UL + 3) / 4;
    143 #endif
    144     unsigned char *buf = NULL, *chunk = NULL;
    145     size_t buflen = 0, chunkbytesize = 0;
    146     int format;
    147 
    148     TRACE();
    149 
    150     if (None == property)
    151         return True;
    152 
    153     do {
    154         unsigned long newbuflen = 0;
    155         unsigned char *newbuf = NULL;
    156 
    157 #ifdef TEST
    158         ErrorF("bytesleft %lu\n", bytesleft);
    159 #endif
    160 
    161         if (Success != XGetWindowProperty(xpbproxy_dpy, win, property,
    162                                           offset, length, delete,
    163                                           AnyPropertyType,
    164                                           type, &format, &numitems,
    165                                           &bytesleft, &chunk)) {
    166             DebugF("Error while getting window property.\n");
    167             *pdata = null_propdata;
    168             free(buf);
    169             return True;
    170         }
    171 
    172 #ifdef TEST
    173         ErrorF("format %d numitems %lu bytesleft %lu\n",
    174                format, numitems, bytesleft);
    175 
    176         ErrorF("type %s\n", XGetAtomName(xpbproxy_dpy, *type));
    177 #endif
    178 
    179         /* Format is the number of bits. */
    180         if (format == 8)
    181             chunkbytesize = numitems;
    182         else if (format == 16)
    183             chunkbytesize = numitems * sizeof(short);
    184         else if (format == 32)
    185             chunkbytesize = numitems * sizeof(long);
    186 
    187 #ifdef TEST
    188         ErrorF("chunkbytesize %zu\n", chunkbytesize);
    189 #endif
    190         newbuflen = buflen + chunkbytesize;
    191         if (newbuflen > 0) {
    192             newbuf = realloc(buf, newbuflen);
    193 
    194             if (NULL == newbuf) {
    195                 XFree(chunk);
    196                 free(buf);
    197                 return True;
    198             }
    199 
    200             memcpy(newbuf + buflen, chunk, chunkbytesize);
    201             XFree(chunk);
    202             buf = newbuf;
    203             buflen = newbuflen;
    204             /* offset is a multiple of 32 bits*/
    205             offset += chunkbytesize / 4;
    206         }
    207         else {
    208             if (chunk)
    209                 XFree(chunk);
    210         }
    211 
    212 #ifdef TEST
    213         ErrorF("bytesleft %lu\n", bytesleft);
    214 #endif
    215     } while (bytesleft > 0);
    216 
    217     pdata->data = buf;
    218     pdata->length = buflen;
    219     pdata->format = format;
    220 
    221     return /*success*/ False;
    222 }
    223 
    224 /* Implementation methods */
    225 
    226 /* This finds the preferred type from a TARGETS list.*/
    227 - (Atom) find_preferred:(struct propdata *)pdata
    228 {
    229     Atom a = None;
    230     size_t i, step;
    231     Bool png = False, jpeg = False, utf8 = False, string = False;
    232 
    233     TRACE();
    234 
    235     if (pdata->format != 32) {
    236         ErrorF(
    237             "Atom list is expected to be formatted as an array of 32bit values.\n");
    238         return None;
    239     }
    240 
    241     for (i = 0, step = sizeof(long); i < pdata->length; i += step) {
    242         a = (Atom) * (long *)(pdata->data + i);
    243 
    244         if (a == atoms->image_png) {
    245             png = True;
    246         }
    247         else if (a == atoms->image_jpeg) {
    248             jpeg = True;
    249         }
    250         else if (a == atoms->utf8_string) {
    251             utf8 = True;
    252         }
    253         else if (a == atoms->string) {
    254             string = True;
    255         }
    256         else {
    257             char *type = XGetAtomName(xpbproxy_dpy, a);
    258             if (type) {
    259                 DebugF("Unhandled X11 mime type: %s", type);
    260                 XFree(type);
    261             }
    262         }
    263     }
    264 
    265     /*We prefer PNG over strings, and UTF8 over a Latin-1 string.*/
    266     if (png)
    267         return atoms->image_png;
    268 
    269     if (jpeg)
    270         return atoms->image_jpeg;
    271 
    272     if (utf8)
    273         return atoms->utf8_string;
    274 
    275     if (string)
    276         return atoms->string;
    277 
    278     /* This is evidently something we don't know how to handle.*/
    279     return None;
    280 }
    281 
    282 /* Return True if this is an INCR-style transfer. */
    283 - (Bool) is_incr_type:(XSelectionEvent *)e
    284 {
    285     Atom seltype;
    286     int format;
    287     unsigned long numitems = 0UL, bytesleft = 0UL;
    288     unsigned char *chunk;
    289 
    290     TRACE();
    291 
    292     if (Success != XGetWindowProperty(xpbproxy_dpy, e->requestor, e->property,
    293                                       /*offset*/ 0L, /*length*/ 4UL,
    294                                       /*Delete*/ False,
    295                                       AnyPropertyType, &seltype, &format,
    296                                       &numitems, &bytesleft, &chunk)) {
    297         return False;
    298     }
    299 
    300     if (chunk)
    301         XFree(chunk);
    302 
    303     return (seltype == atoms->incr) ? True : False;
    304 }
    305 
    306 /*
    307  * This should be called after a selection has been copied,
    308  * or when the selection is unfinished before a transfer completes.
    309  */
    310 - (void) release_pending
    311 {
    312     TRACE();
    313 
    314     free_propdata(&pending.propdata);
    315     pending.requestor = None;
    316     pending.selection = None;
    317 }
    318 
    319 /* Return True if an error occurs during an append.*/
    320 /* Return False if the append succeeds. */
    321 - (Bool) append_to_pending:(struct propdata *)pdata requestor:(Window)
    322    requestor
    323 {
    324     unsigned char *newdata;
    325     size_t newlength;
    326 
    327     TRACE();
    328 
    329     if (requestor != pending.requestor) {
    330         [self release_pending];
    331         pending.requestor = requestor;
    332     }
    333 
    334     newlength = pending.propdata.length + pdata->length;
    335     newdata = realloc(pending.propdata.data, newlength);
    336 
    337     if (NULL == newdata) {
    338         perror("realloc propdata");
    339         [self release_pending];
    340         return True;
    341     }
    342 
    343     memcpy(newdata + pending.propdata.length, pdata->data, pdata->length);
    344     pending.propdata.data = newdata;
    345     pending.propdata.length = newlength;
    346 
    347     return False;
    348 }
    349 
    350 /* Called when X11 becomes active (i.e. has key focus) */
    351 - (void) x_active:(Time)timestamp
    352 {
    353     static NSInteger changeCount;
    354     NSInteger countNow;
    355     NSPasteboard *pb;
    356 
    357     TRACE();
    358 
    359     pb = [NSPasteboard generalPasteboard];
    360 
    361     if (nil == pb)
    362         return;
    363 
    364     countNow = [pb changeCount];
    365 
    366     if (countNow != changeCount) {
    367         DebugF("changed pasteboard!\n");
    368         changeCount = countNow;
    369 
    370         if (pbproxy_prefs.pasteboard_to_primary) {
    371             XSetSelectionOwner(xpbproxy_dpy, atoms->primary,
    372                                _selection_window,
    373                                CurrentTime);
    374         }
    375 
    376         if (pbproxy_prefs.pasteboard_to_clipboard) {
    377             [self own_clipboard];
    378         }
    379     }
    380 
    381 #if 0
    382     /*gstaplin: we should perhaps investigate something like this branch above...*/
    383     if ([_pasteboard availableTypeFromArray: _known_types] != nil) {
    384         /* Pasteboard has data we should proxy; I think it makes
    385            sense to put it on both CLIPBOARD and PRIMARY */
    386 
    387         XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard,
    388                            _selection_window, timestamp);
    389         XSetSelectionOwner(xpbproxy_dpy, atoms->primary,
    390                            _selection_window, timestamp);
    391     }
    392 #endif
    393 }
    394 
    395 /* Called when X11 loses key focus */
    396 - (void) x_inactive:(Time)timestamp
    397 {
    398     TRACE();
    399 }
    400 
    401 /* This requests the TARGETS list from the PRIMARY selection owner. */
    402 - (void) x_copy_request_targets
    403 {
    404     TRACE();
    405 
    406     request_atom = atoms->targets;
    407     XConvertSelection(xpbproxy_dpy, atoms->primary, atoms->targets,
    408                       atoms->primary, _selection_window, CurrentTime);
    409 }
    410 
    411 /* Called when the Edit/Copy item on the main X11 menubar is selected
    412  * and no appkit window claims it. */
    413 - (void) x_copy:(Time)timestamp
    414 {
    415     Window w;
    416 
    417     TRACE();
    418 
    419     w = XGetSelectionOwner(xpbproxy_dpy, atoms->primary);
    420 
    421     if (None != w) {
    422         ++pending_copy;
    423 
    424         if (1 == pending_copy) {
    425             /*
    426              * There are no other copy operations in progress, so we
    427              * can proceed safely.  Otherwise the copy_completed method
    428              * will see that the pending_copy is > 1, and do another copy.
    429              */
    430             [self x_copy_request_targets];
    431         }
    432     }
    433 }
    434 
    435 /* Set pbproxy as owner of the SELECTION_MANAGER selection.
    436  * This prevents tools like xclipboard from causing havoc.
    437  * Returns TRUE on success
    438  */
    439 - (BOOL) set_clipboard_manager_status:(BOOL)value
    440 {
    441     TRACE();
    442 
    443     Window owner = XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager);
    444 
    445     if (value) {
    446         if (owner == _selection_window)
    447             return TRUE;
    448 
    449         if (owner != None) {
    450             ErrorF(
    451                 "A clipboard manager using window 0x%lx already owns the clipboard selection.  "
    452                 "pbproxy will not sync clipboard to pasteboard.\n", owner);
    453             return FALSE;
    454         }
    455 
    456         XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager,
    457                            _selection_window,
    458                            CurrentTime);
    459         return (_selection_window ==
    460                 XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager));
    461     }
    462     else {
    463         if (owner != _selection_window)
    464             return TRUE;
    465 
    466         XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager, None,
    467                            CurrentTime);
    468         return (None ==
    469                 XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard_manager));
    470     }
    471 
    472     return FALSE;
    473 }
    474 
    475 /*
    476  * This occurs when we previously owned a selection,
    477  * and then lost it from another client.
    478  */
    479 - (void) clear_event:(XSelectionClearEvent *)e
    480 {
    481 
    482     TRACE();
    483 
    484     DebugF("e->selection %s\n", XGetAtomName(xpbproxy_dpy, e->selection));
    485 
    486     if (e->selection == atoms->clipboard) {
    487         /*
    488          * We lost ownership of the CLIPBOARD.
    489          */
    490         ++pending_clipboard;
    491 
    492         if (1 == pending_clipboard) {
    493             /* Claim the clipboard contents from the new owner. */
    494             [self claim_clipboard];
    495         }
    496     }
    497     else if (e->selection == atoms->clipboard_manager) {
    498         if (pbproxy_prefs.clipboard_to_pasteboard) {
    499             /* Another CLIPBOARD_MANAGER has set itself as owner.  Disable syncing
    500              * to avoid a race.
    501              */
    502             ErrorF("Another clipboard manager was started!  "
    503                    "xpbproxy is disabling syncing with clipboard.\n");
    504             pbproxy_prefs.clipboard_to_pasteboard = NO;
    505         }
    506     }
    507 }
    508 
    509 /*
    510  * We greedily acquire the clipboard after it changes, and on startup.
    511  */
    512 - (void) claim_clipboard
    513 {
    514     Window owner;
    515 
    516     TRACE();
    517 
    518     if (!pbproxy_prefs.clipboard_to_pasteboard)
    519         return;
    520 
    521     owner = XGetSelectionOwner(xpbproxy_dpy, atoms->clipboard);
    522     if (None == owner) {
    523         /*
    524          * The owner probably died or we are just starting up pbproxy.
    525          * Set pbproxy's _selection_window as the owner, and continue.
    526          */
    527         DebugF("No clipboard owner.\n");
    528         [self copy_completed:atoms->clipboard];
    529         return;
    530     }
    531     else if (owner == _selection_window) {
    532         [self copy_completed:atoms->clipboard];
    533         return;
    534     }
    535 
    536     DebugF("requesting targets\n");
    537 
    538     request_atom = atoms->targets;
    539     XConvertSelection(xpbproxy_dpy, atoms->clipboard, atoms->targets,
    540                       atoms->clipboard, _selection_window, CurrentTime);
    541     XFlush(xpbproxy_dpy);
    542     /* Now we will get a SelectionNotify event in the future. */
    543 }
    544 
    545 /* Greedily acquire the clipboard. */
    546 - (void) own_clipboard
    547 {
    548 
    549     TRACE();
    550 
    551     /* We should perhaps have a boundary limit on the number of iterations... */
    552     do {
    553         XSetSelectionOwner(xpbproxy_dpy, atoms->clipboard, _selection_window,
    554                            CurrentTime);
    555     } while (_selection_window != XGetSelectionOwner(xpbproxy_dpy,
    556                                                      atoms->clipboard));
    557 }
    558 
    559 - (void) init_reply:(XEvent *)reply request:(XSelectionRequestEvent *)e
    560 {
    561     reply->xselection.type = SelectionNotify;
    562     reply->xselection.selection = e->selection;
    563     reply->xselection.target = e->target;
    564     reply->xselection.requestor = e->requestor;
    565     reply->xselection.time = e->time;
    566     reply->xselection.property = None;
    567 }
    568 
    569 - (void) send_reply:(XEvent *)reply
    570 {
    571     /*
    572      * We are supposed to use an empty event mask, and not propagate
    573      * the event, according to the ICCCM.
    574      */
    575     DebugF("reply->xselection.requestor 0x%lx\n", reply->xselection.requestor);
    576 
    577     XSendEvent(xpbproxy_dpy, reply->xselection.requestor, False, 0, reply);
    578     XFlush(xpbproxy_dpy);
    579 }
    580 
    581 /*
    582  * This responds to a TARGETS request.
    583  * The result is a list of a ATOMs that correspond to the types available
    584  * for a selection.
    585  * For instance an application might provide a UTF8_STRING and a STRING
    586  * (in Latin-1 encoding).  The requestor can then make the choice based on
    587  * the list.
    588  */
    589 - (void) send_targets:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)
    590    pb
    591 {
    592     XEvent reply;
    593     NSArray *pbtypes;
    594 
    595     [self init_reply:&reply request:e];
    596 
    597     pbtypes = [pb types];
    598     if (pbtypes) {
    599         long list[7]; /* Don't forget to increase this if we handle more types! */
    600         long count = 0;
    601 
    602         /*
    603          * I'm not sure if this is needed, but some toolkits/clients list
    604          * TARGETS in response to targets.
    605          */
    606         list[count] = atoms->targets;
    607         ++count;
    608 
    609         if ([pbtypes containsObject:NSStringPboardType]) {
    610             /* We have a string type that we can convert to UTF8, or Latin-1... */
    611             DebugF("NSStringPboardType\n");
    612             list[count] = atoms->utf8_string;
    613             ++count;
    614             list[count] = atoms->string;
    615             ++count;
    616             list[count] = atoms->compound_text;
    617             ++count;
    618         }
    619 
    620         /* TODO add the NSPICTPboardType back again, once we have conversion
    621          * functionality in send_image.
    622          */
    623 #ifdef __clang__
    624 #pragma clang diagnostic push
    625 #pragma clang diagnostic ignored "-Wdeprecated-declarations" // NSPICTPboardType
    626 #endif
    627 
    628         if ([pbtypes containsObject:NSPICTPboardType]
    629             || [pbtypes containsObject:NSTIFFPboardType]) {
    630             /* We can convert a TIFF to a PNG or JPEG. */
    631             DebugF("NSTIFFPboardType\n");
    632             list[count] = atoms->image_png;
    633             ++count;
    634             list[count] = atoms->image_jpeg;
    635             ++count;
    636         }
    637 
    638 #ifdef __clang__
    639 #pragma clang diagnostic pop
    640 #endif
    641 
    642         if (count) {
    643             /* We have a list of ATOMs to send. */
    644             XChangeProperty(xpbproxy_dpy, e->requestor, e->property,
    645                             atoms->atom, 32,
    646                             PropModeReplace, (unsigned char *)list,
    647                             count);
    648 
    649             reply.xselection.property = e->property;
    650         }
    651     }
    652 
    653     [self send_reply:&reply];
    654 }
    655 
    656 - (void) send_string:(XSelectionRequestEvent *)e utf8:(BOOL)utf8 pasteboard:(
    657        NSPasteboard *)pb
    658 {
    659     XEvent reply;
    660     NSArray *pbtypes;
    661     NSString *data;
    662     const char *bytes;
    663     NSUInteger length;
    664 
    665     TRACE();
    666 
    667     [self init_reply:&reply request:e];
    668 
    669     pbtypes = [pb types];
    670 
    671     if (![pbtypes containsObject:NSStringPboardType]) {
    672         [self send_reply:&reply];
    673         return;
    674     }
    675 
    676     DebugF("pbtypes retainCount after containsObject: %lu\n",
    677            [pbtypes retainCount]);
    678 
    679     data = [pb stringForType:NSStringPboardType];
    680 
    681     if (nil == data) {
    682         [self send_reply:&reply];
    683         return;
    684     }
    685 
    686     if (utf8) {
    687         bytes = [data UTF8String];
    688         /*
    689          * We don't want the UTF-8 string length here.
    690          * We want the length in bytes.
    691          */
    692         length = strlen(bytes);
    693 
    694         if (length < 50) {
    695             DebugF("UTF-8: %s\n", bytes);
    696             DebugF("UTF-8 length: %lu\n", length);
    697         }
    698     }
    699     else {
    700         DebugF("Latin-1\n");
    701         bytes = [data cStringUsingEncoding:NSISOLatin1StringEncoding];
    702         /*WARNING: bytes is not NUL-terminated. */
    703         length = [data lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
    704     }
    705 
    706     DebugF("e->target %s\n", XGetAtomName(xpbproxy_dpy, e->target));
    707 
    708     XChangeProperty(xpbproxy_dpy, e->requestor, e->property, e->target,
    709                     8, PropModeReplace, (unsigned char *)bytes, length);
    710 
    711     reply.xselection.property = e->property;
    712 
    713     [self send_reply:&reply];
    714 }
    715 
    716 - (void) send_compound_text:(XSelectionRequestEvent *)e pasteboard:(
    717        NSPasteboard *)pb
    718 {
    719     XEvent reply;
    720     NSArray *pbtypes;
    721 
    722     TRACE();
    723 
    724     [self init_reply:&reply request:e];
    725 
    726     pbtypes = [pb types];
    727 
    728     if ([pbtypes containsObject: NSStringPboardType]) {
    729         NSString *data = [pb stringForType:NSStringPboardType];
    730         if (nil != data) {
    731             /*
    732              * Cast to (void *) to avoid a const warning.
    733              * AFAIK Xutf8TextListToTextProperty does not modify the input memory.
    734              */
    735             void *utf8 = (void *)[data UTF8String];
    736             char *list[] = { utf8, NULL };
    737             XTextProperty textprop;
    738 
    739             textprop.value = NULL;
    740 
    741             if (Success == Xutf8TextListToTextProperty(xpbproxy_dpy, list, 1,
    742                                                        XCompoundTextStyle,
    743                                                        &textprop)) {
    744 
    745                 if (8 != textprop.format)
    746                     DebugF(
    747                         "textprop.format is unexpectedly not 8 - it's %d instead\n",
    748                         textprop.format);
    749 
    750                 XChangeProperty(xpbproxy_dpy, e->requestor, e->property,
    751                                 atoms->compound_text, textprop.format,
    752                                 PropModeReplace, textprop.value,
    753                                 textprop.nitems);
    754 
    755                 reply.xselection.property = e->property;
    756             }
    757 
    758             if (textprop.value)
    759                 XFree(textprop.value);
    760 
    761         }
    762     }
    763 
    764     [self send_reply:&reply];
    765 }
    766 
    767 /* Finding a test application that uses MULTIPLE has proven to be difficult. */
    768 - (void) send_multiple:(XSelectionRequestEvent *)e
    769 {
    770     XEvent reply;
    771 
    772     TRACE();
    773 
    774     [self init_reply:&reply request:e];
    775 
    776     if (None != e->property) {}
    777 
    778     [self send_reply:&reply];
    779 }
    780 
    781 /* Return nil if an error occurred. */
    782 /* DO NOT retain the encdata for longer than the length of an event response.
    783  * The autorelease pool will reuse/free it.
    784  */
    785 - (NSData *) encode_image_data:(NSData *)data type:(NSBitmapImageFileType)
    786    enctype
    787 {
    788     NSBitmapImageRep *bmimage = nil;
    789     NSData *encdata = nil;
    790     NSDictionary *dict = nil;
    791 
    792     bmimage = [[NSBitmapImageRep alloc] initWithData:data];
    793 
    794     if (nil == bmimage)
    795         return nil;
    796 
    797     dict = [[NSDictionary alloc] init];
    798     encdata = [bmimage representationUsingType:enctype properties:dict];
    799 
    800     if (nil == encdata) {
    801         [dict autorelease];
    802         [bmimage autorelease];
    803         return nil;
    804     }
    805 
    806     [dict autorelease];
    807     [bmimage autorelease];
    808 
    809     return encdata;
    810 }
    811 
    812 /* Return YES when an error has occurred when trying to send the PICT. */
    813 /* The caller should send a default response with a property of None when an error occurs. */
    814 - (BOOL) send_image_pict_reply:(XSelectionRequestEvent *)e
    815                     pasteboard:(NSPasteboard *)pb
    816                           type:(NSBitmapImageFileType)imagetype
    817 {
    818     XEvent reply;
    819     NSImage *img = nil;
    820     NSData *data = nil, *encdata = nil;
    821     NSUInteger length;
    822     const void *bytes = NULL;
    823 
    824     img = [[NSImage alloc] initWithPasteboard:pb];
    825 
    826     if (nil == img) {
    827         return YES;
    828     }
    829 
    830     data = [img TIFFRepresentation];
    831 
    832     if (nil == data) {
    833         [img autorelease];
    834         ErrorF("unable to convert PICT to TIFF!\n");
    835         return YES;
    836     }
    837 
    838     encdata = [self encode_image_data:data type:imagetype];
    839     if (nil == encdata) {
    840         [img autorelease];
    841         return YES;
    842     }
    843 
    844     [self init_reply:&reply request:e];
    845 
    846     length = [encdata length];
    847     bytes = [encdata bytes];
    848 
    849     XChangeProperty(xpbproxy_dpy, e->requestor, e->property, e->target,
    850                     8, PropModeReplace, bytes, length);
    851     reply.xselection.property = e->property;
    852 
    853     [self send_reply:&reply];
    854 
    855     [img autorelease];
    856 
    857     return NO; /*no error*/
    858 }
    859 
    860 /* Return YES if an error occurred. */
    861 /* The caller should send a reply with a property of None when an error occurs. */
    862 - (BOOL) send_image_tiff_reply:(XSelectionRequestEvent *)e
    863                     pasteboard:(NSPasteboard *)pb
    864                           type:(NSBitmapImageFileType)imagetype
    865 {
    866     XEvent reply;
    867     NSData *data = nil;
    868     NSData *encdata = nil;
    869     NSUInteger length;
    870     const void *bytes = NULL;
    871 
    872     data = [pb dataForType:NSTIFFPboardType];
    873 
    874     if (nil == data)
    875         return YES;
    876 
    877     encdata = [self encode_image_data:data type:imagetype];
    878 
    879     if (nil == encdata)
    880         return YES;
    881 
    882     [self init_reply:&reply request:e];
    883 
    884     length = [encdata length];
    885     bytes = [encdata bytes];
    886 
    887     XChangeProperty(xpbproxy_dpy, e->requestor, e->property, e->target,
    888                     8, PropModeReplace, bytes, length);
    889     reply.xselection.property = e->property;
    890 
    891     [self send_reply:&reply];
    892 
    893     return NO; /*no error*/
    894 }
    895 
    896 - (void) send_image:(XSelectionRequestEvent *)e pasteboard:(NSPasteboard *)pb
    897 {
    898     NSArray *pbtypes = nil;
    899     NSBitmapImageFileType imagetype = NSPNGFileType;
    900 
    901     TRACE();
    902 
    903     if (e->target == atoms->image_png)
    904         imagetype = NSPNGFileType;
    905     else if (e->target == atoms->image_jpeg)
    906         imagetype = NSJPEGFileType;
    907     else {
    908         ErrorF(
    909             "internal failure in xpbproxy!  imagetype being sent isn't PNG or JPEG.\n");
    910     }
    911 
    912     pbtypes = [pb types];
    913 
    914     if (pbtypes) {
    915         if ([pbtypes containsObject:NSTIFFPboardType]) {
    916             if (NO ==
    917                 [self send_image_tiff_reply:e pasteboard:pb type:imagetype])
    918                 return;
    919         }
    920 #ifdef __clang__
    921 #pragma clang diagnostic push
    922 #pragma clang diagnostic ignored "-Wdeprecated-declarations" // NSPICTPboardType
    923 #endif
    924         else if ([pbtypes containsObject:NSPICTPboardType])
    925 #ifdef __clang__
    926 #pragma clang diagnostic pop
    927 #endif
    928         {
    929             if (NO ==
    930                 [self send_image_pict_reply:e pasteboard:pb type:imagetype])
    931                 return;
    932 
    933             /* Fall through intentionally to the send_none: */
    934         }
    935     }
    936 
    937     [self send_none:e];
    938 }
    939 
    940 - (void)send_none:(XSelectionRequestEvent *)e
    941 {
    942     XEvent reply;
    943 
    944     TRACE();
    945 
    946     [self init_reply:&reply request:e];
    947     [self send_reply:&reply];
    948 }
    949 
    950 /* Another client requested the data or targets of data available from the clipboard. */
    951 - (void)request_event:(XSelectionRequestEvent *)e
    952 {
    953     NSPasteboard *pb;
    954 
    955     TRACE();
    956 
    957     /* TODO We should also keep track of the time of the selection, and
    958      * according to the ICCCM "refuse the request" if the event timestamp
    959      * is before we owned it.
    960      * What should we base the time on?  How can we get the current time just
    961      * before an XSetSelectionOwner?  Is it the server's time, or the clients?
    962      * According to the XSelectionRequestEvent manual page, the Time value
    963      * may be set to CurrentTime or a time, so that makes it a bit different.
    964      * Perhaps we should just punt and ignore races.
    965      */
    966 
    967     /*TODO we need a COMPOUND_TEXT test app*/
    968     /*TODO we need a MULTIPLE test app*/
    969 
    970     pb = [NSPasteboard generalPasteboard];
    971     if (nil == pb) {
    972         [self send_none:e];
    973         return;
    974     }
    975 
    976     if (None != e->target)
    977         DebugF("e->target %s\n", XGetAtomName(xpbproxy_dpy, e->target));
    978 
    979     if (e->target == atoms->targets) {
    980         /* The paste requestor wants to know what TARGETS we support. */
    981         [self send_targets:e pasteboard:pb];
    982     }
    983     else if (e->target == atoms->multiple) {
    984         /*
    985          * This isn't finished, and may never be, unless I can find
    986          * a good test app.
    987          */
    988         [self send_multiple:e];
    989     }
    990     else if (e->target == atoms->utf8_string) {
    991         [self send_string:e utf8:YES pasteboard:pb];
    992     }
    993     else if (e->target == atoms->string) {
    994         [self send_string:e utf8:NO pasteboard:pb];
    995     }
    996     else if (e->target == atoms->compound_text) {
    997         [self send_compound_text:e pasteboard:pb];
    998     }
    999     else if (e->target == atoms->multiple) {
   1000         [self send_multiple:e];
   1001     }
   1002     else if (e->target == atoms->image_png || e->target ==
   1003              atoms->image_jpeg) {
   1004         [self send_image:e pasteboard:pb];
   1005     }
   1006     else {
   1007         [self send_none:e];
   1008     }
   1009 }
   1010 
   1011 /* This handles the events resulting from an XConvertSelection request. */
   1012 - (void) notify_event:(XSelectionEvent *)e
   1013 {
   1014     Atom type;
   1015     struct propdata pdata;
   1016 
   1017     TRACE();
   1018 
   1019     [self release_pending];
   1020 
   1021     if (None == e->property) {
   1022         DebugF("e->property is None.\n");
   1023         [self copy_completed:e->selection];
   1024         /* Nothing is selected. */
   1025         return;
   1026     }
   1027 
   1028 #if 0
   1029     ErrorF("e->selection %s\n", XGetAtomName(xpbproxy_dpy, e->selection));
   1030     ErrorF("e->property %s\n", XGetAtomName(xpbproxy_dpy, e->property));
   1031 #endif
   1032 
   1033     if ([self is_incr_type:e]) {
   1034         /*
   1035          * This is an INCR-style transfer, which means that we
   1036          * will get the data after a series of PropertyNotify events.
   1037          */
   1038         DebugF("is INCR\n");
   1039 
   1040         if (get_property(e->requestor, e->property, &pdata, /*Delete*/ True,
   1041                          &type)) {
   1042             /*
   1043              * An error occurred, so we should invoke the copy_completed:, but
   1044              * not handle_selection:type:propdata:
   1045              */
   1046             [self copy_completed:e->selection];
   1047             return;
   1048         }
   1049 
   1050         free_propdata(&pdata);
   1051 
   1052         pending.requestor = e->requestor;
   1053         pending.selection = e->selection;
   1054 
   1055         DebugF("set pending.requestor to 0x%lx\n", pending.requestor);
   1056     }
   1057     else {
   1058         if (get_property(e->requestor, e->property, &pdata, /*Delete*/ True,
   1059                          &type)) {
   1060             [self copy_completed:e->selection];
   1061             return;
   1062         }
   1063 
   1064         /* We have the complete selection data.*/
   1065         [self handle_selection:e->selection type:type propdata:&pdata];
   1066 
   1067         DebugF("handled selection with the first notify_event\n");
   1068     }
   1069 }
   1070 
   1071 /* This is used for INCR transfers.  See the ICCCM for the details. */
   1072 /* This is used to retrieve PRIMARY and CLIPBOARD selections. */
   1073 - (void) property_event:(XPropertyEvent *)e
   1074 {
   1075     struct propdata pdata;
   1076     Atom type;
   1077 
   1078     TRACE();
   1079 
   1080     if (None != e->atom) {
   1081 #ifdef DEBUG
   1082         char *name = XGetAtomName(xpbproxy_dpy, e->atom);
   1083 
   1084         if (name) {
   1085             DebugF("e->atom %s\n", name);
   1086             XFree(name);
   1087         }
   1088 #endif
   1089     }
   1090 
   1091     if (None != pending.requestor && PropertyNewValue == e->state) {
   1092         DebugF("pending.requestor 0x%lx\n", pending.requestor);
   1093 
   1094         if (get_property(e->window, e->atom, &pdata, /*Delete*/ True,
   1095                          &type)) {
   1096             [self copy_completed:pending.selection];
   1097             [self release_pending];
   1098             return;
   1099         }
   1100 
   1101         if (0 == pdata.length) {
   1102             /*
   1103              * We completed the transfer.
   1104              * handle_selection will call copy_completed: for us.
   1105              */
   1106             [self handle_selection:pending.selection type:type propdata:&
   1107              pending.propdata];
   1108             free_propdata(&pdata);
   1109             pending.propdata = null_propdata;
   1110             pending.requestor = None;
   1111             pending.selection = None;
   1112         }
   1113         else {
   1114             [self append_to_pending:&pdata requestor:e->window];
   1115             free_propdata(&pdata);
   1116         }
   1117     }
   1118 }
   1119 
   1120 - (void) xfixes_selection_notify:(XFixesSelectionNotifyEvent *)e
   1121 {
   1122     if (!pbproxy_prefs.active)
   1123         return;
   1124 
   1125     switch (e->subtype) {
   1126     case XFixesSetSelectionOwnerNotify:
   1127         if (e->selection == atoms->primary && pbproxy_prefs.primary_on_grab)
   1128             [self x_copy:e->timestamp];
   1129         break;
   1130 
   1131     case XFixesSelectionWindowDestroyNotify:
   1132     case XFixesSelectionClientCloseNotify:
   1133     default:
   1134         ErrorF("Unhandled XFixesSelectionNotifyEvent: subtype=%d\n",
   1135                e->subtype);
   1136         break;
   1137     }
   1138 }
   1139 
   1140 - (void) handle_targets: (Atom)selection propdata:(struct propdata *)pdata
   1141 {
   1142     /* Find a type we can handle and prefer from the list of ATOMs. */
   1143     Atom preferred;
   1144     char *name;
   1145 
   1146     TRACE();
   1147 
   1148     preferred = [self find_preferred:pdata];
   1149 
   1150     if (None == preferred) {
   1151         /*
   1152          * This isn't required by the ICCCM, but some apps apparently
   1153          * don't respond to TARGETS properly.
   1154          */
   1155         preferred = atoms->string;
   1156     }
   1157 
   1158     (void)name; /* Avoid a warning with non-debug compiles. */
   1159 #ifdef DEBUG
   1160     name = XGetAtomName(xpbproxy_dpy, preferred);
   1161 
   1162     if (name) {
   1163         DebugF("requesting %s\n", name);
   1164     }
   1165 #endif
   1166     request_atom = preferred;
   1167     XConvertSelection(xpbproxy_dpy, selection, preferred, selection,
   1168                       _selection_window, CurrentTime);
   1169 }
   1170 
   1171 /* This handles the image type of selection (typically in CLIPBOARD). */
   1172 /* We convert to a TIFF, so that other applications can paste more easily. */
   1173 - (void) handle_image: (struct propdata *)pdata pasteboard:(NSPasteboard *)pb
   1174 {
   1175     NSArray *pbtypes;
   1176     NSUInteger length;
   1177     NSData *data, *tiff;
   1178     NSBitmapImageRep *bmimage;
   1179 
   1180     TRACE();
   1181 
   1182     length = pdata->length;
   1183     data = [[NSData alloc] initWithBytes:pdata->data length:length];
   1184 
   1185     if (nil == data) {
   1186         DebugF("unable to create NSData object!\n");
   1187         return;
   1188     }
   1189 
   1190     DebugF("data retainCount before NSBitmapImageRep initWithData: %lu\n",
   1191            [data retainCount]);
   1192 
   1193     bmimage = [[NSBitmapImageRep alloc] initWithData:data];
   1194 
   1195     if (nil == bmimage) {
   1196         [data autorelease];
   1197         DebugF("unable to create NSBitmapImageRep!\n");
   1198         return;
   1199     }
   1200 
   1201     DebugF("data retainCount after NSBitmapImageRep initWithData: %lu\n",
   1202            [data retainCount]);
   1203 
   1204     @try
   1205     {
   1206         tiff = [bmimage TIFFRepresentation];
   1207     }
   1208 
   1209     @catch (NSException *e)
   1210     {
   1211         DebugF("NSTIFFException!\n");
   1212         [data autorelease];
   1213         [bmimage autorelease];
   1214         return;
   1215     }
   1216 
   1217     DebugF("bmimage retainCount after TIFFRepresentation %lu\n",
   1218            [bmimage retainCount]);
   1219 
   1220     pbtypes = [NSArray arrayWithObjects:NSTIFFPboardType, nil];
   1221 
   1222     if (nil == pbtypes) {
   1223         [data autorelease];
   1224         [bmimage autorelease];
   1225         return;
   1226     }
   1227 
   1228     [pb declareTypes:pbtypes owner:nil];
   1229     if (YES != [pb setData:tiff forType:NSTIFFPboardType]) {
   1230         DebugF("writing pasteboard data failed!\n");
   1231     }
   1232 
   1233     [data autorelease];
   1234 
   1235     DebugF("bmimage retainCount before release %lu\n", [bmimage retainCount]);
   1236 
   1237     [bmimage autorelease];
   1238 }
   1239 
   1240 /* This handles the UTF8_STRING type of selection. */
   1241 - (void) handle_utf8_string:(struct propdata *)pdata pasteboard:(NSPasteboard
   1242                                                                  *)pb
   1243 {
   1244     NSString *string;
   1245     NSArray *pbtypes;
   1246 
   1247     TRACE();
   1248 
   1249     string =
   1250         [[NSString alloc] initWithBytes:pdata->data length:pdata->length
   1251          encoding:
   1252          NSUTF8StringEncoding];
   1253 
   1254     if (nil == string)
   1255         return;
   1256 
   1257     pbtypes = [NSArray arrayWithObjects:NSStringPboardType, nil];
   1258 
   1259     if (nil == pbtypes) {
   1260         [string autorelease];
   1261         return;
   1262     }
   1263 
   1264     [pb declareTypes:pbtypes owner:nil];
   1265 
   1266     if (YES != [pb setString:string forType:NSStringPboardType]) {
   1267         ErrorF("pasteboard setString:forType: failed!\n");
   1268     }
   1269     [string autorelease];
   1270     DebugF("done handling utf8 string\n");
   1271 }
   1272 
   1273 /* This handles the STRING type, which should be in Latin-1. */
   1274 - (void) handle_string: (struct propdata *)pdata pasteboard:(NSPasteboard *)
   1275    pb
   1276 {
   1277     NSString *string;
   1278     NSArray *pbtypes;
   1279 
   1280     TRACE();
   1281 
   1282     string =
   1283         [[NSString alloc] initWithBytes:pdata->data length:pdata->length
   1284          encoding:
   1285          NSISOLatin1StringEncoding];
   1286 
   1287     if (nil == string)
   1288         return;
   1289 
   1290     pbtypes = [NSArray arrayWithObjects:NSStringPboardType, nil];
   1291 
   1292     if (nil == pbtypes) {
   1293         [string autorelease];
   1294         return;
   1295     }
   1296 
   1297     [pb declareTypes:pbtypes owner:nil];
   1298     if (YES != [pb setString:string forType:NSStringPboardType]) {
   1299         ErrorF("pasteboard setString:forType failed in handle_string!\n");
   1300     }
   1301     [string autorelease];
   1302 }
   1303 
   1304 /* This is called when the selection is completely retrieved from another client. */
   1305 /* Warning: this frees the propdata. */
   1306 - (void) handle_selection:(Atom)selection type:(Atom)type propdata:(struct
   1307                                                                     propdata
   1308                                                                     *)pdata
   1309 {
   1310     NSPasteboard *pb;
   1311 
   1312     TRACE();
   1313 
   1314     pb = [NSPasteboard generalPasteboard];
   1315 
   1316     if (nil == pb) {
   1317         [self copy_completed:selection];
   1318         free_propdata(pdata);
   1319         return;
   1320     }
   1321 
   1322     /*
   1323      * Some apps it seems set the type to TARGETS instead of ATOM, such as Eterm.
   1324      * These aren't ICCCM compliant apps, but we need these to work...
   1325      */
   1326     if (request_atom == atoms->targets
   1327         && (type == atoms->atom || type == atoms->targets)) {
   1328         [self handle_targets:selection propdata:pdata];
   1329         free_propdata(pdata);
   1330         return;
   1331     }
   1332     else if (type == atoms->image_png) {
   1333         [self handle_image:pdata pasteboard:pb];
   1334     }
   1335     else if (type == atoms->image_jpeg) {
   1336         [self handle_image:pdata pasteboard:pb];
   1337     }
   1338     else if (type == atoms->utf8_string) {
   1339         [self handle_utf8_string:pdata pasteboard:pb];
   1340     }
   1341     else if (type == atoms->string) {
   1342         [self handle_string:pdata pasteboard:pb];
   1343     }
   1344 
   1345     free_propdata(pdata);
   1346 
   1347     [self copy_completed:selection];
   1348 }
   1349 
   1350 - (void) copy_completed:(Atom)selection
   1351 {
   1352     TRACE();
   1353     char *name;
   1354 
   1355     (void)name; /* Avoid warning with non-debug compiles. */
   1356 #ifdef DEBUG
   1357     name = XGetAtomName(xpbproxy_dpy, selection);
   1358     if (name) {
   1359         DebugF("copy_completed: %s\n", name);
   1360         XFree(name);
   1361     }
   1362 #endif
   1363 
   1364     if (selection == atoms->primary && pending_copy > 0) {
   1365         --pending_copy;
   1366         if (pending_copy > 0) {
   1367             /* Copy PRIMARY again. */
   1368             [self x_copy_request_targets];
   1369             return;
   1370         }
   1371     }
   1372     else if (selection == atoms->clipboard && pending_clipboard > 0) {
   1373         --pending_clipboard;
   1374         if (pending_clipboard > 0) {
   1375             /* Copy CLIPBOARD. */
   1376             [self claim_clipboard];
   1377             return;
   1378         }
   1379         else {
   1380             /* We got the final data.  Now set pbproxy as the owner. */
   1381             [self own_clipboard];
   1382             return;
   1383         }
   1384     }
   1385 
   1386     /*
   1387      * We had 1 or more primary in progress, and the clipboard arrived
   1388      * while we were busy.
   1389      */
   1390     if (pending_clipboard > 0) {
   1391         [self claim_clipboard];
   1392     }
   1393 }
   1394 
   1395 - (void) reload_preferences
   1396 {
   1397     /*
   1398      * It's uncertain how we could handle the synchronization failing, so cast to void.
   1399      * The prefs_get_bool should fall back to defaults if the org.x.X11 plist doesn't exist or is invalid.
   1400      */
   1401     (void)CFPreferencesAppSynchronize(app_prefs_domain_cfstr);
   1402 #ifdef STANDALONE_XPBPROXY
   1403     if (xpbproxy_is_standalone)
   1404         pbproxy_prefs.active = YES;
   1405     else
   1406 #endif
   1407     pbproxy_prefs.active = prefs_get_bool(CFSTR(
   1408                                               "sync_pasteboard"),
   1409                                           pbproxy_prefs.active);
   1410     pbproxy_prefs.primary_on_grab =
   1411         prefs_get_bool(CFSTR(
   1412                            "sync_primary_on_select"),
   1413                        pbproxy_prefs.primary_on_grab);
   1414     pbproxy_prefs.clipboard_to_pasteboard =
   1415         prefs_get_bool(CFSTR(
   1416                            "sync_clipboard_to_pasteboard"),
   1417                        pbproxy_prefs.clipboard_to_pasteboard);
   1418     pbproxy_prefs.pasteboard_to_primary =
   1419         prefs_get_bool(CFSTR(
   1420                            "sync_pasteboard_to_primary"),
   1421                        pbproxy_prefs.pasteboard_to_primary);
   1422     pbproxy_prefs.pasteboard_to_clipboard =
   1423         prefs_get_bool(CFSTR(
   1424                            "sync_pasteboard_to_clipboard"),
   1425                        pbproxy_prefs.pasteboard_to_clipboard);
   1426 
   1427     /* This is used for debugging. */
   1428     //dump_prefs();
   1429 
   1430     if (pbproxy_prefs.active && pbproxy_prefs.primary_on_grab &&
   1431         !xpbproxy_have_xfixes) {
   1432         ErrorF(
   1433             "Disabling sync_primary_on_select functionality due to missing XFixes extension.\n");
   1434         pbproxy_prefs.primary_on_grab = NO;
   1435     }
   1436 
   1437     /* Claim or release the CLIPBOARD_MANAGER atom */
   1438     if (![self set_clipboard_manager_status:(pbproxy_prefs.active &&
   1439                                              pbproxy_prefs.
   1440                                              clipboard_to_pasteboard)])
   1441         pbproxy_prefs.clipboard_to_pasteboard = NO;
   1442 
   1443     if (pbproxy_prefs.active && pbproxy_prefs.clipboard_to_pasteboard)
   1444         [self claim_clipboard];
   1445 }
   1446 
   1447 - (BOOL) is_active
   1448 {
   1449     return pbproxy_prefs.active;
   1450 }
   1451 
   1452 /* NSPasteboard-required methods */
   1453 
   1454 - (void) paste:(id)sender
   1455 {
   1456     TRACE();
   1457 }
   1458 
   1459 - (void) pasteboard:(NSPasteboard *)pb provideDataForType:(NSString *)type
   1460 {
   1461     TRACE();
   1462 }
   1463 
   1464 - (void) pasteboardChangedOwner:(NSPasteboard *)pb
   1465 {
   1466     TRACE();
   1467 
   1468     /* Right now we don't care with this. */
   1469 }
   1470 
   1471 /* Allocation */
   1472 
   1473 - (id) init
   1474 {
   1475     unsigned long pixel;
   1476 
   1477     self = [super init];
   1478     if (self == nil)
   1479         return nil;
   1480 
   1481     atoms->primary = XInternAtom(xpbproxy_dpy, "PRIMARY", False);
   1482     atoms->clipboard = XInternAtom(xpbproxy_dpy, "CLIPBOARD", False);
   1483     atoms->text = XInternAtom(xpbproxy_dpy, "TEXT", False);
   1484     atoms->utf8_string = XInternAtom(xpbproxy_dpy, "UTF8_STRING", False);
   1485     atoms->string = XInternAtom(xpbproxy_dpy, "STRING", False);
   1486     atoms->targets = XInternAtom(xpbproxy_dpy, "TARGETS", False);
   1487     atoms->multiple = XInternAtom(xpbproxy_dpy, "MULTIPLE", False);
   1488     atoms->cstring = XInternAtom(xpbproxy_dpy, "CSTRING", False);
   1489     atoms->image_png = XInternAtom(xpbproxy_dpy, "image/png", False);
   1490     atoms->image_jpeg = XInternAtom(xpbproxy_dpy, "image/jpeg", False);
   1491     atoms->incr = XInternAtom(xpbproxy_dpy, "INCR", False);
   1492     atoms->atom = XInternAtom(xpbproxy_dpy, "ATOM", False);
   1493     atoms->clipboard_manager = XInternAtom(xpbproxy_dpy, "CLIPBOARD_MANAGER",
   1494                                            False);
   1495     atoms->compound_text = XInternAtom(xpbproxy_dpy, "COMPOUND_TEXT", False);
   1496     atoms->atom_pair = XInternAtom(xpbproxy_dpy, "ATOM_PAIR", False);
   1497 
   1498     pixel = BlackPixel(xpbproxy_dpy, DefaultScreen(xpbproxy_dpy));
   1499     _selection_window =
   1500         XCreateSimpleWindow(xpbproxy_dpy, DefaultRootWindow(xpbproxy_dpy),
   1501                             0, 0, 1, 1, 0, pixel, pixel);
   1502 
   1503     /* This is used to get PropertyNotify events when doing INCR transfers. */
   1504     XSelectInput(xpbproxy_dpy, _selection_window, PropertyChangeMask);
   1505 
   1506     request_atom = None;
   1507 
   1508     init_propdata(&pending.propdata);
   1509     pending.requestor = None;
   1510     pending.selection = None;
   1511 
   1512     pending_copy = 0;
   1513     pending_clipboard = 0;
   1514 
   1515     if (xpbproxy_have_xfixes)
   1516         XFixesSelectSelectionInput(xpbproxy_dpy, _selection_window,
   1517                                    atoms->primary,
   1518                                    XFixesSetSelectionOwnerNotifyMask);
   1519 
   1520     [self reload_preferences];
   1521 
   1522     return self;
   1523 }
   1524 
   1525 - (void) dealloc
   1526 {
   1527     if (None != _selection_window) {
   1528         XDestroyWindow(xpbproxy_dpy, _selection_window);
   1529         _selection_window = None;
   1530     }
   1531 
   1532     free_propdata(&pending.propdata);
   1533 
   1534     [super dealloc];
   1535 }
   1536 
   1537 @end