#include #include #include #include #include "app.h" /* * Changing this to 1 will cause the application to detect and manage * other selections besides the initial ones * (PRIMARY, SECONDARY, and CLIPBOARD). */ #define TRACK_ALL_SELECTIONS 1 struct sel_atom { const char *name; xcb_atom_t *ptr; }; static xcb_atom_t clips[3]; xcb_atom_t xa_targets; xcb_atom_t xa_prop; xcb_atom_t xa_incr; xcb_atom_t xa_atom; xcb_atom_t xa_text; static xcb_atom_t xa_string; static xcb_atom_t xa_utf8; xcb_atom_t wm_name; static struct sel_atom atoms[] = { { "PRIMARY", clips }, { "SECONDARY", clips + 1 }, { "CLIPBOARD", clips + 2 }, { "TARGETS", &xa_targets }, { "ATOM", &xa_atom }, { "INCR", &xa_incr }, { "TEXT", &xa_text }, { "STRING", &xa_string }, { "UTF8_STRING", &xa_utf8 }, { "_SESELMGR_PROP", &xa_prop }, { "WM_NAME", &wm_name }, }; static GHashTable *ahash; static GHashTable *shash; static int initialized; #define NCLIP 3 #define NATOM (sizeof(atoms) / sizeof(*atoms)) #define SEL_MASK (XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | \ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | \ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE) gboolean atom_is_string_type(xcb_atom_t atom) { return (atom == xa_string) || (atom == xa_utf8); } static const char * atom_add(xcb_connection_t *conn, xcb_atom_t atom) { char *name = NULL; xcb_get_atom_name_cookie_t cookie; xcb_get_atom_name_reply_t *reply; xcb_generic_error_t *error; int len; cookie = xcb_get_atom_name(conn, atom); reply = xcb_get_atom_name_reply(conn, cookie, &error); if (error) APP_ERR(error, "An error was received during GetAtomName.\n"); len = xcb_get_atom_name_name_length(reply); name = malloc(len + 1); if (name) { strncpy(name, (const char *)xcb_get_atom_name_name(reply), len); name[len] = '\0'; } g_hash_table_insert(ahash, (void *)atom, name); free(reply); return name; } const char * atom_lookup(xcb_connection_t *conn, xcb_atom_t atom) { const char *name = g_hash_table_lookup(ahash, (void *)atom); if (!name) name = atom_add(conn, atom); return name; } static const char * selection_add(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t atom) { const char *name = atom_lookup(conn, atom); xcb_xfixes_select_selection_input(conn, window, atom, SEL_MASK); g_hash_table_insert(shash, (void *)atom, strdup(name)); return name; } const char * selection_lookup(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t atom) { const char *name = g_hash_table_lookup(shash, (void *)atom); if (TRACK_ALL_SELECTIONS && !name) name = selection_add(conn, window, atom); return name; } void selection_register(xcb_connection_t *conn, xcb_window_t window) { unsigned i; for (i = 0; i < NCLIP; i++) xcb_xfixes_select_selection_input(conn, window, *atoms[i].ptr, SEL_MASK); } void atom_init(xcb_connection_t *conn) { xcb_intern_atom_cookie_t cookie[NATOM]; xcb_intern_atom_reply_t *reply; xcb_generic_error_t *error; unsigned i; if (initialized) return; ahash = g_hash_table_new_full(NULL, NULL, NULL, free); shash = g_hash_table_new_full(NULL, NULL, NULL, free); for (i = 0; i < NATOM; i++) cookie[i] = xcb_intern_atom(conn, 0, strlen(atoms[i].name), atoms[i].name); for (i = 0; i < NATOM; i++) { reply = xcb_intern_atom_reply(conn, cookie[i], &error); if (error) APP_ERR(error, "An error was received during InternAtom.\n"); *atoms[i].ptr = reply->atom; g_hash_table_insert(ahash, (void *)*atoms[i].ptr, strdup(atoms[i].name)); free(reply); } for (i = 0; i < NCLIP; i++) g_hash_table_insert(shash, (void *)*atoms[i].ptr, strdup(atoms[i].name)); initialized = 1; }