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