diff options
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/app.h | 21 | ||||
-rw-r--r-- | src/conn.c | 2 | ||||
-rw-r--r-- | src/inputdemo.c | 237 | ||||
-rw-r--r-- | src/inputmon.c | 434 | ||||
-rw-r--r-- | src/main.c | 54 | ||||
-rw-r--r-- | src/propmon.c | 7 |
7 files changed, 711 insertions, 50 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c6bd887..e850d36 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,14 @@ pkgpythondir = $(pythondir)/xcb -bin_PROGRAMS = sedpymgr +bin_PROGRAMS = sedpymgr seinputdemo 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) +seinputdemo_SOURCES = inputdemo.c conn.c util.c +seinputdemo_CFLAGS = $(SEDPYMGR_CFLAGS) +seinputdemo_LDADD = $(SEDPYMGR_LIBS) + noinst_HEADERS = app.h @@ -10,6 +10,7 @@ #include <X11/Xlib.h> #include <X11/Xlib-xcb.h> #include <X11/extensions/XInput2.h> +#include <gdk/gdkx.h> #include <gtk/gtk.h> enum { @@ -31,6 +32,7 @@ enum { struct thread_data { const gchar *display; GtkTreeModel *store; + GtkTreeView *view; GtkWidget *appwin; GtkWidget *expwin; }; @@ -58,10 +60,24 @@ int prop_monitor_init(struct thread_data *data); /* inputmon.c */ - int input_monitor_init(struct thread_data *data); +void +input_relabel(GtkWidget *widget, GtkTreeView *view); + +void +input_add(GtkWidget *widget, GtkTreeView *view); + +void +input_remove(GtkWidget *widget, GtkTreeView *view); + +void +input_activate(GtkWidget *widget, GtkTreeView *view); + +void +input_configure(GtkWidget *widget, GtkTreeView *view); + /* atom.c */ extern xcb_atom_t xa_targets; extern xcb_atom_t xa_prop; @@ -89,6 +105,9 @@ selection_register(xcb_connection_t *conn, xcb_window_t window); void make_connection(const gchar *name, xcb_connection_t **conn, Display **dpy); +void +check_xselinux(xcb_connection_t *conn); + int fetch_property_data(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, int delete, @@ -31,7 +31,7 @@ check_xfixes(xcb_connection_t *conn) free(version); } -static void +void check_xselinux(xcb_connection_t *conn) { const xcb_query_extension_reply_t *ext; diff --git a/src/inputdemo.c b/src/inputdemo.c new file mode 100644 index 0000000..d434e14 --- /dev/null +++ b/src/inputdemo.c @@ -0,0 +1,237 @@ +#define _GNU_SOURCE +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include "app.h" + +#define PREFIX "Private Device" +#define PTRNAME "Private Device pointer" +#define KEYNAME "Private Device keyboard" + +#define COREPTR 2 +#define COREKEY 3 + +static xcb_connection_t *conn; +static Display *dpy; +static GtkWidget *dlg; +static GtkListStore *store; +static GtkTreeIter mptr, mkey, cptr, ckey; +static int ndevs; + +static void +add_device(XIDeviceInfo *info, GtkTreeIter *iter) +{ + xcb_selinux_get_device_context_cookie_t cookie; + xcb_selinux_get_device_context_reply_t *reply; + xcb_generic_error_t *error; + + /* Skip the annoying XTEST slaves */ + /* XXX these really need a separate "use" type */ + if (strstr(info->name, "XTEST")) + return; + + /* 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 GetDeviceContext.\n"); + + /* Add device to list */ + gtk_list_store_append(store, iter); + gtk_list_store_set(store, iter, + COL_NAME, info->name, + COL_ATOM, info->deviceid, + COL_WIN, info->attachment, + COL_TYPE, info->use, + COL_OCTX, xcb_selinux_get_device_context_context(reply), + -1); + + free(reply); +} + +static void +create_store(void) +{ + XIDeviceInfo *all; + int i; + GtkTreeIter iter; + + store = gtk_list_store_new(N_COLS, + G_TYPE_STRING, /* NAME */ + G_TYPE_INT, /* ATOM */ + G_TYPE_INT, /* WIN */ + G_TYPE_INT, /* 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 */ + ); + + all = XIQueryDevice(dpy, XIAllDevices, &ndevs); + + /* Find various devices of interest */ + for (i = 0; i < ndevs; i++) + switch (all[i].use) { + case XIMasterPointer: + if (all[i].deviceid == COREPTR) + add_device(all + i, &cptr); + else if (!strcmp(all[i].name, PTRNAME)) + add_device(all + i, &mptr); + break; + case XIMasterKeyboard: + if (all[i].deviceid == COREKEY) + add_device(all + i, &ckey); + else if (!strcmp(all[i].name, KEYNAME)) + add_device(all + i, &mkey); + break; + case XISlavePointer: + if (all[i].attachment == COREPTR) + add_device(all + i, &iter); + break; + case XISlaveKeyboard: + if (all[i].attachment == COREKEY) + add_device(all + i, &iter); + break; + } + + XIFreeDeviceInfo(all); +} + +static void +create_device(void) +{ + XIAnyHierarchyChangeInfo hci; + + /* Create master device pair for this context */ + hci.add.type = XIAddMaster; + hci.add.name = PREFIX; + hci.add.send_core = 1; + hci.add.enable = 1; + + if (XIChangeHierarchy(dpy, &hci, 1) != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); +} + +static void +reattach_devices(void) +{ + GtkTreeModel *tree = GTK_TREE_MODEL(store); + GtkTreeIter iter; + const xcb_setup_t *setup; + security_context_t kctx; + int id, pid, kid, use, i; + XIAnyHierarchyChangeInfo *hci; + + hci = calloc(ndevs, sizeof(*hci)); + if (!hci && ndevs) + APP_ERR(NULL, "Out of memory.\n"); + + /* Set our client pointer to the new device */ + gtk_tree_model_get(tree, &mkey, COL_ATOM, &kid, COL_OCTX, &kctx, -1); + gtk_tree_model_get(tree, &mptr, COL_ATOM, &pid, -1); + setup = xcb_get_setup(conn); + XISetClientPointer(dpy, setup->resource_id_base, pid); + + /* Reattach and relabel all the slave devices */ + i = 0; + gtk_tree_model_get_iter_first(tree, &iter); + do { + gtk_tree_model_get(tree, &iter, COL_ATOM, &id, COL_TYPE, &use, -1); + if (use == XISlaveKeyboard) { + hci[i].attach.type = XIAttachSlave; + hci[i].attach.deviceid = id; + hci[i].attach.new_master = kid; + xcb_selinux_set_device_context(conn, id, strlen(kctx) + 1, kctx); + i++; + } + } while (gtk_tree_model_iter_next(tree, &iter)); + + if (XIChangeHierarchy(dpy, hci, i) != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + + free(kctx); + free(hci); +} + +static void +cleanup_devices(void) +{ + GtkTreeModel *tree = GTK_TREE_MODEL(store); + GtkTreeIter iter; + security_context_t kctx; + int id, pid, use; + XIAnyHierarchyChangeInfo hci; + + /* Remove our master device */ + gtk_tree_model_get(tree, &mptr, COL_ATOM, &pid, -1); + gtk_tree_model_get(tree, &ckey, COL_OCTX, &kctx, -1); + + hci.remove.type = XIRemoveMaster; + hci.remove.deviceid = pid; + hci.remove.return_mode = XIAttachToMaster; + hci.remove.return_pointer = COREPTR; + hci.remove.return_keyboard = COREKEY; + + if (XIChangeHierarchy(dpy, &hci, 1) != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + + /* Relabel all the slave devices */ + gtk_tree_model_get_iter_first(tree, &iter); + do { + gtk_tree_model_get(tree, &iter, COL_ATOM, &id, COL_TYPE, &use, -1); + if (use == XISlaveKeyboard) + xcb_selinux_set_device_context(conn, id, strlen(kctx) + 1, kctx); + } while (gtk_tree_model_iter_next(tree, &iter)); + + free(kctx); + XFlush(dpy); +} + +static void +create_gui(void) +{ + GtkWidget *label, *entry, *vbox; + + /* Create dialog */ + label = gtk_label_new("Enter secret authorization:"); + entry = gtk_entry_new(); + + dlg = gtk_dialog_new_with_buttons("Sample Password Dialog", + 0, + 0, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + NULL); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 10); + gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 10); +} + +int main (int argc, char **argv) +{ + gtk_init(&argc, &argv); + + dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + conn = XGetXCBConnection(dpy); + check_xselinux(conn); + + create_device(); + create_store(); + create_gui(); + reattach_devices(); + + gtk_widget_show_all(dlg); + gtk_dialog_run(GTK_DIALOG(dlg)); + + cleanup_devices(); + + return 0; +} diff --git a/src/inputmon.c b/src/inputmon.c index 988d7c1..d762ab2 100644 --- a/src/inputmon.c +++ b/src/inputmon.c @@ -1,17 +1,33 @@ +#define _GNU_SOURCE #include <unistd.h> #include <stdlib.h> #include <string.h> +#include <stdio.h> #include "app.h" -Display *dpy; +static Display *dpy; static xcb_connection_t *conn; static GtkTreeStore *store; -xcb_window_t window; +static GtkTreeView *view; +static GtkWidget *appwin; +static xcb_window_t window; -static GHashTable *devices; +static GHashTable *clients, *devices; +static int active; +struct change_order { + XIAnyHierarchyChangeInfo *ptr; + security_context_t ctx; + int new_p; + int new_k; +}; + +struct configure_helper_struct { + GtkComboBox *combo; + gpointer type; +}; static void setup_x(const gchar *display) @@ -42,7 +58,7 @@ setup_x(const gchar *display) /* Select for all device events */ mask = XI_HierarchyChangedMask; all.deviceid = XIAllDevices; - all.mask_len = 1; + all.mask_len = 4; all.mask = (unsigned char *)&mask; if (XISelectEvents(dpy, window, &all, 1) != Success) @@ -70,11 +86,112 @@ get_use_text(int use) } } +static void +activate_helper(gpointer key, gpointer value, struct change_order *c) +{ + XIAnyHierarchyChangeInfo *hci = c->ptr; + + hci->attach.type = XIAttachSlave; + hci->attach.deviceid = (int)key; + hci->attach.new_master = (value == (gpointer)XISlavePointer) ? + c->new_p : c->new_k; + + xcb_selinux_set_device_context(conn, (int)key, strlen(c->ctx) + 1, c->ctx); + + c->ptr = c->ptr + 1; +} + +static void +activate(int pid, int kid, security_context_t ctx) +{ + XIAnyHierarchyChangeInfo *hci; + struct change_order c; + guint num; + Status rc; + + num = g_hash_table_size(devices); + hci = calloc(num, sizeof(*hci)); + if (!hci) + APP_ERR(NULL, "Out of memory!\n"); + + c.ptr = hci; + c.ctx = ctx; + c.new_p = pid; + c.new_k = kid; + g_hash_table_foreach(devices, (GHFunc)activate_helper, &c); + + rc = XIChangeHierarchy(dpy, hci, num); + if (rc != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + + active = pid; + free(hci); +} + +static void +relabel(security_context_t ctx, GtkTreeIter *iter) +{ + GtkTreeModel *tree = GTK_TREE_MODEL(store); + GtkTreeIter child; + int cur; + + gtk_tree_model_get(tree, iter, COL_ATOM, &cur, -1); + xcb_selinux_set_device_context(conn, cur, strlen(ctx) + 1, ctx); + + if (gtk_tree_model_iter_children(tree, &child, iter)) { + do { + relabel(ctx, &child); + } while (gtk_tree_model_iter_next(tree, &child)); + } + + while (gtk_tree_model_iter_next(tree, iter)) + relabel(ctx, iter); +} + +static void +configure(int ptr, int key, GtkTreeIter *iter) +{ + GtkTreeModel *tree = GTK_TREE_MODEL(store); + GtkTreeIter child; + XIAnyHierarchyChangeInfo hci; + security_context_t ctx; + int cur; + Status rc; + + hci.attach.type = XIAttachSlave; + + /* do the pointer */ + gtk_tree_model_get(tree, iter, COL_ATOM, &cur, COL_OCTX, &ctx, -1); + + hci.attach.deviceid = ptr; + hci.attach.new_master = cur; + rc = XIChangeHierarchy(dpy, &hci, 1); + if (rc != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + + xcb_selinux_set_device_context(conn, ptr, strlen(ctx) + 1, ctx); + g_free(ctx); + + /* do the keyboard */ + if (!gtk_tree_model_iter_children(tree, &child, iter)) + APP_ERR(NULL, "Master pointer has no attached keyboard!\n"); + gtk_tree_model_get(tree, &child, COL_ATOM, &cur, COL_OCTX, &ctx, -1); + + hci.attach.deviceid = key; + hci.attach.new_master = cur; + rc = XIChangeHierarchy(dpy, &hci, 1); + if (rc != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + + xcb_selinux_set_device_context(conn, key, strlen(ctx) + 1, ctx); + g_free(ctx); +} + static gboolean find_device_helper(int id, GtkTreeIter *iter) { GtkTreeModel *tree = GTK_TREE_MODEL(store); - GtkTreeIter child; + GtkTreeIter child, tmp; int cur; gtk_tree_model_get(tree, iter, COL_ATOM, &cur, -1); @@ -90,9 +207,12 @@ find_device_helper(int id, GtkTreeIter *iter) } while (gtk_tree_model_iter_next(tree, &child)); } - while (gtk_tree_model_iter_next(tree, iter)) - if (find_device_helper(id, iter)) + tmp = *iter; + while (gtk_tree_model_iter_next(tree, &tmp)) + if (find_device_helper(id, &tmp)) { + *iter = tmp; return TRUE; + } return FALSE; } @@ -118,20 +238,21 @@ add_device(XIDeviceInfo *info) GtkTreeIter iter, parent; char *color; + /* Skip the annoying Xtst slaves */ +// if (strstr(info->name, "Xtst")) +// return; + /* 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 GetDeviceContext.\n"); - octx = xcb_selinux_get_device_context_context(reply); - color = get_colors(octx, 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)) @@ -139,6 +260,15 @@ add_device(XIDeviceInfo *info) gtk_tree_store_append(store, &iter, &parent); } + if (info->use == XISlaveKeyboard) + g_hash_table_insert(devices, + (gpointer)info->deviceid, + (gpointer)XISlaveKeyboard); + else if (info->use == XISlavePointer) + g_hash_table_insert(devices, + (gpointer)info->deviceid, + (gpointer)XISlavePointer); + /* Add device to list */ gtk_tree_store_set(store, &iter, COL_ICON, GTK_STOCK_FILE, @@ -152,6 +282,7 @@ add_device(XIDeviceInfo *info) -1); free(color); + free(reply); } static void @@ -161,14 +292,26 @@ update(void) int i, n; gtk_tree_store_clear(store); - g_hash_table_remove_all(devices); all = XIQueryDevice(dpy, XIAllDevices, &n); + /* Add master pointers */ for (i = 0; i < n; i++) - add_device(all + i); + if (all[i].use == XIMasterPointer) + add_device(all + i); + + /* Add master keyboards */ + for (i = 0; i < n; i++) + if (all[i].use == XIMasterKeyboard) + add_device(all + i); + + /* Add slaves */ + for (i = 0; i < n; i++) + if (all[i].use != XIMasterPointer && all[i].use != XIMasterKeyboard) + add_device(all + i); XIFreeDeviceInfo(all); + gtk_tree_view_expand_all(view); } static void @@ -178,36 +321,18 @@ handle_create(XCreateWindowEvent *event) xcb_selinux_get_client_context_reply_t *reply; xcb_generic_error_t *error; security_context_t ctx; - int dev; - XIAnyHierarchyChangeInfo hci; - /* Get client context */ + /* Get window 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"); + free(error); + return; } ctx = xcb_selinux_get_client_context_context(reply); - dev = (int)g_hash_table_lookup(devices, ctx); - - /* 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; - - /* Create master device pair for this context */ - if (XIChangeHierarchy(dpy, &hci, 1) != Success) - APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); - } + g_hash_table_insert(clients, ctx, (gpointer)-1); } static gboolean @@ -218,8 +343,9 @@ handle_event(GIOChannel *source, GIOCondition condition, gpointer data) while (XPending(dpy)) { XNextEvent(dpy, &ev); + XGetEventData(dpy, event); - if (XGetEventData(dpy, event)) + if (ev.type == GenericEvent) update(); else if (ev.type == CreateNotify) handle_create((XCreateWindowEvent *)event); @@ -238,8 +364,11 @@ input_monitor_init(struct thread_data *data) GIOChannel *channel; store = GTK_TREE_STORE(data->store); + appwin = data->appwin; + view = data->view; - devices = g_hash_table_new(g_str_hash, g_str_equal); + clients = g_hash_table_new(g_str_hash, g_str_equal); + devices = g_hash_table_new(NULL, NULL); setup_x(data->display); @@ -253,3 +382,236 @@ input_monitor_init(struct thread_data *data) return 0; } + +static void +input_add_helper(gpointer key, gpointer value, GtkComboBox *combo) +{ + gtk_combo_box_append_text(combo, key); +} + +void +input_relabel(GtkWidget *widget, GtkTreeView *view) +{ + GtkWidget *combo, *dialog, *label, *box; + GtkTreeSelection *sel; + GtkTreeIter iter; + security_context_t ctx; + + /* Create dialog */ + label = gtk_label_new("Select new context for device:"); + combo = gtk_combo_box_new_text(); + g_hash_table_foreach(clients, (GHFunc)input_add_helper, combo); + + dialog = gtk_dialog_new_with_buttons("Create Protected Device", + GTK_WINDOW(appwin), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + NULL); + + box = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 10); + gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 10); + + /* Check selection */ + sel = gtk_tree_view_get_selection(view); + if (gtk_tree_selection_get_selected(sel, NULL, &iter)) { + /* XXX check type */ + /* Show dialog */ + gtk_widget_show_all(dialog); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + /* Relabel devices */ + ctx = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo)); + relabel(ctx, &iter); + g_free(ctx); + xcb_flush(conn); + update(); + } + } + + gtk_widget_destroy(dialog); +} + +static void +configure_add_helper(gpointer key, gpointer value, + struct configure_helper_struct *hs) +{ + GtkTreeModel *tree = GTK_TREE_MODEL(store); + int id = (int)key; + gchar *text, *name; + GtkTreeIter iter; + + if (value == hs->type && find_device(id, &iter)) { + gtk_tree_model_get(tree, &iter, COL_NAME, &name, -1); + asprintf(&text, "%d - %s", id, name); + gtk_combo_box_append_text(hs->combo, text); + free(text); + g_free(name); + } +} + +void +input_configure(GtkWidget *widget, GtkTreeView *view) +{ + GtkWidget *dialog, *ptrcom, *keycom, *ptrlab, *keylab, *box; + GtkTreeSelection *sel; + GtkTreeIter iter; + struct configure_helper_struct hs; + gchar *str; + int ptr, key; + + /* Create dialog */ + ptrlab = gtk_label_new("Select slave pointer to move:"); + ptrcom = gtk_combo_box_new_text(); + hs.combo = GTK_COMBO_BOX(ptrcom); + hs.type = (gpointer)XISlavePointer; + g_hash_table_foreach(devices, (GHFunc)configure_add_helper, &hs); + + keylab = gtk_label_new("Select slave keyboard to move:"); + keycom = gtk_combo_box_new_text(); + hs.combo = GTK_COMBO_BOX(keycom); + hs.type = (gpointer)XISlaveKeyboard; + g_hash_table_foreach(devices, (GHFunc)configure_add_helper, &hs); + + dialog = gtk_dialog_new_with_buttons("Reassign Slave Devices", + GTK_WINDOW(appwin), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + NULL); + + box = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(box), ptrlab, FALSE, TRUE, 10); + gtk_box_pack_start(GTK_BOX(box), ptrcom, TRUE, TRUE, 10); + gtk_box_pack_start(GTK_BOX(box), keylab, FALSE, TRUE, 10); + gtk_box_pack_start(GTK_BOX(box), keycom, TRUE, TRUE, 10); + + /* Check selection */ + sel = gtk_tree_view_get_selection(view); + if (gtk_tree_selection_get_selected(sel, NULL, &iter)) { + /* XXX check type */ + /* Show dialog */ + gtk_widget_show_all(dialog); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + /* Get slave selections */ + str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(ptrcom)); + ptr = atoi(str); + g_free(str); + str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(keycom)); + key = atoi(str); + g_free(str); + /* Reconfigure devices */ + configure(ptr, key, &iter); + xcb_flush(conn); + update(); + } + } + + gtk_widget_destroy(dialog); +} + +void +input_add(GtkWidget *widget, GtkTreeView *view) +{ + security_context_t ctx; + XIAnyHierarchyChangeInfo hci; + GtkWidget *combo, *dialog, *label, *box; + + /* Show dialog */ + label = gtk_label_new("Select context for new device:"); + combo = gtk_combo_box_new_text(); + g_hash_table_foreach(clients, (GHFunc)input_add_helper, combo); + + dialog = gtk_dialog_new_with_buttons("Create Protected Device", + GTK_WINDOW(appwin), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + NULL); + + box = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 10); + gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 10); + gtk_widget_show_all(dialog); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + /* Set device create context */ + ctx = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo)); + xcb_selinux_set_device_create_context(conn, strlen(ctx) + 1, ctx); + hci.add.type = XIAddMaster; + hci.add.name = "Protected"; + hci.add.send_core = 1; + hci.add.enable = 1; + + /* Create master device pair for this context */ + if (XIChangeHierarchy(dpy, &hci, 1) != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + + g_free(ctx); + } + + gtk_widget_destroy(dialog); +} + +void +input_remove(GtkWidget *widget, GtkTreeView *view) +{ + GtkTreeSelection *sel; + GtkTreeIter iter; + int id; + XIAnyHierarchyChangeInfo hci; + + sel = gtk_tree_view_get_selection(view); + + if (gtk_tree_selection_get_selected(sel, NULL, &iter)) { + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, COL_ATOM, &id, -1); + + hci.remove.type = XIRemoveMaster; + hci.remove.deviceid = id; + hci.remove.return_mode = XIFloating; + hci.remove.return_pointer = 0; + hci.remove.return_keyboard = 0; + + /* Create master device pair for this context */ + if (XIChangeHierarchy(dpy, &hci, 1) != Success) + APP_ERR(NULL, "An error was received during XIChangeHierarchy.\n"); + } +} + +void +input_activate(GtkWidget *widget, GtkTreeView *view) +{ + GtkTreeModel *tree = GTK_TREE_MODEL(store); + GtkTreeSelection *sel; + GtkTreeIter iter, child; + int pid, kid; + security_context_t ctx; + + sel = gtk_tree_view_get_selection(view); + + if (gtk_tree_selection_get_selected(sel, NULL, &iter)) { + /* Get the master pointer id */ + gtk_tree_model_get(tree, &iter, COL_ATOM, &pid, -1); + + /* XXX check type */ + if (active == pid) + return; + + /* Get the master keyboard id */ + if (!gtk_tree_model_iter_children(tree, &child, &iter)) + APP_ERR(NULL, "Master pointer has no attached keyboard!\n"); + + gtk_tree_model_get(tree, &child, COL_ATOM, &kid, -1); + + /* Do it */ + gtk_tree_model_get(tree, &iter, COL_OCTX, &ctx, -1); + activate(pid, kid, ctx); + g_free(ctx); + } +} @@ -11,7 +11,7 @@ static GtkWidget *appwin; static GtkWidget *expwin; static GtkListStore *sel_store; static GtkTreeStore *prop_store, *dev_store; -static GtkTreeView *sel_view; +static GtkTreeView *sel_view, *dev_view; unsigned timestamp; static gboolean @@ -52,7 +52,7 @@ create_sel_widget(void) GtkTreeViewColumn *column; store = gtk_list_store_new(N_COLS, - G_TYPE_STRING, /* SEL */ + G_TYPE_STRING, /* NAME */ G_TYPE_ULONG, /* ATOM */ G_TYPE_ULONG, /* WIN */ G_TYPE_STRING, /* TYPE */ @@ -115,7 +115,7 @@ create_dev_widget(void) GtkTreeViewColumn *column; store = gtk_tree_store_new(N_COLS, - G_TYPE_STRING, /* SEL */ + G_TYPE_STRING, /* NAME */ G_TYPE_LONG, /* ATOM */ G_TYPE_LONG, /* WIN */ G_TYPE_STRING, /* TYPE */ @@ -156,6 +156,13 @@ create_dev_widget(void) renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( + "ID", renderer, + "text", COL_ATOM, + 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, @@ -164,6 +171,7 @@ create_dev_widget(void) gtk_tree_view_append_column(tree, column); dev_store = store; + dev_view = tree; return GTK_WIDGET(tree); } @@ -177,7 +185,7 @@ create_prop_widget(void) GtkTreeViewColumn *column; store = gtk_tree_store_new(N_COLS, - G_TYPE_STRING, /* SEL */ + G_TYPE_STRING, /* NAME */ G_TYPE_ULONG, /* ATOM */ G_TYPE_ULONG, /* WIN */ G_TYPE_STRING, /* TYPE */ @@ -306,6 +314,32 @@ create_menu(void) gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); gtk_menu_shell_append(GTK_MENU_SHELL(bar), item); + /* Device menu */ + menu = gtk_menu_new(); + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL); + event_register(G_OBJECT(item)); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(input_add), dev_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL); + event_register(G_OBJECT(item)); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(input_remove), dev_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + item = gtk_menu_item_new_with_mnemonic("_Relabel"); + event_register(G_OBJECT(item)); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(input_relabel), dev_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + item = gtk_menu_item_new_with_mnemonic("_Configure"); + event_register(G_OBJECT(item)); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(input_configure), dev_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + item = gtk_menu_item_new_with_mnemonic("_Device"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); + gtk_menu_shell_append(GTK_MENU_SHELL(bar), item); + return bar; } @@ -321,6 +355,7 @@ create_gui(void) /* App window */ appwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(appwin), 800, 480); + gtk_window_maximize(GTK_WINDOW(appwin)); g_signal_connect(G_OBJECT(appwin), "delete_event", G_CALLBACK(delete_event), NULL); g_signal_connect(G_OBJECT(appwin), "destroy", G_CALLBACK(destroy), NULL); @@ -328,16 +363,16 @@ create_gui(void) tabs = gtk_notebook_new(); 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")); + + scroll = gtk_scrolled_window_new(NULL, NULL); gtk_container_add(GTK_CONTAINER(scroll), selw); gtk_notebook_append_page(GTK_NOTEBOOK(tabs), scroll, gtk_label_new("Selections")); scroll = gtk_scrolled_window_new(NULL, NULL); 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); @@ -368,10 +403,11 @@ main(int argc, char **argv) prop_monitor_init(&data); data.store = GTK_TREE_MODEL(dev_store); + data.view = dev_view; input_monitor_init(&data); gtk_widget_show_all(appwin); gtk_main(); - exit(0); + return 0; } diff --git a/src/propmon.c b/src/propmon.c index 30b9802..01b536f 100644 --- a/src/propmon.c +++ b/src/propmon.c @@ -262,8 +262,10 @@ update(xcb_window_t window, xcb_atom_t 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"); + if (error) { + free(error); + goto out; + } text = get_prop_text(propinfo); update_item(window, iter.data->name, octx, dctx, propinfo->type, text); @@ -279,6 +281,7 @@ update(xcb_window_t window, xcb_atom_t atom) ++i; } +out: free(reply); free(cookies); xcb_flush(conn); |