#include "config.h"
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#ifdef USE_XCB_ICCCM
# include <xcb/xcb_icccm.h>
#endif
#include <X11/cursorfont.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "clientwin.h"
#include "dsimple.h"
char *program_name = "unknown_program";
const char *Get_Display_Name (const char *display_name)
{
const char *name = display_name;
if (!name) {
name = getenv ("DISPLAY");
if (!name)
name = "";
}
return (name);
}
void Setup_Display_And_Screen (
const char *display_name,
xcb_connection_t **dpy,
xcb_screen_t **screen)
{
int screen_number, i;
*dpy = xcb_connect (display_name, &screen_number);
if (xcb_connection_has_error (*dpy)) {
Fatal_Error ("unable to open display \"%s\"",
Get_Display_Name(display_name) );
}
if (screen) {
const xcb_setup_t *setup = xcb_get_setup(*dpy);
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
for (i = 0; i < screen_number; i++)
xcb_screen_next(&screen_iter);
*screen = screen_iter.data;
}
}
static xcb_cursor_t
Create_Font_Cursor (xcb_connection_t *dpy, uint16_t glyph)
{
static xcb_font_t cursor_font;
xcb_cursor_t cursor;
if (!cursor_font) {
cursor_font = xcb_generate_id (dpy);
xcb_open_font (dpy, cursor_font, strlen ("cursor"), "cursor");
}
cursor = xcb_generate_id (dpy);
xcb_create_glyph_cursor (dpy, cursor, cursor_font, cursor_font,
glyph, glyph + 1,
0, 0, 0, 0xffff, 0xffff, 0xffff);
return cursor;
}
xcb_window_t Select_Window(xcb_connection_t *dpy,
const xcb_screen_t *screen,
int descend)
{
xcb_cursor_t cursor;
xcb_generic_event_t *event;
xcb_window_t target_win = XCB_WINDOW_NONE;
xcb_window_t root = screen->root;
int buttons = 0;
xcb_generic_error_t *err;
xcb_grab_pointer_cookie_t grab_cookie;
xcb_grab_pointer_reply_t *grab_reply;
cursor = Create_Font_Cursor (dpy, XC_crosshair);
grab_cookie = xcb_grab_pointer
(dpy, False, root,
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE,
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
root, cursor, XCB_TIME_CURRENT_TIME);
grab_reply = xcb_grab_pointer_reply (dpy, grab_cookie, &err);
if (grab_reply->status != XCB_GRAB_STATUS_SUCCESS)
Fatal_Error ("Can't grab the mouse.");
while ((target_win == XCB_WINDOW_NONE) || (buttons != 0)) {
xcb_allow_events (dpy, XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME);
xcb_flush (dpy);
event = xcb_wait_for_event (dpy);
switch (event->response_type & 0x7f) {
case XCB_BUTTON_PRESS:
{
xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event;
if (target_win == XCB_WINDOW_NONE) {
target_win = bp->child;
if (target_win == XCB_WINDOW_NONE)
target_win = root;
}
buttons++;
break;
}
case XCB_BUTTON_RELEASE:
if (buttons > 0)
buttons--;
break;
default:
break;
}
free (event);
}
xcb_ungrab_pointer (dpy, XCB_TIME_CURRENT_TIME);
if (!descend || (target_win == root))
return (target_win);
target_win = Find_Client (dpy, root, target_win);
return (target_win);
}
struct wininfo_cookies {
xcb_get_property_cookie_t get_net_wm_name;
xcb_get_property_cookie_t get_wm_name;
xcb_query_tree_cookie_t query_tree;
};
#ifndef USE_XCB_ICCCM
# define xcb_get_wm_name(Dpy, Win) \
xcb_get_property (Dpy, False, Win, XCB_ATOM_WM_NAME, \
XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ)
#endif
static xcb_atom_t atom_net_wm_name, atom_utf8_string;
# define xcb_get_net_wm_name(Dpy, Win) \
xcb_get_property (Dpy, False, Win, atom_net_wm_name, \
atom_utf8_string, 0, BUFSIZ)
static xcb_window_t
recursive_Window_With_Name (
xcb_connection_t *dpy,
xcb_window_t window,
struct wininfo_cookies *cookies,
const char *name)
{
xcb_window_t *children;
unsigned int nchildren;
int i;
xcb_window_t w = 0;
xcb_generic_error_t *err;
xcb_query_tree_reply_t *tree;
struct wininfo_cookies *child_cookies;
xcb_get_property_reply_t *prop;
if (cookies->get_net_wm_name.sequence) {
prop = xcb_get_property_reply (dpy, cookies->get_net_wm_name, &err);
if (prop) {
if (prop->type == atom_utf8_string) {
const char *prop_name = xcb_get_property_value (prop);
int prop_name_len = xcb_get_property_value_length (prop);
if (strncmp (prop_name, name, prop_name_len) == 0) {
w = window;
}
}
free (prop);
} else if (err) {
if (err->response_type == 0)
Print_X_Error (dpy, err);
return 0;
}
}
if (w) {
xcb_discard_reply (dpy, cookies->get_wm_name.sequence);
} else {
#ifdef USE_XCB_ICCCM
xcb_get_text_property_reply_t nameprop;
if (xcb_get_wm_name_reply (dpy, cookies->get_wm_name,
&nameprop, &err)) {
if (strncmp (nameprop.name, name, nameprop.name_len) == 0) {
w = window;
}
xcb_get_text_property_reply_wipe (&nameprop);
}
#else
prop = xcb_get_property_reply (dpy, cookies->get_wm_name, &err);
if (prop) {
if (prop->type == XCB_ATOM_STRING) {
const char *prop_name = xcb_get_property_value (prop);
int prop_name_len = xcb_get_property_value_length (prop);
if (strncmp (prop_name, name, prop_name_len) == 0) {
w = window;
}
}
free (prop);
}
#endif
else if (err) {
if (err->response_type == 0)
Print_X_Error (dpy, err);
return 0;
}
}
if (w)
{
xcb_discard_reply (dpy, cookies->query_tree.sequence);
return w;
}
tree = xcb_query_tree_reply (dpy, cookies->query_tree, &err);
if (!tree) {
if (err->response_type == 0)
Print_X_Error (dpy, err);
return 0;
}
nchildren = xcb_query_tree_children_length (tree);
children = xcb_query_tree_children (tree);
child_cookies = calloc(nchildren, sizeof(struct wininfo_cookies));
if (child_cookies == NULL)
Fatal_Error("Failed to allocate memory in recursive_Window_With_Name");
for (i = 0; i < nchildren; i++) {
if (atom_net_wm_name && atom_utf8_string)
child_cookies[i].get_net_wm_name =
xcb_get_net_wm_name (dpy, children[i]);
child_cookies[i].get_wm_name = xcb_get_wm_name (dpy, children[i]);
child_cookies[i].query_tree = xcb_query_tree (dpy, children[i]);
}
xcb_flush (dpy);
for (i = 0; i < nchildren; i++) {
w = recursive_Window_With_Name (dpy, children[i],
&child_cookies[i], name);
if (w)
break;
}
if (w)
{
for (; i < nchildren; i++) {
if (child_cookies[i].get_net_wm_name.sequence)
xcb_discard_reply (dpy,
child_cookies[i].get_net_wm_name.sequence);
xcb_discard_reply (dpy, child_cookies[i].get_wm_name.sequence);
xcb_discard_reply (dpy, child_cookies[i].query_tree.sequence);
}
}
free (child_cookies);
free (tree);
return (w);
}
xcb_window_t
Window_With_Name (
xcb_connection_t *dpy,
xcb_window_t top,
const char *name)
{
struct wininfo_cookies cookies;
atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME");
atom_utf8_string = Get_Atom (dpy, "UTF8_STRING");
if (atom_net_wm_name && atom_utf8_string)
cookies.get_net_wm_name = xcb_get_net_wm_name (dpy, top);
cookies.get_wm_name = xcb_get_wm_name (dpy, top);
cookies.query_tree = xcb_query_tree (dpy, top);
xcb_flush (dpy);
return recursive_Window_With_Name(dpy, top, &cookies, name);
}
void Fatal_Error (char *msg, ...)
{
va_list args;
fflush (stdout);
fflush (stderr);
fprintf (stderr, "%s: error: ", program_name);
va_start (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
fprintf (stderr, "\n");
exit (EXIT_FAILURE);
}
void
Print_X_Error (
xcb_connection_t *dpy,
xcb_generic_error_t *err
)
{
char buffer[256] = "";
if ((err == NULL) || (err->response_type != 0))
return;
if (err->error_code >= 128)
{
fprintf (stderr, "X Extension Error: Error code %d\n",
err->error_code);
}
else
{
switch (err->error_code)
{
case XCB_REQUEST:
snprintf (buffer, sizeof(buffer), ": Bad Request");
break;
case XCB_VALUE:
snprintf (buffer, sizeof(buffer),
": Bad Value: 0x%x", err->resource_id);
break;
case XCB_WINDOW:
snprintf (buffer, sizeof(buffer),
": Bad Window: 0x%x", err->resource_id);
break;
case XCB_PIXMAP:
snprintf (buffer, sizeof(buffer),
": Bad Pixmap: 0x%x", err->resource_id);
break;
case XCB_ATOM:
snprintf (buffer, sizeof(buffer),
": Bad Atom: 0x%x", err->resource_id);
break;
case XCB_CURSOR:
snprintf (buffer, sizeof(buffer),
": Bad Cursor: 0x%x", err->resource_id);
break;
case XCB_FONT:
snprintf (buffer, sizeof(buffer),
": Bad Font: 0x%x", err->resource_id);
break;
case XCB_MATCH:
snprintf (buffer, sizeof(buffer), ": Bad Match");
break;
case XCB_DRAWABLE:
snprintf (buffer, sizeof(buffer),
": Bad Drawable: 0x%x", err->resource_id);
break;
case XCB_ACCESS:
snprintf (buffer, sizeof(buffer), ": Access Denied");
break;
case XCB_ALLOC:
snprintf (buffer, sizeof(buffer),
": Server Memory Allocation Failure");
break;
case XCB_COLORMAP:
snprintf (buffer, sizeof(buffer),
": Bad Color: 0x%x", err->resource_id);
break;
case XCB_G_CONTEXT:
snprintf (buffer, sizeof(buffer),
": Bad GC: 0x%x", err->resource_id);
break;
case XCB_ID_CHOICE:
snprintf (buffer, sizeof(buffer),
": Bad XID: 0x%x", err->resource_id);
break;
case XCB_NAME:
snprintf (buffer, sizeof(buffer),
": Bad Name");
break;
case XCB_LENGTH:
snprintf (buffer, sizeof(buffer),
": Bad Request Length");
break;
case XCB_IMPLEMENTATION:
snprintf (buffer, sizeof(buffer),
": Server Implementation Failure");
break;
default:
snprintf (buffer, sizeof(buffer), ": Unknown error");
break;
}
fprintf (stderr, "X Error: %d%s\n", err->error_code, buffer);
}
fprintf (stderr, " Request Major code: %d\n", err->major_code);
if (err->major_code >= 128)
{
fprintf (stderr, " Request Minor code: %d\n", err->minor_code);
}
fprintf (stderr, " Request serial number: %d\n", err->full_sequence);
}
struct atom_cache_entry {
xcb_atom_t atom;
const char *name;
xcb_intern_atom_cookie_t intern_atom;
struct atom_cache_entry *next;
};
static struct atom_cache_entry *atom_cache;
struct atom_cache_entry *Intern_Atom (xcb_connection_t * dpy, const char *name)
{
struct atom_cache_entry *a;
for (a = atom_cache ; a != NULL ; a = a->next) {
if (strcmp (a->name, name) == 0)
return a;
}
a = calloc(1, sizeof(struct atom_cache_entry));
if (a != NULL) {
a->name = name;
a->intern_atom = xcb_intern_atom (dpy, False, strlen (name), (name));
a->next = atom_cache;
atom_cache = a;
}
return a;
}
xcb_atom_t Get_Atom (xcb_connection_t * dpy, const char *name)
{
struct atom_cache_entry *a = Intern_Atom (dpy, name);
if (a == NULL)
return XCB_ATOM_NONE;
if (a->atom == XCB_ATOM_NONE) {
xcb_intern_atom_reply_t *reply;
reply = xcb_intern_atom_reply(dpy, a->intern_atom, NULL);
if (reply) {
a->atom = reply->atom;
free (reply);
} else {
a->atom = -1;
}
}
if (a->atom == -1)
return XCB_ATOM_NONE;
return a->atom;
}
const char *Get_Atom_Name (xcb_connection_t * dpy, xcb_atom_t atom)
{
struct atom_cache_entry *a;
for (a = atom_cache ; a != NULL ; a = a->next) {
if (a->atom == atom)
return a->name;
}
a = calloc(1, sizeof(struct atom_cache_entry));
if (a != NULL) {
xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name (dpy, atom);
xcb_get_atom_name_reply_t *reply
= xcb_get_atom_name_reply (dpy, cookie, NULL);
a->atom = atom;
if (reply) {
int len = xcb_get_atom_name_name_length (reply);
char *name = malloc(len + 1);
if (name) {
memcpy (name, xcb_get_atom_name_name (reply), len);
name[len] = '\0';
a->name = name;
}
free (reply);
}
a->next = atom_cache;
atom_cache = a;
return a->name;
}
return NULL;
}