clipboard_x11.cpp (23400B)
1 /** 2 * \file clipboard_x11.c 3 * \brief X11 implementation of the clipboard. 4 * 5 * \copyright Copyright (C) 2016 Jeremy Tan. 6 * This file is released under the MIT license. 7 * 8 * The MIT License (MIT) 9 * 10 * Copyright (c) 2016 Jeremy Tan 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining a copy 13 * of this software and associated documentation files (the "Software"), to deal 14 * in the Software without restriction, including without limitation the rights 15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 * copies of the Software, and to permit persons to whom the Software is 17 * furnished to do so, subject to the following conditions: 18 * 19 * The above copyright notice and this permission notice shall be included in all 20 * copies or substantial portions of the Software. 21 * 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 * SOFTWARE. 29 */ 30 31 #define _POSIX_C_SOURCE 199309L 32 33 #include "libclipboard.h" 34 35 #ifdef LIBCLIPBOARD_BUILD_X11 36 37 #include <assert.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <stdint.h> 41 #include <stdio.h> 42 #include <sys/time.h> 43 44 #include <xcb/xcb.h> 45 #include <pthread.h> 46 47 /* X11 headers are borked */ 48 #if defined(__MINGW32__) && defined(gettimeofday) 49 # undef gettimeofday 50 #endif 51 52 #define VALID_MODE(x) ((x) >= LCB_CLIPBOARD && (x) < LCB_MODE_END) 53 54 /** 55 * Enumeration of standard X11 atom identifiers 56 */ 57 typedef enum std_x_atoms { 58 /** The TARGETS atom identifier **/ 59 X_ATOM_TARGETS = 0, 60 /** The MULTIPLE atom identifier **/ 61 X_ATOM_MULTIPLE, 62 /** The TIMESTAMP atom identifier **/ 63 X_ATOM_TIMESTAMP, 64 /** The INCR atom identifier **/ 65 X_ATOM_INCR, 66 /** The CLIPBOARD atom identifier **/ 67 X_ATOM_CLIPBOARD, 68 /** The UTF8_STRING atom identifier **/ 69 X_ATOM_UTF8_STRING, 70 /** End marker sentinel **/ 71 X_ATOM_END 72 } std_x_atoms; 73 74 /** 75 * Union to simplify getting interned atoms from XCB 76 */ 77 typedef union atom_c { 78 /** The atom **/ 79 xcb_atom_t atom; 80 /** The cookie returned by xcb_intern_atom **/ 81 xcb_intern_atom_cookie_t cookie; 82 } atom_c; 83 84 /** 85 * Contains selection data 86 */ 87 typedef struct selection_c { 88 /** Determines if we currently own the given selection **/ 89 bool has_ownership; 90 /** The pointer to the data of the selection **/ 91 unsigned char *data; 92 /** The length (in bytes) of the selection data **/ 93 size_t length; 94 /** The type of data held in this selection **/ 95 xcb_atom_t target; 96 /** The X11 atom for the selection mode e.g. XA_PRIMARY **/ 97 xcb_atom_t xmode; 98 } selection_c; 99 100 /** X11 Implementation of the clipboard context **/ 101 struct clipboard_c { 102 /** XCB Display connection **/ 103 xcb_connection_t *xc; 104 /** XCB Default screen **/ 105 xcb_screen_t *xs; 106 /** Standard atoms **/ 107 atom_c std_atoms[X_ATOM_END]; 108 /** Our window to use for messages **/ 109 xcb_window_t xw; 110 /** Action timeout (ms) **/ 111 int action_timeout; 112 /** Transfer size (bytes) **/ 113 uint32_t transfer_size; 114 115 /** Event loop thread **/ 116 pthread_t event_loop; 117 /** Indicates true iff event_loop is initted **/ 118 bool event_loop_initted; 119 /** Mutex for access to context data **/ 120 pthread_mutex_t mu; 121 /** Indicates true iff mu is initted **/ 122 bool mu_initted; 123 /** Condition variable to notify when action is complete **/ 124 pthread_cond_t cond; 125 /** Indicates true iff cond is initted **/ 126 bool cond_initted; 127 128 /** Selection data **/ 129 selection_c selections[LCB_MODE_END]; 130 131 /** malloc **/ 132 clipboard_malloc_fn malloc; 133 /** calloc **/ 134 clipboard_calloc_fn calloc; 135 /** realloc **/ 136 clipboard_realloc_fn realloc; 137 /** free **/ 138 clipboard_free_fn free; 139 }; 140 141 /** 142 * The standard atom names. These values should match the 143 * std_x_atoms enumeration. 144 */ 145 const char *g_std_atom_names[X_ATOM_END] = { 146 "TARGETS", "MULTIPLE", "TIMESTAMP", "INCR", 147 "CLIPBOARD", "UTF8_STRING", 148 }; 149 150 /** 151 * \brief Interns the list of atoms 152 * 153 * \param [in] xc The XCB connection. 154 * \param [out] atoms The location to store interned atoms. 155 * \param [in] atom_names The names of the atoms to intern. 156 * \param [in] number The number of atoms to intern. 157 * \return true iff all atoms were interned. 158 */ 159 static bool x11_intern_atoms(xcb_connection_t *xc, atom_c *atoms, const char **atom_names, int number) { 160 for (int i = 0; i < number; i++) { 161 atoms[i].cookie = xcb_intern_atom(xc, 0, 162 strlen(atom_names[i]), atom_names[i]); 163 } 164 165 for (int i = 0; i < number; i++) { 166 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xc, 167 atoms[i].cookie, NULL); 168 if (reply == NULL) { 169 return false; 170 } 171 172 atoms[i].atom = reply->atom; 173 free(reply); /* XCB: Do not use custom allocators */ 174 } 175 176 return true; 177 } 178 179 /** 180 * \brief Retrieves the screen from the given connection. 181 * 182 * \param [in] xc The XCB connection. 183 * \param [in] screen The screen number to retrieve. 184 * \return The screen, or NULL on error. 185 * 186 * Based on https://xcb.freedesktop.org/xlibtoxcbtranslationguide/ 187 */ 188 static xcb_screen_t *x11_get_screen(xcb_connection_t *xc, int screen) { 189 xcb_screen_iterator_t iter; 190 191 iter = xcb_setup_roots_iterator(xcb_get_setup(xc)); 192 for (; iter.rem; screen--, xcb_screen_next(&iter)) { 193 if (screen == 0) { 194 return iter.data; 195 } 196 } 197 198 return NULL; 199 } 200 201 /** 202 * \brief Clears the selection data held in our cache on SelectionClear. 203 * 204 * \param [in] cb The clipboard context. 205 * \param [in] e The selection clear event. 206 */ 207 static void x11_clear_selection(clipboard_c *cb, xcb_selection_clear_event_t *e) { 208 if (e->owner != cb->xw) { 209 return; 210 } 211 212 for (int i = 0; i < LCB_MODE_END; i++) { 213 selection_c *sel = &cb->selections[i]; 214 if (sel->xmode == e->selection && (pthread_mutex_lock(&cb->mu) == 0)) { 215 cb->free(sel->data); 216 sel->data = NULL; 217 sel->length = 0; 218 sel->has_ownership = false; 219 sel->target = XCB_NONE; 220 pthread_mutex_unlock(&cb->mu); 221 break; 222 } 223 } 224 } 225 226 /** 227 * \brief Retrieves the selection into our cache on SelectionNotify 228 * 229 * \param [in] cb The clipboard context. 230 * \param [in] e The selection notify event. 231 * 232 * The INCR format is not supported. 233 */ 234 static void x11_retrieve_selection(clipboard_c *cb, xcb_selection_notify_event_t *e) { 235 unsigned char *buf = NULL; 236 size_t bufsiz = 0, bytes_after = 1; 237 xcb_get_property_reply_t *reply = NULL; 238 xcb_atom_t actual_type; 239 uint8_t actual_format; 240 241 if (e->property != XCB_ATOM_PRIMARY && e->property != XCB_ATOM_SECONDARY && e->property != cb->std_atoms[X_ATOM_CLIPBOARD].atom) { 242 fprintf(stderr, "x11_retrieve_selection: [Warn] Unknown selection property returned: %d\n", e->property); 243 return; 244 } 245 246 while (bytes_after > 0) { 247 free(reply); /* XCB: Do not use custom allocators */ 248 xcb_get_property_cookie_t ck = xcb_get_property(cb->xc, true, cb->xw, 249 e->property, XCB_ATOM_ANY, 250 bufsiz / 4, cb->transfer_size / 4); 251 reply = xcb_get_property_reply(cb->xc, ck, NULL); 252 /* reply->format should be 8, 16 or 32. */ 253 if (reply == NULL || (bufsiz > 0 && (reply->format != actual_format || reply->type != actual_type)) || ((reply->format % 8) != 0)) { 254 fprintf(stderr, "x11_retrieve_selection: [Err] Invalid return value from xcb_get_property_reply\n"); 255 cb->free(buf); 256 buf = NULL; 257 break; 258 } 259 260 if (bufsiz == 0) { 261 actual_type = reply->type; 262 actual_format = reply->format; 263 } 264 265 /* Todo: Check for INCR */ 266 267 int nitems = xcb_get_property_value_length(reply); 268 if (nitems > 0) { 269 if ((bufsiz % 4) != 0) { 270 fprintf(stderr, "x11_retrieve_selection: [Err] Got more data but read data size is not a multiple of 4\n"); 271 cb->free(buf); 272 buf = NULL; 273 break; 274 } 275 276 size_t unit_size = (reply->format / 8); 277 buf = (unsigned char *)cb->realloc(buf, unit_size * (bufsiz + nitems)); 278 if (buf == NULL) { 279 fprintf(stderr, "x11_retrieve_selection: [Err] realloc failed\n"); 280 break; 281 } 282 283 memcpy(buf + bufsiz, xcb_get_property_value(reply), nitems * unit_size); 284 bufsiz += nitems * unit_size; 285 } 286 287 bytes_after = reply->bytes_after; 288 } 289 free(reply); /* XCB: Do not use custom allocators */ 290 291 if (buf != NULL && (pthread_mutex_lock(&cb->mu) == 0)) { 292 selection_c *sel = NULL; 293 for (int i = 0; i < LCB_MODE_END; i++) { 294 if (cb->selections[i].xmode == e->property) { 295 sel = &cb->selections[i]; 296 break; 297 } 298 } 299 300 if (sel != NULL && sel->target == actual_type) { 301 cb->free(sel->data); 302 sel->data = buf; 303 sel->length = bufsiz; 304 buf = NULL; 305 } else { 306 fprintf(stderr, "x11_retrieve_selection: [Warn] Mismatched selection: actual_type=%d\n", actual_type); 307 } 308 309 pthread_cond_broadcast(&cb->cond); 310 pthread_mutex_unlock(&cb->mu); 311 } 312 cb->free(buf); 313 } 314 315 /** 316 * \brief Sends the selection data to the requestor on SelectionRequest 317 * 318 * \param [in] cb The clipboard context. 319 * \param [in] e The selection request event. 320 * \return true iff the data was sent (requestor's property was changed) 321 * 322 * Not currently ICCCM compliant as MULTIPLE target is unsupported. Also 323 * not compliant because we're not supplying a proper TIMESTAMP value. 324 */ 325 static bool x11_transmit_selection(clipboard_c *cb, xcb_selection_request_event_t *e) { 326 /* Default location to store data if none specified */ 327 if (e->property == XCB_NONE) { 328 e->property = e->target; 329 } 330 331 if (e->target == cb->std_atoms[X_ATOM_TARGETS].atom) { 332 xcb_atom_t targets[] = { 333 cb->std_atoms[X_ATOM_TIMESTAMP].atom, 334 cb->std_atoms[X_ATOM_TARGETS].atom, 335 cb->std_atoms[X_ATOM_UTF8_STRING].atom 336 }; 337 xcb_change_property(cb->xc, XCB_PROP_MODE_REPLACE, e->requestor, 338 e->property, XCB_ATOM_ATOM, 339 sizeof(xcb_atom_t) * 8, 340 sizeof(targets) / sizeof(xcb_atom_t), targets); 341 } else if (e->target == cb->std_atoms[X_ATOM_TIMESTAMP].atom) { 342 xcb_timestamp_t cur = XCB_CURRENT_TIME; 343 xcb_change_property(cb->xc, XCB_PROP_MODE_REPLACE, e->requestor, 344 e->property, XCB_ATOM_INTEGER, sizeof(cur) * 8, 345 1, &cur); 346 } else if (e->target == cb->std_atoms[X_ATOM_UTF8_STRING].atom) { 347 selection_c *sel = NULL; 348 if (pthread_mutex_lock(&cb->mu) != 0) { 349 return false; 350 } 351 352 for (int i = 0; i < LCB_MODE_END; i++) { 353 if (cb->selections[i].xmode == e->selection) { 354 sel = &cb->selections[i]; 355 break; 356 } 357 } 358 359 if (sel == NULL || !sel->has_ownership || sel->data == NULL || sel->target != e->target) { 360 pthread_mutex_unlock(&cb->mu); 361 return false; 362 } 363 364 xcb_change_property(cb->xc, XCB_PROP_MODE_REPLACE, e->requestor, 365 e->property, e->target, 8, sel->length, sel->data); 366 pthread_mutex_unlock(&cb->mu); 367 } else { 368 /* Unknown target */ 369 return false; 370 } 371 372 return true; 373 } 374 375 /** 376 * \brief The main event loop to process window messages. 377 * 378 * \param [in] arg The clipboard context. 379 * 380 * This thread will run indefinitely until the clipboard context's window 381 * is destroyed. It *must* receive a DestroyNotify message to end. 382 */ 383 static void *x11_event_loop(void *arg) { 384 clipboard_c *cb = (clipboard_c *)arg; 385 xcb_generic_event_t *e; 386 387 while ((e = xcb_wait_for_event(cb->xc))) { 388 if (e->response_type == 0) { 389 /* I think this cast is appropriate... */ 390 xcb_generic_error_t *err = (xcb_generic_error_t *) e; 391 fprintf(stderr, "x11_event_loop: [Warn] Received X11 error: %d\n", err->error_code); 392 free(e); /* XCB: Do not use custom allocators */ 393 continue; 394 } 395 396 switch (e->response_type & ~0x80) { 397 case XCB_DESTROY_NOTIFY: { 398 xcb_destroy_notify_event_t *evt = (xcb_destroy_notify_event_t *)e; 399 if (evt->window == cb->xw) { 400 free(e); /* XCB: Do not use custom allocators */ 401 return NULL; 402 } 403 } 404 break; 405 case XCB_SELECTION_CLEAR: { 406 x11_clear_selection(cb, (xcb_selection_clear_event_t *)e); 407 } 408 break; 409 case XCB_SELECTION_NOTIFY: { 410 x11_retrieve_selection(cb, (xcb_selection_notify_event_t *)e); 411 } 412 break; 413 case XCB_SELECTION_REQUEST: { 414 xcb_selection_request_event_t *req = (xcb_selection_request_event_t *)e; 415 xcb_selection_notify_event_t notify = {0}; 416 notify.response_type = XCB_SELECTION_NOTIFY; 417 notify.time = XCB_CURRENT_TIME; 418 notify.requestor = req->requestor; 419 notify.selection = req->selection; 420 notify.target = req->target; 421 notify.property = x11_transmit_selection(cb, req) ? req->property : XCB_NONE; 422 xcb_send_event(cb->xc, false, req->requestor, XCB_EVENT_MASK_PROPERTY_CHANGE, (char *)¬ify); 423 xcb_flush(cb->xc); 424 } 425 break; 426 case XCB_PROPERTY_NOTIFY: { 427 } 428 break; 429 default: { 430 /* Ignore unknown messages */ 431 } 432 } 433 free(e); /* XCB: Do not use custom allocators */ 434 } 435 436 fprintf(stderr, "x11_event_loop: [Warn] xcb_wait_for_event returned NULL\n"); 437 return NULL; 438 } 439 440 LCB_API clipboard_c *LCB_CC clipboard_new(clipboard_opts *cb_opts) { 441 clipboard_opts defaults = { }; 442 defaults.x11.display_name = NULL; 443 defaults.x11.action_timeout = LCB_X11_ACTION_TIMEOUT_DEFAULT; 444 defaults.x11.transfer_size = LCB_X11_TRANSFER_SIZE_DEFAULT; 445 446 if (cb_opts == NULL) { 447 cb_opts = &defaults; 448 } 449 450 clipboard_calloc_fn calloc_fn = cb_opts->user_calloc_fn ? cb_opts->user_calloc_fn : calloc; 451 clipboard_c *cb = (clipboard_c *)calloc_fn(1, sizeof(clipboard_c)); 452 if (cb == NULL) { 453 return NULL; 454 } 455 LCB_SET_ALLOCATORS(cb, cb_opts); 456 457 cb->action_timeout = cb_opts->x11.action_timeout > 0 ? 458 cb_opts->x11.action_timeout : LCB_X11_ACTION_TIMEOUT_DEFAULT; 459 /* Round down to nearest multiple of 4 */ 460 cb->transfer_size = (cb_opts->x11.transfer_size / 4) * 4; 461 if (cb->transfer_size == 0) { 462 cb->transfer_size = LCB_X11_TRANSFER_SIZE_DEFAULT; 463 } 464 465 cb->mu_initted = pthread_mutex_init(&cb->mu, NULL) == 0; 466 if (!cb->mu_initted) { 467 clipboard_free(cb); 468 return NULL; 469 } 470 471 cb->cond_initted = pthread_cond_init(&cb->cond, NULL) == 0; 472 if (!cb->cond_initted) { 473 clipboard_free(cb); 474 return NULL; 475 } 476 477 int preferred_screen; 478 cb->xc = xcb_connect(cb_opts->x11.display_name, &preferred_screen); 479 assert(cb->xc != NULL); /* Docs say return is never NULL */ 480 if (xcb_connection_has_error(cb->xc) != 0) { 481 clipboard_free(cb); 482 return NULL; 483 } 484 cb->xs = x11_get_screen(cb->xc, preferred_screen); 485 assert(cb->xs != NULL); 486 487 if (!x11_intern_atoms(cb->xc, cb->std_atoms, g_std_atom_names, X_ATOM_END)) { 488 clipboard_free(cb); 489 return NULL; 490 } 491 492 cb->selections[LCB_CLIPBOARD].xmode = cb->std_atoms[X_ATOM_CLIPBOARD].atom; 493 cb->selections[LCB_PRIMARY].xmode = XCB_ATOM_PRIMARY; 494 cb->selections[LCB_SECONDARY].xmode = XCB_ATOM_SECONDARY; 495 496 /* Structure notify mask to get DestroyNotify messages */ 497 /* Property change mask for PropertyChange messages */ 498 uint32_t event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; 499 cb->xw = xcb_generate_id(cb->xc); 500 xcb_generic_error_t *err = xcb_request_check(cb->xc, 501 xcb_create_window_checked(cb->xc, 502 XCB_COPY_FROM_PARENT, cb->xw, cb->xs->root, 503 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, 504 cb->xs->root_visual, 505 XCB_CW_EVENT_MASK, &event_mask)); 506 if (err != NULL) { 507 cb->xw = 0; 508 /* Am I meant to free this? */ 509 free(err); /* XCB: Do not use custom allocators */ 510 clipboard_free(cb); 511 return NULL; 512 } 513 514 cb->event_loop_initted = pthread_create(&cb->event_loop, NULL, 515 x11_event_loop, (void *)cb) == 0; 516 if (!cb->event_loop_initted) { 517 clipboard_free(cb); 518 return NULL; 519 } 520 521 return cb; 522 } 523 524 LCB_API void LCB_CC clipboard_free(clipboard_c *cb) { 525 if (cb == NULL) { 526 return; 527 } 528 529 if (cb->event_loop_initted) { 530 /* This should send a DestroyNotify message as the termination condition */ 531 xcb_destroy_window(cb->xc, cb->xw); 532 xcb_flush(cb->xc); 533 pthread_join(cb->event_loop, NULL); 534 } else if (cb->xw != 0) { 535 xcb_destroy_window(cb->xc, cb->xw); 536 } 537 538 if (cb->xc != NULL) { 539 xcb_disconnect(cb->xc); 540 } 541 542 if (cb->cond_initted) { 543 pthread_cond_destroy(&cb->cond); 544 } 545 if (cb->mu_initted) { 546 pthread_mutex_destroy(&cb->mu); 547 } 548 549 /* Free selection data */ 550 for (int i = 0; i < LCB_MODE_END; i++) { 551 if (cb->selections[i].data != NULL) { 552 cb->free(cb->selections[i].data); 553 } 554 } 555 556 cb->free(cb); 557 } 558 559 LCB_API void LCB_CC clipboard_clear(clipboard_c *cb, clipboard_mode mode) { 560 if (cb == NULL || cb->xc == NULL) { 561 return; 562 } 563 564 xcb_atom_t sel; 565 566 switch (mode) { 567 case LCB_CLIPBOARD: 568 sel = cb->std_atoms[X_ATOM_CLIPBOARD].atom; 569 break; 570 case LCB_PRIMARY: 571 sel = XCB_ATOM_PRIMARY; 572 break; 573 case LCB_SECONDARY: 574 sel = XCB_ATOM_SECONDARY; 575 break; 576 default: 577 return; 578 } 579 580 xcb_set_selection_owner(cb->xc, XCB_NONE, sel, XCB_CURRENT_TIME); 581 xcb_flush(cb->xc); 582 } 583 584 LCB_API bool LCB_CC clipboard_has_ownership(clipboard_c *cb, clipboard_mode mode) { 585 bool ret = false; 586 587 if (!VALID_MODE(mode)) { 588 return false; 589 } 590 591 if (cb && (pthread_mutex_lock(&cb->mu) == 0)) { 592 ret = cb->selections[mode].has_ownership; 593 pthread_mutex_unlock(&cb->mu); 594 } 595 return ret; 596 } 597 598 /** 599 * \brief Copies the selection data into a newly allocated buffer 600 * 601 * \param [in] cb The clipboard context 602 * \param [in] sel The selection context 603 * \param [out] ret The return location 604 * \param [out] length The length of the returned data (optional) 605 */ 606 static void retrieve_text_selection(clipboard_c *cb, selection_c *sel, char **ret, int *length) { 607 if (sel->data != NULL && sel->target == cb->std_atoms[X_ATOM_UTF8_STRING].atom) { 608 *ret = (char *)cb->malloc(sizeof(char) * (sel->length + 1)); 609 if (*ret != NULL) { 610 memcpy(*ret, sel->data, sel->length); 611 (*ret)[sel->length] = '\0'; 612 613 if (length != NULL) { 614 *length = sel->length; 615 } 616 } 617 } 618 } 619 620 LCB_API char LCB_CC *clipboard_text_ex(clipboard_c *cb, int *length, clipboard_mode mode) { 621 char *ret = NULL; 622 623 if (cb == NULL || !VALID_MODE(mode)) { 624 return NULL; 625 } 626 627 if (pthread_mutex_lock(&cb->mu) == 0) { 628 selection_c *sel = &cb->selections[mode]; 629 if (sel->has_ownership) { 630 retrieve_text_selection(cb, sel, &ret, length); 631 } else { 632 /* Convert selection & wait for reply */ 633 struct timeval now; 634 struct timespec timeout; 635 int pret = 0; 636 637 xcb_get_selection_owner_reply_t *owner = xcb_get_selection_owner_reply(cb->xc, 638 xcb_get_selection_owner(cb->xc, sel->xmode), NULL); 639 if (owner == NULL || owner->owner == 0) { 640 /* No selection owner; no data available */ 641 pthread_mutex_unlock(&cb->mu); 642 free(owner); /* XCB: Do not use custom allocators */ 643 return NULL; 644 } 645 free(owner); /* XCB: Do not use custom allocators */ 646 647 /* Unset any old value */ 648 cb->free(sel->data); 649 sel->data = NULL; 650 sel->length = 0; 651 652 sel->target = cb->std_atoms[X_ATOM_UTF8_STRING].atom; 653 xcb_convert_selection(cb->xc, cb->xw, sel->xmode, 654 sel->target, sel->xmode, XCB_CURRENT_TIME); 655 xcb_flush(cb->xc); 656 657 /* Calculate timeout */ 658 gettimeofday(&now, NULL); 659 timeout.tv_sec = now.tv_sec + (cb->action_timeout / 1000); 660 timeout.tv_nsec = (now.tv_usec * 1000UL) + ((cb->action_timeout % 1000) * 1000000UL); 661 if (timeout.tv_nsec >= 1000000000UL) { 662 timeout.tv_sec += timeout.tv_nsec / 1000000000UL; 663 timeout.tv_nsec = timeout.tv_nsec % 1000000000UL; 664 } 665 666 while (pret == 0 && sel->data == NULL) { 667 pret = pthread_cond_timedwait(&cb->cond, &cb->mu, &timeout); 668 } 669 670 retrieve_text_selection(cb, sel, &ret, length); 671 } 672 673 pthread_mutex_unlock(&cb->mu); 674 } 675 676 return ret; 677 } 678 679 LCB_API bool LCB_CC clipboard_set_text_ex(clipboard_c *cb, const char *src, int length, clipboard_mode mode) { 680 bool ret = false; 681 682 if (cb == NULL || src == NULL || length == 0 || !VALID_MODE(mode)) { 683 return false; 684 } 685 686 if (pthread_mutex_lock(&cb->mu) == 0) { 687 selection_c *sel = &cb->selections[mode]; 688 if (sel->data != NULL) { 689 cb->free(sel->data); 690 } 691 if (length < 0) { 692 length = strlen(src); 693 } 694 695 sel->data = (unsigned char *)cb->malloc(sizeof(char) * (length + 1)); 696 if (sel->data != NULL) { 697 memcpy(sel->data, src, length); 698 sel->data[length] = '\0'; 699 sel->length = length; 700 sel->has_ownership = true; 701 sel->target = cb->std_atoms[X_ATOM_UTF8_STRING].atom; 702 xcb_set_selection_owner(cb->xc, cb->xw, sel->xmode, XCB_CURRENT_TIME); 703 xcb_flush(cb->xc); 704 ret = true; 705 } 706 707 pthread_mutex_unlock(&cb->mu); 708 } 709 710 return ret; 711 } 712 713 #endif /* LIBCLIPBOARD_BUILD_X11 */