diff options
author | Eamon Walsh <ewalsh@tycho.nsa.gov> | 2009-07-29 17:34:39 -0400 |
---|---|---|
committer | Eamon Walsh <ewalsh@tycho.nsa.gov> | 2009-07-29 17:34:39 -0400 |
commit | 1b2247c544a7d7ae5101ae26a786b89536e291bf (patch) | |
tree | e21e0bfe1dcb3af092431cf537369c05ea176e66 | |
parent | e7fa84d2281568d5d620dd6d1bde4f5044d14e50 (diff) |
Initial input device monitor and interface tab.
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/app.h | 1 | ||||
-rw-r--r-- | src/inputmon.c | 493 | ||||
-rw-r--r-- | src/main.c | 75 |
4 files changed, 216 insertions, 356 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index eca067a..c6bd887 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,8 @@ pkgpythondir = $(pythondir)/xcb bin_PROGRAMS = sedpymgr -sedpymgr_SOURCES = main.c selmon.c selmgr.c propmon.c atom.c conn.c util.c +sedpymgr_SOURCES = main.c selmon.c selmgr.c propmon.c inputmon.c \ + atom.c conn.c util.c sedpymgr_CFLAGS = $(SEDPYMGR_CFLAGS) sedpymgr_LDADD = $(SEDPYMGR_LIBS) @@ -9,6 +9,7 @@ #include <xcb/xselinux.h> #include <X11/Xlib.h> #include <X11/Xlib-xcb.h> +#include <X11/extensions/XInput2.h> #include <gtk/gtk.h> enum { diff --git a/src/inputmon.c b/src/inputmon.c index d54add6..988d7c1 100644 --- a/src/inputmon.c +++ b/src/inputmon.c @@ -5,440 +5,227 @@ #include "app.h" +Display *dpy; static xcb_connection_t *conn; static GtkTreeStore *store; -xcb_window_t root; +xcb_window_t window; + +static GHashTable *devices; + static void setup_x(const gchar *display) { const xcb_setup_t *setup; xcb_screen_iterator_t iter; + xcb_window_t root; uint32_t mask, list; + XIEventMask all; - conn = make_connection(display); + make_connection(display, &conn, &dpy); setup = xcb_get_setup(conn); iter = xcb_setup_roots_iterator(setup); root = iter.data->root; + window = xcb_generate_id(conn); + xcb_create_window(conn, 0, window, root, 0, 0, 100, 100, 0, + XCB_WINDOW_CLASS_INPUT_ONLY, 0, 0, NULL); + atom_init(conn); /* Select for root window events */ mask = XCB_CW_EVENT_MASK; - list = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; + list = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; xcb_change_window_attributes(conn, root, mask, &list); - xcb_flush(conn); -} - -static char * -get_prop_text(xcb_get_property_reply_t *reply) -{ - size_t size; - char *data; - - if (!atom_is_string_type(reply->type)) - return NULL; - - size = (reply->format >> 3) * xcb_get_property_value_length(reply); - - data = malloc(size + 1); - if (!data) - APP_ERR(NULL, "Out of memory"); - - memcpy(data, xcb_get_property_value(reply), size); - data[size] = '\0'; - - return scrub_text(data); -} - -static gboolean -find_window(xcb_window_t window, GtkTreeIter *iter) -{ - GtkTreeModel *tree = GTK_TREE_MODEL(store); - xcb_window_t cur; - - if (gtk_tree_model_get_iter_first(tree, iter)) { - do { - gtk_tree_model_get(tree, iter, COL_WIN, &cur, -1); - if (cur == window) - return TRUE; - - } while (gtk_tree_model_iter_next(tree, iter)); - } - - return FALSE; -} - -static void -update_window_name(xcb_window_t window, const char *text) -{ - GtkTreeIter iter; - char buf[128]; - - if (find_window(window, &iter)) { - snprintf(buf, sizeof(buf), "%s (%08x)", text, window); - gtk_tree_store_set(store, &iter, COL_NAME, buf, -1); - } -} - -static void -add_item(GtkTreeIter *parent, GtkTreeIter *sibling, - xcb_window_t window, xcb_atom_t atom, - security_context_t octx, security_context_t dctx, - xcb_atom_t type, const char *data) -{ - GtkTreeIter iter; - char *color; - - color = get_colors(octx, dctx); + /* Select for all device events */ + mask = XI_HierarchyChangedMask; + all.deviceid = XIAllDevices; + all.mask_len = 1; + all.mask = (unsigned char *)&mask; - gtk_tree_store_insert_before(store, &iter, parent, sibling); - gtk_tree_store_set(store, &iter, - COL_ICON, GTK_STOCK_FILE, - COL_ATOM, atom, - COL_WIN, window, - COL_DATA, data, - COL_NAME, atom_lookup(conn, atom), - COL_TYPE, atom_lookup(conn, type), - COL_OCTX, octx, - COL_DCTX, dctx, - COL_OFG, color, - COL_OBG, color + 8, - COL_DFG, color + 16, - COL_DBG, color + 24, - -1); + if (XISelectEvents(dpy, window, &all, 1) != Success) + APP_ERR(NULL, "An error was received during XISelectEvents.\n"); - free(color); + XFlush(dpy); } -static void -remove_items(xcb_window_t window, xcb_atom_t atom) -{ - GtkTreeModel *tree = GTK_TREE_MODEL(store); - GtkTreeIter iter, child; - xcb_atom_t iatom; - int more; - - if (!find_window(window, &iter)) - return; - - if (gtk_tree_model_iter_children(tree, &child, &iter)) { - do { - gtk_tree_model_get(tree, &child, COL_ATOM, &iatom, -1); - if (atom == iatom) - more = gtk_tree_store_remove(store, &child); - else - more = gtk_tree_model_iter_next(tree, &child); - } while (more); +static char * +get_use_text(int use) +{ + switch (use) { + case XIMasterPointer: + return "Master Pointer"; + case XIMasterKeyboard: + return "Master Keyboard"; + case XISlavePointer: + return "Slave Pointer"; + case XISlaveKeyboard: + return "Slave Keyboard"; + case XIFloatingSlave: + return "Floating Slave"; + default: + return "Unknown"; } } -static int -update_item_matches(GtkTreeIter *iter, xcb_atom_t atom, security_context_t ctx) -{ - GtkTreeModel *tree = GTK_TREE_MODEL(store); - xcb_atom_t iatom; - security_context_t ictx; - int result; - - gtk_tree_model_get(tree, iter, COL_ATOM, &iatom, COL_OCTX, &ictx, -1); - result = (atom == iatom) && !strcmp(ctx, ictx); - g_free(ictx); - return result; -} - static gboolean -update_item_next(xcb_atom_t atom, GtkTreeIter *iter, GtkTreeIter **sibling) -{ - GtkTreeModel *tree = GTK_TREE_MODEL(store); - xcb_atom_t iatom; - const char *name, *iname; - - gtk_tree_model_get(tree, iter, COL_ATOM, &iatom, -1); - name = atom_lookup(conn, atom); - iname = atom_lookup(conn, iatom); - - if (strcasecmp(name, iname) < 0) { - *sibling = iter; - return FALSE; - } - - *sibling = NULL; - return gtk_tree_model_iter_next(tree, iter); -} - -static void -update_item(xcb_window_t window, xcb_atom_t atom, - security_context_t octx, security_context_t dctx, - xcb_atom_t type, const char *data) +find_device_helper(int id, GtkTreeIter *iter) { GtkTreeModel *tree = GTK_TREE_MODEL(store); - GtkTreeIter iter, child, *sibling = NULL; - char *color; + GtkTreeIter child; + int cur; - if (!find_window(window, &iter)) - return; + gtk_tree_model_get(tree, iter, COL_ATOM, &cur, -1); + if (cur == id) + return TRUE; - if (gtk_tree_model_iter_children(tree, &child, &iter)) { + if (gtk_tree_model_iter_children(tree, &child, iter)) { do { - if (update_item_matches(&child, atom, octx)) { - color = get_colors(NULL, dctx); - gtk_tree_store_set(store, &child, - COL_DATA, data, - COL_TYPE, atom_lookup(conn, type), - COL_DCTX, dctx, - COL_DFG, color + 16, - COL_DBG, color + 24, - -1); - free(color); - return; + if (find_device_helper(id, &child)) { + *iter = child; + return TRUE; } - } while (update_item_next(atom, &child, &sibling)); + } while (gtk_tree_model_iter_next(tree, &child)); } - add_item(&iter, sibling, window, atom, octx, dctx, type, data); -} + while (gtk_tree_model_iter_next(tree, iter)) + if (find_device_helper(id, iter)) + return TRUE; -static gboolean -update_is_managed(xcb_atom_t atom, xcb_atom_t wildcard) -{ - return (wildcard == XCB_NONE || wildcard == atom); + return FALSE; } -static void -update(xcb_window_t window, xcb_atom_t atom) +static gboolean +find_device(int id, GtkTreeIter *iter) { - xcb_selinux_list_properties_cookie_t cookie; - xcb_selinux_list_properties_reply_t *reply; - xcb_get_property_cookie_t *cookies; - xcb_get_property_reply_t *propinfo; - xcb_generic_error_t *error; - xcb_selinux_list_item_iterator_t iter; - security_context_t octx, dctx; - char *text; - int i; - - /* remove existing items if updating */ - if (atom != XCB_NONE) - remove_items(window, atom); - - /* list all property instances on the window */ - cookie = xcb_selinux_list_properties(conn, window); - reply = xcb_selinux_list_properties_reply(conn, cookie, &error); - if (error) { - /* window might have gone away */ - if (error->error_code == XCB_WINDOW) { - free(error); - return; - } - APP_ERR(error, "An error was received during ListProperties.\n"); - } - - iter = xcb_selinux_list_properties_properties_iterator(reply); - cookies = malloc(iter.rem * sizeof(*cookies)); - if (!cookies) - APP_ERR(NULL, "Out of memory\n"); - - /* get the property type and text content */ - i = 0; - while (iter.rem) { - if (update_is_managed(iter.data->name, atom)) { - octx = xcb_selinux_list_item_object_context(iter.data); - xcb_selinux_set_property_use_context(conn, strlen(octx) + 1, octx); - cookies[i] = xcb_get_property(conn, FALSE, window, iter.data->name, - XCB_GET_PROPERTY_TYPE_ANY, 0, 1024); - } - xcb_selinux_list_item_next(&iter); - ++i; - } + GtkTreeModel *tree = GTK_TREE_MODEL(store); - /* process the replies */ - i = 0; - iter = xcb_selinux_list_properties_properties_iterator(reply); - while (iter.rem) { - if (update_is_managed(iter.data->name, atom)) { - octx = xcb_selinux_list_item_object_context(iter.data); - dctx = xcb_selinux_list_item_data_context(iter.data); - propinfo = xcb_get_property_reply(conn, cookies[i], &error); - if (error) - APP_ERR(error, "An error was received during GetProperty.\n"); - - text = get_prop_text(propinfo); - update_item(window, iter.data->name, octx, dctx, propinfo->type, text); - - /* special window updates */ - if (iter.data->name == wm_name) - update_window_name(window, text); - - free(text); - free(propinfo); - } - xcb_selinux_list_item_next(&iter); - ++i; - } + if (gtk_tree_model_get_iter_first(tree, iter)) + return find_device_helper(id, iter); - free(reply); - free(cookies); - xcb_flush(conn); + return FALSE; } static void -add_window(xcb_window_t window) +add_device(XIDeviceInfo *info) { - xcb_selinux_get_window_context_cookie_t cookie; - xcb_selinux_get_window_context_reply_t *reply; + xcb_selinux_get_device_context_cookie_t cookie; + xcb_selinux_get_device_context_reply_t *reply; xcb_generic_error_t *error; security_context_t octx; - uint32_t mask, list; - GtkTreeIter iter; - char *color, buf[24]; + GtkTreeIter iter, parent; + char *color; - /* Get window context */ - cookie = xcb_selinux_get_window_context(conn, window); - reply = xcb_selinux_get_window_context_reply(conn, cookie, &error); + /* Get device context */ + cookie = xcb_selinux_get_device_context(conn, info->deviceid); + reply = xcb_selinux_get_device_context_reply(conn, cookie, &error); if (error) - APP_ERR(error, "An error was received during GetWindowContext.\n"); + APP_ERR(error, "An error was received during GetDeviceContext.\n"); - octx = xcb_selinux_get_window_context_context(reply); + octx = xcb_selinux_get_device_context_context(reply); - /* Add window to list */ color = get_colors(octx, NULL); - if (window == root) - snprintf(buf, sizeof(buf), "root (%08x)", window); - else - snprintf(buf, sizeof(buf), "unnamed (%08x)", window); - gtk_tree_store_append(store, &iter, NULL); + if (info->use == XIMasterPointer) { + /* Create toplevel row */ + gtk_tree_store_append(store, &iter, NULL); + g_hash_table_insert(devices, octx, (gpointer)info->deviceid); + } else { + /* Create child row */ + if (!find_device(info->attachment, &parent)) + APP_ERR(NULL, "Device parent not found!\n"); + gtk_tree_store_append(store, &iter, &parent); + } + + /* Add device to list */ gtk_tree_store_set(store, &iter, - COL_ICON, GTK_STOCK_DIRECTORY, - COL_WIN, window, - COL_NAME, buf, + COL_ICON, GTK_STOCK_FILE, + COL_NAME, info->name, + COL_ATOM, info->deviceid, + COL_WIN, info->attachment, + COL_TYPE, get_use_text(info->use), COL_OCTX, octx, COL_OFG, color, COL_OBG, color + 8, -1); - /* Select for window events */ - if (window != root) { - mask = XCB_CW_EVENT_MASK; - list = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; - xcb_change_window_attributes(conn, window, mask, &list); - } - - update(window, XCB_NONE); free(color); - free(reply); } static void -add_windows(void) +update(void) { - xcb_query_tree_cookie_t cookie; - xcb_query_tree_reply_t *reply; - xcb_generic_error_t *error; - xcb_window_t *children; - int i; - - cookie = xcb_query_tree(conn, root); - reply = xcb_query_tree_reply(conn, cookie, &error); - if (error) - APP_ERR(error, "An error was received during QueryTree.\n"); + XIDeviceInfo *all; + int i, n; - children = xcb_query_tree_children(reply); + gtk_tree_store_clear(store); + g_hash_table_remove_all(devices); - add_window(root); - for (i = 0; i < reply->children_len; i++) - add_window(children[i]); + all = XIQueryDevice(dpy, XIAllDevices, &n); - free(reply); -} + for (i = 0; i < n; i++) + add_device(all + i); -static void -del_window_helper(xcb_window_t window, GtkTreeIter *iter) -{ - GtkTreeModel *tree = GTK_TREE_MODEL(store); - GtkTreeIter child; - xcb_window_t cur; - int more; - - do { - gtk_tree_model_get(tree, iter, COL_WIN, &cur, -1); - if (cur == window) { - if (gtk_tree_model_iter_children(tree, &child, iter)) - del_window_helper(window, &child); - more = gtk_tree_store_remove(store, iter); - } else - more = gtk_tree_model_iter_next(tree, iter); - } while (more); + XIFreeDeviceInfo(all); } static void -del_window(xcb_window_t window) +handle_create(XCreateWindowEvent *event) { - GtkTreeModel *tree = GTK_TREE_MODEL(store); - GtkTreeIter iter; + xcb_selinux_get_client_context_cookie_t cookie; + xcb_selinux_get_client_context_reply_t *reply; + xcb_generic_error_t *error; + security_context_t ctx; + int dev; + XIAnyHierarchyChangeInfo hci; - if (gtk_tree_model_get_iter_first(tree, &iter)) - del_window_helper(window, &iter); -} + /* Get client context */ + cookie = xcb_selinux_get_client_context(conn, event->window); + reply = xcb_selinux_get_client_context_reply(conn, cookie, &error); + if (error) { + /* window might have gone away */ + if (error->error_code == XCB_VALUE) { + free(error); + return; + } + APP_ERR(error, "An error was received during GetClientContext.\n"); + } -static void -handle_property(xcb_property_notify_event_t *event) -{ - update(event->window, event->atom); -} + ctx = xcb_selinux_get_client_context_context(reply); + dev = (int)g_hash_table_lookup(devices, ctx); -static void -handle_create(xcb_create_notify_event_t *event) -{ - add_window(event->window); -} + /* Set device create context */ + xcb_selinux_set_device_create_context(conn, strlen(ctx) + 1, ctx); + if (!dev) { + hci.add.type = XIAddMaster; + hci.add.name = "Protected"; + hci.add.send_core = 0; + hci.add.enable = 1; -static void -handle_destroy(xcb_destroy_notify_event_t *event) -{ - del_window(event->window); + /* Create master device pair for this context */ + if (XIChangeHierarchy(dpy, &hci, 1) != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + } } static gboolean handle_event(GIOChannel *source, GIOCondition condition, gpointer data) { - xcb_generic_event_t *event; - - while ((event = xcb_poll_for_event(conn))) { - switch (event->response_type) { - case XCB_PROPERTY_NOTIFY: - handle_property((xcb_property_notify_event_t *)event); - break; - - case XCB_CREATE_NOTIFY: - handle_create((xcb_create_notify_event_t *)event); - break; - - case XCB_DESTROY_NOTIFY: - handle_destroy((xcb_destroy_notify_event_t *)event); - break; - - case XCB_MAP_NOTIFY: - case XCB_UNMAP_NOTIFY: - case XCB_UNMAP_NOTIFY | 0x80: - case XCB_CONFIGURE_NOTIFY: - case XCB_CONFIGURE_NOTIFY | 0x80: - case XCB_REPARENT_NOTIFY: - case XCB_MAPPING_NOTIFY: - case XCB_CLIENT_MESSAGE: - case XCB_CLIENT_MESSAGE | 0x80: - /* do nothing */ - break; - - default: - fprintf(stderr, "Monitor: %d event\n", event->response_type); - break; - } - free(event); - xcb_flush(conn); + XEvent ev; + XGenericEventCookie *event = (XGenericEventCookie *)&ev; + + while (XPending(dpy)) { + XNextEvent(dpy, &ev); + + if (XGetEventData(dpy, event)) + update(); + else if (ev.type == CreateNotify) + handle_create((XCreateWindowEvent *)event); + + XFreeEventData(dpy, event); + XFlush(dpy); } return TRUE; @@ -452,6 +239,8 @@ input_monitor_init(struct thread_data *data) store = GTK_TREE_STORE(data->store); + devices = g_hash_table_new(g_str_hash, g_str_equal); + setup_x(data->display); fd = xcb_get_file_descriptor(conn); @@ -459,7 +248,7 @@ input_monitor_init(struct thread_data *data) g_io_channel_set_encoding(channel, NULL, NULL); g_io_add_watch(channel, G_IO_IN|G_IO_ERR|G_IO_HUP, handle_event, NULL); - add_windows(); + update(); handle_event(channel, 0, NULL); return 0; @@ -10,7 +10,7 @@ static GtkWidget *appwin; static GtkWidget *expwin; static GtkListStore *sel_store; -static GtkTreeStore *prop_store; +static GtkTreeStore *prop_store, *dev_store; static unsigned timestamp; static gboolean @@ -156,6 +156,68 @@ create_sel_widget(void) } static GtkWidget * +create_dev_widget(void) +{ + GtkTreeStore *store; + GtkTreeView *tree; + GtkTreeSelection *sel; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + store = gtk_tree_store_new(N_COLS, + G_TYPE_STRING, /* SEL */ + G_TYPE_LONG, /* ATOM */ + G_TYPE_LONG, /* WIN */ + G_TYPE_STRING, /* TYPE */ + G_TYPE_STRING, /* DATA */ + G_TYPE_STRING, /* OCTX */ + G_TYPE_STRING, /* OFG */ + G_TYPE_STRING, /* OBG */ + G_TYPE_STRING, /* DCTX */ + G_TYPE_STRING, /* DFG */ + G_TYPE_STRING, /* DBG */ + G_TYPE_STRING /* ICON */ + ); + + tree = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store))); + sel = gtk_tree_view_get_selection(tree); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); + + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes( + "", renderer, + "stock-id", COL_ICON, + NULL); + gtk_tree_view_append_column(tree, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes( + "Name", renderer, + "text", COL_NAME, + NULL); + gtk_tree_view_append_column(tree, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes( + "Type", renderer, + "text", COL_TYPE, + NULL); + gtk_tree_view_append_column(tree, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes( + "Level", renderer, + "text", COL_OCTX, + "foreground", COL_OFG, + "background", COL_OBG, + NULL); + gtk_tree_view_append_column(tree, column); + + dev_store = store; + return GTK_WIDGET(tree); +} + +static GtkWidget * create_prop_widget(void) { GtkTreeStore *store; @@ -304,6 +366,7 @@ create_gui(void) { GtkWidget *selw = create_sel_widget(); GtkWidget *propw = create_prop_widget(); + GtkWidget *devw = create_dev_widget(); GtkWidget *menubar = create_menu(); GtkWidget *scroll, *box, *tabs; @@ -324,6 +387,10 @@ create_gui(void) gtk_container_add(GTK_CONTAINER(scroll), propw); gtk_notebook_append_page(GTK_NOTEBOOK(tabs), scroll, gtk_label_new("Properties")); + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(scroll), devw); + gtk_notebook_append_page(GTK_NOTEBOOK(tabs), scroll, gtk_label_new("Devices")); + box = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), tabs, TRUE, TRUE, 0); @@ -342,17 +409,19 @@ main(int argc, char **argv) create_gui(); data.display = gdk_display_get_name(gdk_display_get_default()); - data.store = GTK_TREE_MODEL(sel_store); data.appwin = appwin; data.expwin = expwin; + data.store = GTK_TREE_MODEL(sel_store); sel_manager_init(&data); sel_monitor_init(&data); data.store = GTK_TREE_MODEL(prop_store); - prop_monitor_init(&data); + data.store = GTK_TREE_MODEL(dev_store); + input_monitor_init(&data); + gtk_widget_show_all(appwin); gtk_main(); |