dsimple.c revision 863f95b1
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 */ 78char *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; 112 113 /* Open Display */ 114 *dpy = xcb_connect (display_name, &screen_number); 115 if (xcb_connection_has_error (*dpy)) { 116 Fatal_Error ("unable to open display \"%s\"", 117 Get_Display_Name(display_name) ); 118 } 119 120 if (screen) { 121 /* find our screen */ 122 const xcb_setup_t *setup = xcb_get_setup(*dpy); 123 xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup); 124 125 for (i = 0; i < screen_number; i++) 126 xcb_screen_next(&screen_iter); 127 *screen = screen_iter.data; 128 } 129} 130 131/* 132 * xcb equivalent of XCreateFontCursor 133 */ 134static xcb_cursor_t 135Create_Font_Cursor (xcb_connection_t *dpy, uint16_t glyph) 136{ 137 static xcb_font_t cursor_font; 138 xcb_cursor_t cursor; 139 140 if (!cursor_font) { 141 cursor_font = xcb_generate_id (dpy); 142 xcb_open_font (dpy, cursor_font, strlen ("cursor"), "cursor"); 143 } 144 145 cursor = xcb_generate_id (dpy); 146 xcb_create_glyph_cursor (dpy, cursor, cursor_font, cursor_font, 147 glyph, glyph + 1, 148 0, 0, 0, 0xffff, 0xffff, 0xffff); /* rgb, rgb */ 149 150 return cursor; 151} 152 153/* 154 * Routine to let user select a window using the mouse 155 */ 156 157xcb_window_t Select_Window(xcb_connection_t *dpy, 158 const xcb_screen_t *screen, 159 int descend) 160{ 161 xcb_cursor_t cursor; 162 xcb_generic_event_t *event; 163 xcb_window_t target_win = XCB_WINDOW_NONE; 164 xcb_window_t root = screen->root; 165 int buttons = 0; 166 xcb_generic_error_t *err; 167 xcb_grab_pointer_cookie_t grab_cookie; 168 xcb_grab_pointer_reply_t *grab_reply; 169 170 /* Make the target cursor */ 171 cursor = Create_Font_Cursor (dpy, XC_crosshair); 172 173 /* Grab the pointer using target cursor, letting it room all over */ 174 grab_cookie = xcb_grab_pointer 175 (dpy, False, root, 176 XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, 177 XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, 178 root, cursor, XCB_TIME_CURRENT_TIME); 179 grab_reply = xcb_grab_pointer_reply (dpy, grab_cookie, &err); 180 if (grab_reply->status != XCB_GRAB_STATUS_SUCCESS) 181 Fatal_Error ("Can't grab the mouse."); 182 183 /* Let the user select a window... */ 184 while ((target_win == XCB_WINDOW_NONE) || (buttons != 0)) { 185 /* allow one more event */ 186 xcb_allow_events (dpy, XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); 187 xcb_flush (dpy); 188 event = xcb_wait_for_event (dpy); 189 switch (event->response_type & 0x7f) { 190 case XCB_BUTTON_PRESS: 191 { 192 xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event; 193 194 if (target_win == XCB_WINDOW_NONE) { 195 target_win = bp->child; /* window selected */ 196 if (target_win == XCB_WINDOW_NONE) 197 target_win = root; 198 } 199 buttons++; 200 break; 201 } 202 case XCB_BUTTON_RELEASE: 203 if (buttons > 0) /* there may have been some down before we started */ 204 buttons--; 205 break; 206 default: 207 /* just discard all other events */ 208 break; 209 } 210 free (event); 211 } 212 213 xcb_ungrab_pointer (dpy, XCB_TIME_CURRENT_TIME); /* Done with pointer */ 214 215 if (!descend || (target_win == root)) 216 return (target_win); 217 218 target_win = Find_Client (dpy, root, target_win); 219 220 return (target_win); 221} 222 223 224/* 225 * Window_With_Name: routine to locate a window with a given name on a display. 226 * If no window with the given name is found, 0 is returned. 227 * If more than one window has the given name, the first 228 * one found will be returned. Only top and its subwindows 229 * are looked at. Normally, top should be the RootWindow. 230 */ 231 232struct wininfo_cookies { 233 xcb_get_property_cookie_t get_net_wm_name; 234 xcb_get_property_cookie_t get_wm_name; 235 xcb_query_tree_cookie_t query_tree; 236}; 237 238#ifndef USE_XCB_ICCCM 239# define xcb_get_wm_name(Dpy, Win) \ 240 xcb_get_property (Dpy, False, Win, XCB_ATOM_WM_NAME, \ 241 XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ) 242#endif 243 244static xcb_atom_t atom_net_wm_name, atom_utf8_string; 245 246# define xcb_get_net_wm_name(Dpy, Win) \ 247 xcb_get_property (Dpy, False, Win, atom_net_wm_name, \ 248 atom_utf8_string, 0, BUFSIZ) 249 250 251static xcb_window_t 252recursive_Window_With_Name ( 253 xcb_connection_t *dpy, 254 xcb_window_t window, 255 struct wininfo_cookies *cookies, 256 const char *name) 257{ 258 xcb_window_t *children; 259 unsigned int nchildren; 260 int i; 261 xcb_window_t w = 0; 262 xcb_generic_error_t *err; 263 xcb_query_tree_reply_t *tree; 264 struct wininfo_cookies *child_cookies; 265 xcb_get_property_reply_t *prop; 266 267 if (cookies->get_net_wm_name.sequence) { 268 prop = xcb_get_property_reply (dpy, cookies->get_net_wm_name, &err); 269 270 if (prop) { 271 if (prop->type == atom_utf8_string) { 272 const char *prop_name = xcb_get_property_value (prop); 273 int prop_name_len = xcb_get_property_value_length (prop); 274 275 /* can't use strcmp, since prop.name is not null terminated */ 276 if (strncmp (prop_name, name, prop_name_len) == 0) { 277 w = window; 278 } 279 } 280 free (prop); 281 } else if (err) { 282 if (err->response_type == 0) 283 Print_X_Error (dpy, err); 284 return 0; 285 } 286 } 287 288 if (w) { 289 xcb_discard_reply (dpy, cookies->get_wm_name.sequence); 290 } else { 291#ifdef USE_XCB_ICCCM 292 xcb_get_text_property_reply_t nameprop; 293 294 if (xcb_get_wm_name_reply (dpy, cookies->get_wm_name, 295 &nameprop, &err)) { 296 /* can't use strcmp, since nameprop.name is not null terminated */ 297 if (strncmp (nameprop.name, name, nameprop.name_len) == 0) { 298 w = window; 299 } 300 301 xcb_get_text_property_reply_wipe (&nameprop); 302 } 303#else 304 prop = xcb_get_property_reply (dpy, cookies->get_wm_name, &err); 305 306 if (prop) { 307 if (prop->type == XCB_ATOM_STRING) { 308 const char *prop_name = xcb_get_property_value (prop); 309 int prop_name_len = xcb_get_property_value_length (prop); 310 311 /* can't use strcmp, since prop.name is not null terminated */ 312 if (strncmp (prop_name, name, prop_name_len) == 0) { 313 w = window; 314 } 315 } 316 free (prop); 317 } 318#endif 319 else if (err) { 320 if (err->response_type == 0) 321 Print_X_Error (dpy, err); 322 return 0; 323 } 324 } 325 326 if (w) 327 { 328 xcb_discard_reply (dpy, cookies->query_tree.sequence); 329 return w; 330 } 331 332 tree = xcb_query_tree_reply (dpy, cookies->query_tree, &err); 333 if (!tree) { 334 if (err->response_type == 0) 335 Print_X_Error (dpy, err); 336 return 0; 337 } 338 339 nchildren = xcb_query_tree_children_length (tree); 340 children = xcb_query_tree_children (tree); 341 child_cookies = calloc(nchildren, sizeof(struct wininfo_cookies)); 342 343 if (child_cookies == NULL) 344 Fatal_Error("Failed to allocate memory in recursive_Window_With_Name"); 345 346 for (i = 0; i < nchildren; i++) { 347 if (atom_net_wm_name && atom_utf8_string) 348 child_cookies[i].get_net_wm_name = 349 xcb_get_net_wm_name (dpy, children[i]); 350 child_cookies[i].get_wm_name = xcb_get_wm_name (dpy, children[i]); 351 child_cookies[i].query_tree = xcb_query_tree (dpy, children[i]); 352 } 353 xcb_flush (dpy); 354 355 for (i = 0; i < nchildren; i++) { 356 w = recursive_Window_With_Name (dpy, children[i], 357 &child_cookies[i], name); 358 if (w) 359 break; 360 } 361 362 if (w) 363 { 364 /* clean up remaining replies */ 365 for (/* keep previous i */; i < nchildren; i++) { 366 if (child_cookies[i].get_net_wm_name.sequence) 367 xcb_discard_reply (dpy, 368 child_cookies[i].get_net_wm_name.sequence); 369 xcb_discard_reply (dpy, child_cookies[i].get_wm_name.sequence); 370 xcb_discard_reply (dpy, child_cookies[i].query_tree.sequence); 371 } 372 } 373 374 free (child_cookies); 375 free (tree); /* includes storage for children[] */ 376 return (w); 377} 378 379xcb_window_t 380Window_With_Name ( 381 xcb_connection_t *dpy, 382 xcb_window_t top, 383 const char *name) 384{ 385 struct wininfo_cookies cookies; 386 387 atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME"); 388 atom_utf8_string = Get_Atom (dpy, "UTF8_STRING"); 389 390 if (atom_net_wm_name && atom_utf8_string) 391 cookies.get_net_wm_name = xcb_get_net_wm_name (dpy, top); 392 cookies.get_wm_name = xcb_get_wm_name (dpy, top); 393 cookies.query_tree = xcb_query_tree (dpy, top); 394 xcb_flush (dpy); 395 return recursive_Window_With_Name(dpy, top, &cookies, name); 396} 397 398 399/* 400 * Standard fatal error routine - call like printf 401 */ 402void Fatal_Error (char *msg, ...) 403{ 404 va_list args; 405 fflush (stdout); 406 fflush (stderr); 407 fprintf (stderr, "%s: error: ", program_name); 408 va_start (args, msg); 409 vfprintf (stderr, msg, args); 410 va_end (args); 411 fprintf (stderr, "\n"); 412 exit (EXIT_FAILURE); 413} 414 415/* 416 * Print X error information like the default Xlib error handler 417 */ 418void 419Print_X_Error ( 420 xcb_connection_t *dpy, 421 xcb_generic_error_t *err 422 ) 423{ 424 char buffer[256] = ""; 425 426 if ((err == NULL) || (err->response_type != 0)) /* not an error */ 427 return; 428 429 /* Todo: find a more user friendly way to show request/extension info */ 430 if (err->error_code >= 128) 431 { 432 fprintf (stderr, "X Extension Error: Error code %d\n", 433 err->error_code); 434 } 435 else 436 { 437 switch (err->error_code) 438 { 439 case XCB_REQUEST: 440 snprintf (buffer, sizeof(buffer), ": Bad Request"); 441 break; 442 443 case XCB_VALUE: 444 snprintf (buffer, sizeof(buffer), 445 ": Bad Value: 0x%x", err->resource_id); 446 break; 447 448 case XCB_WINDOW: 449 snprintf (buffer, sizeof(buffer), 450 ": Bad Window: 0x%x", err->resource_id); 451 break; 452 453 case XCB_PIXMAP: 454 snprintf (buffer, sizeof(buffer), 455 ": Bad Pixmap: 0x%x", err->resource_id); 456 break; 457 458 case XCB_ATOM: 459 snprintf (buffer, sizeof(buffer), 460 ": Bad Atom: 0x%x", err->resource_id); 461 break; 462 463 case XCB_CURSOR: 464 snprintf (buffer, sizeof(buffer), 465 ": Bad Cursor: 0x%x", err->resource_id); 466 break; 467 468 case XCB_FONT: 469 snprintf (buffer, sizeof(buffer), 470 ": Bad Font: 0x%x", err->resource_id); 471 break; 472 473 case XCB_MATCH: 474 snprintf (buffer, sizeof(buffer), ": Bad Match"); 475 break; 476 477 case XCB_DRAWABLE: 478 snprintf (buffer, sizeof(buffer), 479 ": Bad Drawable: 0x%x", err->resource_id); 480 break; 481 482 case XCB_ACCESS: 483 snprintf (buffer, sizeof(buffer), ": Access Denied"); 484 break; 485 486 case XCB_ALLOC: 487 snprintf (buffer, sizeof(buffer), 488 ": Server Memory Allocation Failure"); 489 break; 490 491 case XCB_COLORMAP: 492 snprintf (buffer, sizeof(buffer), 493 ": Bad Color: 0x%x", err->resource_id); 494 break; 495 496 case XCB_G_CONTEXT: 497 snprintf (buffer, sizeof(buffer), 498 ": Bad GC: 0x%x", err->resource_id); 499 break; 500 501 case XCB_ID_CHOICE: 502 snprintf (buffer, sizeof(buffer), 503 ": Bad XID: 0x%x", err->resource_id); 504 break; 505 506 case XCB_NAME: 507 snprintf (buffer, sizeof(buffer), 508 ": Bad Name"); 509 break; 510 511 case XCB_LENGTH: 512 snprintf (buffer, sizeof(buffer), 513 ": Bad Request Length"); 514 break; 515 516 case XCB_IMPLEMENTATION: 517 snprintf (buffer, sizeof(buffer), 518 ": Server Implementation Failure"); 519 break; 520 521 default: 522 snprintf (buffer, sizeof(buffer), ": Unknown error"); 523 break; 524 } 525 fprintf (stderr, "X Error: %d%s\n", err->error_code, buffer); 526 } 527 528 fprintf (stderr, " Request Major code: %d\n", err->major_code); 529 if (err->major_code >= 128) 530 { 531 fprintf (stderr, " Request Minor code: %d\n", err->minor_code); 532 } 533 534 fprintf (stderr, " Request serial number: %d\n", err->full_sequence); 535} 536 537/* 538 * Cache for atom lookups in either direction 539 */ 540struct atom_cache_entry { 541 xcb_atom_t atom; 542 const char *name; 543 xcb_intern_atom_cookie_t intern_atom; 544 struct atom_cache_entry *next; 545}; 546 547static struct atom_cache_entry *atom_cache; 548 549/* 550 * Send a request to the server for an atom by name 551 * Does not create the atom if it is not already present 552 */ 553struct atom_cache_entry *Intern_Atom (xcb_connection_t * dpy, const char *name) 554{ 555 struct atom_cache_entry *a; 556 557 for (a = atom_cache ; a != NULL ; a = a->next) { 558 if (strcmp (a->name, name) == 0) 559 return a; /* already requested or found */ 560 } 561 562 a = calloc(1, sizeof(struct atom_cache_entry)); 563 if (a != NULL) { 564 a->name = name; 565 a->intern_atom = xcb_intern_atom (dpy, False, strlen (name), (name)); 566 a->next = atom_cache; 567 atom_cache = a; 568 } 569 return a; 570} 571 572/* Get an atom by name when it is needed. */ 573xcb_atom_t Get_Atom (xcb_connection_t * dpy, const char *name) 574{ 575 struct atom_cache_entry *a = Intern_Atom (dpy, name); 576 577 if (a == NULL) 578 return XCB_ATOM_NONE; 579 580 if (a->atom == XCB_ATOM_NONE) { 581 xcb_intern_atom_reply_t *reply; 582 583 reply = xcb_intern_atom_reply(dpy, a->intern_atom, NULL); 584 if (reply) { 585 a->atom = reply->atom; 586 free (reply); 587 } else { 588 a->atom = -1; 589 } 590 } 591 if (a->atom == -1) /* internal error */ 592 return XCB_ATOM_NONE; 593 594 return a->atom; 595} 596 597/* Get the name for an atom when it is needed. */ 598const char *Get_Atom_Name (xcb_connection_t * dpy, xcb_atom_t atom) 599{ 600 struct atom_cache_entry *a; 601 602 for (a = atom_cache ; a != NULL ; a = a->next) { 603 if (a->atom == atom) 604 return a->name; /* already requested or found */ 605 } 606 607 a = calloc(1, sizeof(struct atom_cache_entry)); 608 if (a != NULL) { 609 xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name (dpy, atom); 610 xcb_get_atom_name_reply_t *reply 611 = xcb_get_atom_name_reply (dpy, cookie, NULL); 612 613 a->atom = atom; 614 if (reply) { 615 int len = xcb_get_atom_name_name_length (reply); 616 char *name = malloc(len + 1); 617 if (name) { 618 memcpy (name, xcb_get_atom_name_name (reply), len); 619 name[len] = '\0'; 620 a->name = name; 621 } 622 free (reply); 623 } 624 625 a->next = atom_cache; 626 atom_cache = a; 627 628 return a->name; 629 } 630 return NULL; 631} 632