dsimple.c revision 05bee9bc
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 switch (event->response_type & 0x7f) { 209 case XCB_BUTTON_PRESS: 210 { 211 xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event; 212 213 if (target_win == XCB_WINDOW_NONE) { 214 target_win = bp->child; /* window selected */ 215 if (target_win == XCB_WINDOW_NONE) 216 target_win = root; 217 } 218 buttons++; 219 break; 220 } 221 case XCB_BUTTON_RELEASE: 222 if (buttons > 0) /* there may have been some down before we started */ 223 buttons--; 224 break; 225 default: 226 /* just discard all other events */ 227 break; 228 } 229 free (event); 230 } 231 232 xcb_ungrab_pointer (dpy, XCB_TIME_CURRENT_TIME); /* Done with pointer */ 233 234 if (!descend || (target_win == root)) 235 return (target_win); 236 237 target_win = Find_Client (dpy, root, target_win); 238 239 return (target_win); 240} 241 242 243/* 244 * Window_With_Name: routine to locate a window with a given name on a display. 245 * If no window with the given name is found, 0 is returned. 246 * If more than one window has the given name, the first 247 * one found will be returned. Only top and its subwindows 248 * are looked at. Normally, top should be the RootWindow. 249 */ 250 251struct wininfo_cookies { 252 xcb_get_property_cookie_t get_net_wm_name; 253 xcb_get_property_cookie_t get_wm_name; 254 xcb_query_tree_cookie_t query_tree; 255}; 256 257#ifndef USE_XCB_ICCCM 258# define xcb_icccm_get_wm_name(Dpy, Win) \ 259 xcb_get_property (Dpy, False, Win, XCB_ATOM_WM_NAME, \ 260 XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ) 261#endif 262 263static xcb_atom_t atom_net_wm_name, atom_utf8_string; 264 265# define xcb_get_net_wm_name(Dpy, Win) \ 266 xcb_get_property (Dpy, False, Win, atom_net_wm_name, \ 267 atom_utf8_string, 0, BUFSIZ) 268 269 270static xcb_window_t 271recursive_Window_With_Name ( 272 xcb_connection_t *dpy, 273 xcb_window_t window, 274 struct wininfo_cookies *cookies, 275 const char *name, 276 size_t namelen) 277{ 278 xcb_window_t *children; 279 unsigned int nchildren; 280 int i; 281 xcb_window_t w = 0; 282 xcb_generic_error_t *err; 283 xcb_query_tree_reply_t *tree; 284 struct wininfo_cookies *child_cookies; 285 xcb_get_property_reply_t *prop; 286 287 if (cookies->get_net_wm_name.sequence) { 288 prop = xcb_get_property_reply (dpy, cookies->get_net_wm_name, &err); 289 290 if (prop) { 291 if (prop->type == atom_utf8_string) { 292 const char *prop_name = xcb_get_property_value (prop); 293 int prop_name_len = xcb_get_property_value_length (prop); 294 295 /* can't use strcmp, since prop.name is not null terminated */ 296 if ((namelen == prop_name_len) && 297 memcmp (prop_name, name, namelen) == 0) { 298 w = window; 299 } 300 } 301 free (prop); 302 } else if (err) { 303 if (err->response_type == 0) 304 Print_X_Error (dpy, err); 305 return 0; 306 } 307 } 308 309 if (w) { 310 xcb_discard_reply (dpy, cookies->get_wm_name.sequence); 311 } else { 312#ifdef USE_XCB_ICCCM 313 xcb_icccm_get_text_property_reply_t nameprop; 314 315 if (xcb_icccm_get_wm_name_reply (dpy, cookies->get_wm_name, 316 &nameprop, &err)) { 317 /* can't use strcmp, since nameprop.name is not null terminated */ 318 if ((namelen == nameprop.name_len) && 319 memcmp (nameprop.name, name, namelen) == 0) { 320 w = window; 321 } 322 323 xcb_icccm_get_text_property_reply_wipe (&nameprop); 324 } 325#else 326 prop = xcb_get_property_reply (dpy, cookies->get_wm_name, &err); 327 328 if (prop) { 329 if (prop->type == XCB_ATOM_STRING) { 330 const char *prop_name = xcb_get_property_value (prop); 331 int prop_name_len = xcb_get_property_value_length (prop); 332 333 /* can't use strcmp, since prop.name is not null terminated */ 334 if ((namelen == prop_name_len) && 335 memcmp (prop_name, name, namelen) == 0) { 336 w = window; 337 } 338 } 339 free (prop); 340 } 341#endif 342 else if (err) { 343 if (err->response_type == 0) 344 Print_X_Error (dpy, err); 345 return 0; 346 } 347 } 348 349 if (w) 350 { 351 xcb_discard_reply (dpy, cookies->query_tree.sequence); 352 return w; 353 } 354 355 tree = xcb_query_tree_reply (dpy, cookies->query_tree, &err); 356 if (!tree) { 357 if (err->response_type == 0) 358 Print_X_Error (dpy, err); 359 return 0; 360 } 361 362 nchildren = xcb_query_tree_children_length (tree); 363 children = xcb_query_tree_children (tree); 364 child_cookies = calloc(nchildren, sizeof(struct wininfo_cookies)); 365 366 if (child_cookies == NULL) 367 Fatal_Error("Failed to allocate memory in recursive_Window_With_Name"); 368 369 for (i = 0; i < nchildren; i++) { 370 if (atom_net_wm_name && atom_utf8_string) 371 child_cookies[i].get_net_wm_name = 372 xcb_get_net_wm_name (dpy, children[i]); 373 child_cookies[i].get_wm_name = xcb_icccm_get_wm_name (dpy, children[i]); 374 child_cookies[i].query_tree = xcb_query_tree (dpy, children[i]); 375 } 376 xcb_flush (dpy); 377 378 for (i = 0; i < nchildren; i++) { 379 w = recursive_Window_With_Name (dpy, children[i], 380 &child_cookies[i], name, namelen); 381 if (w) 382 break; 383 } 384 385 if (w) 386 { 387 /* clean up remaining replies */ 388 for (/* keep previous i */; i < nchildren; i++) { 389 if (child_cookies[i].get_net_wm_name.sequence) 390 xcb_discard_reply (dpy, 391 child_cookies[i].get_net_wm_name.sequence); 392 xcb_discard_reply (dpy, child_cookies[i].get_wm_name.sequence); 393 xcb_discard_reply (dpy, child_cookies[i].query_tree.sequence); 394 } 395 } 396 397 free (child_cookies); 398 free (tree); /* includes storage for children[] */ 399 return (w); 400} 401 402xcb_window_t 403Window_With_Name ( 404 xcb_connection_t *dpy, 405 xcb_window_t top, 406 const char *name) 407{ 408 struct wininfo_cookies cookies; 409 410 atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME"); 411 atom_utf8_string = Get_Atom (dpy, "UTF8_STRING"); 412 413 if (atom_net_wm_name && atom_utf8_string) 414 cookies.get_net_wm_name = xcb_get_net_wm_name (dpy, top); 415 cookies.get_wm_name = xcb_icccm_get_wm_name (dpy, top); 416 cookies.query_tree = xcb_query_tree (dpy, top); 417 xcb_flush (dpy); 418 return recursive_Window_With_Name(dpy, top, &cookies, name, strlen(name)); 419} 420 421 422/* 423 * Standard fatal error routine - call like printf 424 */ 425void Fatal_Error (const char *msg, ...) 426{ 427 va_list args; 428 fflush (stdout); 429 fflush (stderr); 430 fprintf (stderr, "%s: error: ", program_name); 431 va_start (args, msg); 432 vfprintf (stderr, msg, args); 433 va_end (args); 434 fprintf (stderr, "\n"); 435 exit (EXIT_FAILURE); 436} 437 438/* 439 * Print X error information like the default Xlib error handler 440 */ 441void 442Print_X_Error ( 443 xcb_connection_t *dpy, 444 xcb_generic_error_t *err 445 ) 446{ 447 char buffer[256] = ""; 448 449 if ((err == NULL) || (err->response_type != 0)) /* not an error */ 450 return; 451 452 /* Todo: find a more user friendly way to show request/extension info */ 453 if (err->error_code >= 128) 454 { 455 fprintf (stderr, "X Extension Error: Error code %d\n", 456 err->error_code); 457 } 458 else 459 { 460 switch (err->error_code) 461 { 462 case XCB_REQUEST: 463 snprintf (buffer, sizeof(buffer), "Bad Request"); 464 break; 465 466 case XCB_VALUE: 467 snprintf (buffer, sizeof(buffer), 468 "Bad Value: 0x%x", err->resource_id); 469 break; 470 471 case XCB_WINDOW: 472 snprintf (buffer, sizeof(buffer), 473 "Bad Window: 0x%x", err->resource_id); 474 break; 475 476 case XCB_PIXMAP: 477 snprintf (buffer, sizeof(buffer), 478 "Bad Pixmap: 0x%x", err->resource_id); 479 break; 480 481 case XCB_ATOM: 482 snprintf (buffer, sizeof(buffer), 483 "Bad Atom: 0x%x", err->resource_id); 484 break; 485 486 case XCB_CURSOR: 487 snprintf (buffer, sizeof(buffer), 488 "Bad Cursor: 0x%x", err->resource_id); 489 break; 490 491 case XCB_FONT: 492 snprintf (buffer, sizeof(buffer), 493 "Bad Font: 0x%x", err->resource_id); 494 break; 495 496 case XCB_MATCH: 497 snprintf (buffer, sizeof(buffer), "Bad Match"); 498 break; 499 500 case XCB_DRAWABLE: 501 snprintf (buffer, sizeof(buffer), 502 "Bad Drawable: 0x%x", err->resource_id); 503 break; 504 505 case XCB_ACCESS: 506 snprintf (buffer, sizeof(buffer), "Access Denied"); 507 break; 508 509 case XCB_ALLOC: 510 snprintf (buffer, sizeof(buffer), 511 "Server Memory Allocation Failure"); 512 break; 513 514 case XCB_COLORMAP: 515 snprintf (buffer, sizeof(buffer), 516 "Bad Color: 0x%x", err->resource_id); 517 break; 518 519 case XCB_G_CONTEXT: 520 snprintf (buffer, sizeof(buffer), 521 "Bad GC: 0x%x", err->resource_id); 522 break; 523 524 case XCB_ID_CHOICE: 525 snprintf (buffer, sizeof(buffer), 526 "Bad XID: 0x%x", err->resource_id); 527 break; 528 529 case XCB_NAME: 530 snprintf (buffer, sizeof(buffer), 531 "Bad Name"); 532 break; 533 534 case XCB_LENGTH: 535 snprintf (buffer, sizeof(buffer), 536 "Bad Request Length"); 537 break; 538 539 case XCB_IMPLEMENTATION: 540 snprintf (buffer, sizeof(buffer), 541 "Server Implementation Failure"); 542 break; 543 544 default: 545 snprintf (buffer, sizeof(buffer), "Unknown error"); 546 break; 547 } 548 fprintf (stderr, "X Error: %d: %s\n", err->error_code, buffer); 549 } 550 551 fprintf (stderr, " Request Major code: %d\n", err->major_code); 552 if (err->major_code >= 128) 553 { 554 fprintf (stderr, " Request Minor code: %d\n", err->minor_code); 555 } 556 557 fprintf (stderr, " Request serial number: %d\n", err->full_sequence); 558} 559 560/* 561 * Cache for atom lookups in either direction 562 */ 563struct atom_cache_entry { 564 xcb_atom_t atom; 565 const char *name; 566 xcb_intern_atom_cookie_t intern_atom; 567 struct atom_cache_entry *next; 568}; 569 570static struct atom_cache_entry *atom_cache; 571 572/* 573 * Send a request to the server for an atom by name 574 * Does not create the atom if it is not already present 575 */ 576struct atom_cache_entry *Intern_Atom (xcb_connection_t * dpy, const char *name) 577{ 578 struct atom_cache_entry *a; 579 580 for (a = atom_cache ; a != NULL ; a = a->next) { 581 if (strcmp (a->name, name) == 0) 582 return a; /* already requested or found */ 583 } 584 585 a = calloc(1, sizeof(struct atom_cache_entry)); 586 if (a != NULL) { 587 a->name = name; 588 a->intern_atom = xcb_intern_atom (dpy, False, strlen (name), (name)); 589 a->next = atom_cache; 590 atom_cache = a; 591 } 592 return a; 593} 594 595/* Get an atom by name when it is needed. */ 596xcb_atom_t Get_Atom (xcb_connection_t * dpy, const char *name) 597{ 598 struct atom_cache_entry *a = Intern_Atom (dpy, name); 599 600 if (a == NULL) 601 return XCB_ATOM_NONE; 602 603 if (a->atom == XCB_ATOM_NONE) { 604 xcb_intern_atom_reply_t *reply; 605 606 reply = xcb_intern_atom_reply(dpy, a->intern_atom, NULL); 607 if (reply) { 608 a->atom = reply->atom; 609 free (reply); 610 } else { 611 a->atom = -1; 612 } 613 } 614 if (a->atom == -1) /* internal error */ 615 return XCB_ATOM_NONE; 616 617 return a->atom; 618} 619 620/* Get the name for an atom when it is needed. */ 621const char *Get_Atom_Name (xcb_connection_t * dpy, xcb_atom_t atom) 622{ 623 struct atom_cache_entry *a; 624 625 for (a = atom_cache ; a != NULL ; a = a->next) { 626 if (a->atom == atom) 627 return a->name; /* already requested or found */ 628 } 629 630 a = calloc(1, sizeof(struct atom_cache_entry)); 631 if (a != NULL) { 632 xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name (dpy, atom); 633 xcb_get_atom_name_reply_t *reply 634 = xcb_get_atom_name_reply (dpy, cookie, NULL); 635 636 a->atom = atom; 637 if (reply) { 638 int len = xcb_get_atom_name_name_length (reply); 639 char *name = malloc(len + 1); 640 if (name) { 641 memcpy (name, xcb_get_atom_name_name (reply), len); 642 name[len] = '\0'; 643 a->name = name; 644 } 645 free (reply); 646 } 647 648 a->next = atom_cache; 649 atom_cache = a; 650 651 return a->name; 652 } 653 return NULL; 654} 655