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