dsimple.c revision c175af84
1/* 2 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23/* 24 25Copyright 1993, 1998 The Open Group 26 27Permission to use, copy, modify, distribute, and sell this software and its 28documentation for any purpose is hereby granted without fee, provided that 29the above copyright notice appear in all copies and that both that 30copyright notice and this permission notice appear in supporting 31documentation. 32 33The above copyright notice and this permission notice shall be included 34in all copies or substantial portions of the Software. 35 36THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 37OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 38MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 39IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR 40OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 41ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 42OTHER DEALINGS IN THE SOFTWARE. 43 44Except as contained in this notice, the name of The Open Group shall 45not be used in advertising or otherwise to promote the sale, use or 46other dealings in this Software without prior written authorization 47from The Open Group. 48 49*/ 50 51#include "config.h" 52 53#include <xcb/xcb.h> 54#include <xcb/xproto.h> 55#ifdef USE_XCB_ICCCM 56# include <xcb/xcb_icccm.h> 57#endif 58#include <X11/cursorfont.h> 59#include <stdio.h> 60#include <stdlib.h> 61#include <stdarg.h> 62#include <string.h> 63#include "clientwin.h" 64#include "dsimple.h" 65 66/* 67 * Just_display: A group of routines designed to make the writing of simple 68 * X11 applications which open a display but do not open 69 * any windows much faster and easier. Unless a routine says 70 * otherwise, it may be assumed to require program_name 71 * to be already defined on entry. 72 * 73 * Written by Mark Lillibridge. Last updated 7/1/87 74 */ 75 76 77/* This stuff is defined in the calling program by dsimple.h */ 78const char *program_name = "unknown_program"; 79 80/* 81 * Get_Display_Name (argc, argv) - return string representing display name 82 * that would be used given the specified argument (i.e. if it's NULL, check 83 * getenv("DISPLAY") - always returns a non-NULL pointer, though it may be 84 * an unwritable constant, so is safe to printf() on platforms that crash 85 * on NULL printf arguments. 86 */ 87const char *Get_Display_Name (const char *display_name) 88{ 89 const char *name = display_name; 90 91 if (!name) { 92 name = getenv ("DISPLAY"); 93 if (!name) 94 name = ""; 95 } 96 return (name); 97} 98 99 100/* 101 * Setup_Display_And_Screen: This routine opens up the correct display (i.e., 102 * it calls Get_Display_Name) and then stores a 103 * pointer to it in dpy. The default screen 104 * for this display is then stored in screen. 105 */ 106void Setup_Display_And_Screen ( 107 const char *display_name, 108 xcb_connection_t **dpy, /* MODIFIED */ 109 xcb_screen_t **screen) /* MODIFIED */ 110{ 111 int screen_number, i, err; 112 113 /* Open Display */ 114 *dpy = xcb_connect (display_name, &screen_number); 115 if ((err = xcb_connection_has_error (*dpy)) != 0) { 116 switch (err) { 117 case XCB_CONN_CLOSED_MEM_INSUFFICIENT: 118 Fatal_Error ("Failed to allocate memory in xcb_connect"); 119 case XCB_CONN_CLOSED_PARSE_ERR: 120 Fatal_Error ("unable to parse display name \"%s\"", 121 Get_Display_Name(display_name) ); 122#ifdef XCB_CONN_CLOSED_INVALID_SCREEN 123 case XCB_CONN_CLOSED_INVALID_SCREEN: 124 Fatal_Error ("invalid screen %d in display \"%s\"", 125 screen_number, Get_Display_Name(display_name)); 126#endif 127 default: 128 Fatal_Error ("unable to open display \"%s\"", 129 Get_Display_Name(display_name) ); 130 } 131 } 132 133 if (screen) { 134 /* find our screen */ 135 const xcb_setup_t *setup = xcb_get_setup(*dpy); 136 xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup); 137 int screen_count = xcb_setup_roots_length(setup); 138 if (screen_count <= screen_number) 139 { 140 Fatal_Error ("unable to access screen %d, max is %d", 141 screen_number, screen_count-1 ); 142 } 143 144 for (i = 0; i < screen_number; i++) 145 xcb_screen_next(&screen_iter); 146 *screen = screen_iter.data; 147 } 148} 149 150/* 151 * xcb equivalent of XCreateFontCursor 152 */ 153static xcb_cursor_t 154Create_Font_Cursor (xcb_connection_t *dpy, uint16_t glyph) 155{ 156 static xcb_font_t cursor_font; 157 xcb_cursor_t cursor; 158 159 if (!cursor_font) { 160 cursor_font = xcb_generate_id (dpy); 161 xcb_open_font (dpy, cursor_font, strlen ("cursor"), "cursor"); 162 } 163 164 cursor = xcb_generate_id (dpy); 165 xcb_create_glyph_cursor (dpy, cursor, cursor_font, cursor_font, 166 glyph, glyph + 1, 167 0, 0, 0, 0xffff, 0xffff, 0xffff); /* rgb, rgb */ 168 169 return cursor; 170} 171 172/* 173 * Routine to let user select a window using the mouse 174 */ 175 176xcb_window_t Select_Window(xcb_connection_t *dpy, 177 const xcb_screen_t *screen, 178 int descend) 179{ 180 xcb_cursor_t cursor; 181 xcb_generic_event_t *event; 182 xcb_window_t target_win = XCB_WINDOW_NONE; 183 xcb_window_t root = screen->root; 184 int buttons = 0; 185 xcb_generic_error_t *err; 186 xcb_grab_pointer_cookie_t grab_cookie; 187 xcb_grab_pointer_reply_t *grab_reply; 188 189 /* Make the target cursor */ 190 cursor = Create_Font_Cursor (dpy, XC_crosshair); 191 192 /* Grab the pointer using target cursor, letting it room all over */ 193 grab_cookie = xcb_grab_pointer 194 (dpy, False, root, 195 XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, 196 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, 197 root, cursor, XCB_TIME_CURRENT_TIME); 198 grab_reply = xcb_grab_pointer_reply (dpy, grab_cookie, &err); 199 if (grab_reply->status != XCB_GRAB_STATUS_SUCCESS) 200 Fatal_Error ("Can't grab the mouse."); 201 202 /* Let the user select a window... */ 203 while ((target_win == XCB_WINDOW_NONE) || (buttons != 0)) { 204 /* allow one more event */ 205 xcb_allow_events (dpy, XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); 206 xcb_flush (dpy); 207 event = xcb_wait_for_event (dpy); 208 if (event == NULL) 209 Fatal_Error ("Fatal IO error"); 210 switch (event->response_type & 0x7f) { 211 case XCB_BUTTON_PRESS: 212 { 213 xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event; 214 215 if (target_win == XCB_WINDOW_NONE) { 216 target_win = bp->child; /* window selected */ 217 if (target_win == XCB_WINDOW_NONE) 218 target_win = root; 219 } 220 buttons++; 221 break; 222 } 223 case XCB_BUTTON_RELEASE: 224 if (buttons > 0) /* there may have been some down before we started */ 225 buttons--; 226 break; 227 default: 228 /* just discard all other events */ 229 break; 230 } 231 free (event); 232 } 233 234 xcb_ungrab_pointer (dpy, XCB_TIME_CURRENT_TIME); /* Done with pointer */ 235 236 if (!descend || (target_win == root)) 237 return (target_win); 238 239 target_win = Find_Client (dpy, root, target_win); 240 241 return (target_win); 242} 243 244 245/* 246 * Window_With_Name: routine to locate a window with a given name on a display. 247 * If no window with the given name is found, 0 is returned. 248 * If more than one window has the given name, the first 249 * one found will be returned. Only top and its subwindows 250 * are looked at. Normally, top should be the RootWindow. 251 */ 252 253struct wininfo_cookies { 254 xcb_get_property_cookie_t get_net_wm_name; 255 xcb_get_property_cookie_t get_wm_name; 256 xcb_query_tree_cookie_t query_tree; 257}; 258 259#ifndef USE_XCB_ICCCM 260# define xcb_icccm_get_wm_name(Dpy, Win) \ 261 xcb_get_property (Dpy, False, Win, XCB_ATOM_WM_NAME, \ 262 XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ) 263#endif 264 265static xcb_atom_t atom_net_wm_name, atom_utf8_string; 266 267# define xcb_get_net_wm_name(Dpy, Win) \ 268 xcb_get_property (Dpy, False, Win, atom_net_wm_name, \ 269 atom_utf8_string, 0, BUFSIZ) 270 271 272static xcb_window_t 273recursive_Window_With_Name ( 274 xcb_connection_t *dpy, 275 xcb_window_t window, 276 struct wininfo_cookies *cookies, 277 const char *name, 278 size_t namelen) 279{ 280 xcb_window_t *children; 281 unsigned int nchildren; 282 unsigned int i; 283 xcb_window_t w = 0; 284 xcb_generic_error_t *err; 285 xcb_query_tree_reply_t *tree; 286 struct wininfo_cookies *child_cookies; 287 xcb_get_property_reply_t *prop; 288 289 if (cookies->get_net_wm_name.sequence) { 290 prop = xcb_get_property_reply (dpy, cookies->get_net_wm_name, &err); 291 292 if (prop) { 293 if (prop->type == atom_utf8_string) { 294 const char *prop_name = xcb_get_property_value (prop); 295 int prop_name_len = xcb_get_property_value_length (prop); 296 297 /* can't use strcmp, since prop.name is not null terminated */ 298 if ((namelen == (size_t) prop_name_len) && 299 memcmp (prop_name, name, namelen) == 0) { 300 w = window; 301 } 302 } 303 free (prop); 304 } else if (err) { 305 if (err->response_type == 0) 306 Print_X_Error (dpy, err); 307 return 0; 308 } 309 } 310 311 if (w) { 312 xcb_discard_reply (dpy, cookies->get_wm_name.sequence); 313 } else { 314#ifdef USE_XCB_ICCCM 315 xcb_icccm_get_text_property_reply_t nameprop; 316 317 if (xcb_icccm_get_wm_name_reply (dpy, cookies->get_wm_name, 318 &nameprop, &err)) { 319 /* can't use strcmp, since nameprop.name is not null terminated */ 320 if ((namelen == (size_t) nameprop.name_len) && 321 memcmp (nameprop.name, name, namelen) == 0) { 322 w = window; 323 } 324 325 xcb_icccm_get_text_property_reply_wipe (&nameprop); 326 } 327#else 328 prop = xcb_get_property_reply (dpy, cookies->get_wm_name, &err); 329 330 if (prop) { 331 if (prop->type == XCB_ATOM_STRING) { 332 const char *prop_name = xcb_get_property_value (prop); 333 int prop_name_len = xcb_get_property_value_length (prop); 334 335 /* can't use strcmp, since prop.name is not null terminated */ 336 if ((namelen == (size_t) prop_name_len) && 337 memcmp (prop_name, name, namelen) == 0) { 338 w = window; 339 } 340 } 341 free (prop); 342 } 343#endif 344 else if (err) { 345 if (err->response_type == 0) 346 Print_X_Error (dpy, err); 347 return 0; 348 } 349 } 350 351 if (w) 352 { 353 xcb_discard_reply (dpy, cookies->query_tree.sequence); 354 return w; 355 } 356 357 tree = xcb_query_tree_reply (dpy, cookies->query_tree, &err); 358 if (!tree) { 359 if (err->response_type == 0) 360 Print_X_Error (dpy, err); 361 return 0; 362 } 363 364 nchildren = xcb_query_tree_children_length (tree); 365 children = xcb_query_tree_children (tree); 366 child_cookies = calloc(nchildren, sizeof(struct wininfo_cookies)); 367 368 if (child_cookies == NULL) 369 Fatal_Error("Failed to allocate memory in recursive_Window_With_Name"); 370 371 for (i = 0; i < nchildren; i++) { 372 if (atom_net_wm_name && atom_utf8_string) 373 child_cookies[i].get_net_wm_name = 374 xcb_get_net_wm_name (dpy, children[i]); 375 child_cookies[i].get_wm_name = xcb_icccm_get_wm_name (dpy, children[i]); 376 child_cookies[i].query_tree = xcb_query_tree (dpy, children[i]); 377 } 378 xcb_flush (dpy); 379 380 for (i = 0; i < nchildren; i++) { 381 w = recursive_Window_With_Name (dpy, children[i], 382 &child_cookies[i], name, namelen); 383 if (w) 384 break; 385 } 386 387 if (w) 388 { 389 /* clean up remaining replies */ 390 for (/* keep previous i */; i < nchildren; i++) { 391 if (child_cookies[i].get_net_wm_name.sequence) 392 xcb_discard_reply (dpy, 393 child_cookies[i].get_net_wm_name.sequence); 394 xcb_discard_reply (dpy, child_cookies[i].get_wm_name.sequence); 395 xcb_discard_reply (dpy, child_cookies[i].query_tree.sequence); 396 } 397 } 398 399 free (child_cookies); 400 free (tree); /* includes storage for children[] */ 401 return (w); 402} 403 404xcb_window_t 405Window_With_Name ( 406 xcb_connection_t *dpy, 407 xcb_window_t top, 408 const char *name) 409{ 410 struct wininfo_cookies cookies; 411 412 atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME"); 413 atom_utf8_string = Get_Atom (dpy, "UTF8_STRING"); 414 415 if (atom_net_wm_name && atom_utf8_string) 416 cookies.get_net_wm_name = xcb_get_net_wm_name (dpy, top); 417 cookies.get_wm_name = xcb_icccm_get_wm_name (dpy, top); 418 cookies.query_tree = xcb_query_tree (dpy, top); 419 xcb_flush (dpy); 420 return recursive_Window_With_Name(dpy, top, &cookies, name, strlen(name)); 421} 422 423 424/* 425 * Standard fatal error routine - call like printf 426 */ 427void Fatal_Error (const char *msg, ...) 428{ 429 va_list args; 430 fflush (stdout); 431 fflush (stderr); 432 fprintf (stderr, "%s: error: ", program_name); 433 va_start (args, msg); 434 vfprintf (stderr, msg, args); 435 va_end (args); 436 fprintf (stderr, "\n"); 437 exit (EXIT_FAILURE); 438} 439 440/* 441 * Print X error information like the default Xlib error handler 442 */ 443void 444Print_X_Error ( 445 xcb_connection_t *dpy, 446 xcb_generic_error_t *err 447 ) 448{ 449 char buffer[256] = ""; 450 451 if ((err == NULL) || (err->response_type != 0)) /* not an error */ 452 return; 453 454 /* Todo: find a more user friendly way to show request/extension info */ 455 if (err->error_code >= 128) 456 { 457 fprintf (stderr, "X Extension Error: Error code %d\n", 458 err->error_code); 459 } 460 else 461 { 462 switch (err->error_code) 463 { 464 case XCB_REQUEST: 465 snprintf (buffer, sizeof(buffer), "Bad Request"); 466 break; 467 468 case XCB_VALUE: 469 snprintf (buffer, sizeof(buffer), 470 "Bad Value: 0x%x", err->resource_id); 471 break; 472 473 case XCB_WINDOW: 474 snprintf (buffer, sizeof(buffer), 475 "Bad Window: 0x%x", err->resource_id); 476 break; 477 478 case XCB_PIXMAP: 479 snprintf (buffer, sizeof(buffer), 480 "Bad Pixmap: 0x%x", err->resource_id); 481 break; 482 483 case XCB_ATOM: 484 snprintf (buffer, sizeof(buffer), 485 "Bad Atom: 0x%x", err->resource_id); 486 break; 487 488 case XCB_CURSOR: 489 snprintf (buffer, sizeof(buffer), 490 "Bad Cursor: 0x%x", err->resource_id); 491 break; 492 493 case XCB_FONT: 494 snprintf (buffer, sizeof(buffer), 495 "Bad Font: 0x%x", err->resource_id); 496 break; 497 498 case XCB_MATCH: 499 snprintf (buffer, sizeof(buffer), "Bad Match"); 500 break; 501 502 case XCB_DRAWABLE: 503 snprintf (buffer, sizeof(buffer), 504 "Bad Drawable: 0x%x", err->resource_id); 505 break; 506 507 case XCB_ACCESS: 508 snprintf (buffer, sizeof(buffer), "Access Denied"); 509 break; 510 511 case XCB_ALLOC: 512 snprintf (buffer, sizeof(buffer), 513 "Server Memory Allocation Failure"); 514 break; 515 516 case XCB_COLORMAP: 517 snprintf (buffer, sizeof(buffer), 518 "Bad Color: 0x%x", err->resource_id); 519 break; 520 521 case XCB_G_CONTEXT: 522 snprintf (buffer, sizeof(buffer), 523 "Bad GC: 0x%x", err->resource_id); 524 break; 525 526 case XCB_ID_CHOICE: 527 snprintf (buffer, sizeof(buffer), 528 "Bad XID: 0x%x", err->resource_id); 529 break; 530 531 case XCB_NAME: 532 snprintf (buffer, sizeof(buffer), 533 "Bad Name"); 534 break; 535 536 case XCB_LENGTH: 537 snprintf (buffer, sizeof(buffer), 538 "Bad Request Length"); 539 break; 540 541 case XCB_IMPLEMENTATION: 542 snprintf (buffer, sizeof(buffer), 543 "Server Implementation Failure"); 544 break; 545 546 default: 547 snprintf (buffer, sizeof(buffer), "Unknown error"); 548 break; 549 } 550 fprintf (stderr, "X Error: %d: %s\n", err->error_code, buffer); 551 } 552 553 fprintf (stderr, " Request Major code: %d\n", err->major_code); 554 if (err->major_code >= 128) 555 { 556 fprintf (stderr, " Request Minor code: %d\n", err->minor_code); 557 } 558 559 fprintf (stderr, " Request serial number: %d\n", err->full_sequence); 560} 561 562/* 563 * Cache for atom lookups in either direction 564 */ 565struct atom_cache_entry { 566 xcb_atom_t atom; 567 const char *name; 568 xcb_intern_atom_cookie_t intern_atom; 569 struct atom_cache_entry *next; 570}; 571 572static struct atom_cache_entry *atom_cache; 573 574/* 575 * Send a request to the server for an atom by name 576 * Does not create the atom if it is not already present 577 */ 578struct atom_cache_entry *Intern_Atom (xcb_connection_t * dpy, const char *name) 579{ 580 struct atom_cache_entry *a; 581 582 for (a = atom_cache ; a != NULL ; a = a->next) { 583 if (strcmp (a->name, name) == 0) 584 return a; /* already requested or found */ 585 } 586 587 a = calloc(1, sizeof(struct atom_cache_entry)); 588 if (a != NULL) { 589 a->name = name; 590 a->intern_atom = xcb_intern_atom (dpy, False, strlen (name), (name)); 591 a->next = atom_cache; 592 atom_cache = a; 593 } 594 return a; 595} 596 597/* Get an atom by name when it is needed. */ 598xcb_atom_t Get_Atom (xcb_connection_t * dpy, const char *name) 599{ 600 struct atom_cache_entry *a = Intern_Atom (dpy, name); 601 602 if (a == NULL) 603 return XCB_ATOM_NONE; 604 605 if (a->atom == XCB_ATOM_NONE) { 606 xcb_intern_atom_reply_t *reply; 607 608 reply = xcb_intern_atom_reply(dpy, a->intern_atom, NULL); 609 if (reply) { 610 a->atom = reply->atom; 611 free (reply); 612 } else { 613 a->atom = (xcb_atom_t) -1; 614 } 615 } 616 if (a->atom == (xcb_atom_t) -1) /* internal error */ 617 return XCB_ATOM_NONE; 618 619 return a->atom; 620} 621 622/* Get the name for an atom when it is needed. */ 623const char *Get_Atom_Name (xcb_connection_t * dpy, xcb_atom_t atom) 624{ 625 struct atom_cache_entry *a; 626 627 for (a = atom_cache ; a != NULL ; a = a->next) { 628 if (a->atom == atom) 629 return a->name; /* already requested or found */ 630 } 631 632 a = calloc(1, sizeof(struct atom_cache_entry)); 633 if (a != NULL) { 634 xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name (dpy, atom); 635 xcb_get_atom_name_reply_t *reply 636 = xcb_get_atom_name_reply (dpy, cookie, NULL); 637 638 a->atom = atom; 639 if (reply) { 640 int len = xcb_get_atom_name_name_length (reply); 641 char *name = malloc(len + 1); 642 if (name) { 643 memcpy (name, xcb_get_atom_name_name (reply), len); 644 name[len] = '\0'; 645 a->name = name; 646 } 647 free (reply); 648 } 649 650 a->next = atom_cache; 651 atom_cache = a; 652 653 return a->name; 654 } 655 return NULL; 656} 657