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