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
25 Copyright 1993, 1998 The Open Group
26
27 Permission to use, copy, modify, distribute, and sell this software and its
28 documentation for any purpose is hereby granted without fee, provided that
29 the above copyright notice appear in all copies and that both that
30 copyright notice and this permission notice appear in supporting
31 documentation.
32
33 The above copyright notice and this permission notice shall be included
34 in all copies or substantial portions of the Software.
35
36 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
37 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
38 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
39 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
40 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
41 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
42 OTHER DEALINGS IN THE SOFTWARE.
43
44 Except as contained in this notice, the name of The Open Group shall
45 not be used in advertising or otherwise to promote the sale, use or
46 other dealings in this Software without prior written authorization
47 from 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 */
78 const 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 */
Get_Display_Name(const char * display_name)87 const 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 */
Setup_Display_And_Screen(const char * display_name,xcb_connection_t ** dpy,xcb_screen_t ** screen)106 void 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 */
153 static xcb_cursor_t
Create_Font_Cursor(xcb_connection_t * dpy,uint16_t glyph)154 Create_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
Select_Window(xcb_connection_t * dpy,const xcb_screen_t * screen,int descend)176 xcb_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
253 struct 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
265 static 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
272 static xcb_window_t
recursive_Window_With_Name(xcb_connection_t * dpy,xcb_window_t window,struct wininfo_cookies * cookies,const char * name,size_t namelen)273 recursive_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
404 xcb_window_t
Window_With_Name(xcb_connection_t * dpy,xcb_window_t top,const char * name)405 Window_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 */
Fatal_Error(const char * msg,...)427 void 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 */
443 void
Print_X_Error(xcb_connection_t * dpy,xcb_generic_error_t * err)444 Print_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 */
565 struct 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
572 static 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 */
Intern_Atom(xcb_connection_t * dpy,const char * name)578 struct 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. */
Get_Atom(xcb_connection_t * dpy,const char * name)598 xcb_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. */
Get_Atom_Name(xcb_connection_t * dpy,xcb_atom_t atom)623 const 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