summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEamon Walsh <ewalsh@tycho.nsa.gov>2009-10-05 12:03:17 -0700
committerEamon Walsh <ewalsh@tycho.nsa.gov>2009-10-05 12:03:17 -0700
commit1eb1e3d7e749c9a268d3119fc56e90a89c12bf0d (patch)
treee6465b8d1d0ffd34bb25cc6455873416cd085038
parentc3deafe1f772f54e4dda294be45830aa1efef318 (diff)
Updates for 2009 Linuxcon/LPC.
Includes password dialog box demo, more device manager funtions, and some misc fixes.
-rw-r--r--src/Makefile.am6
-rw-r--r--src/app.h21
-rw-r--r--src/conn.c2
-rw-r--r--src/inputdemo.c237
-rw-r--r--src/inputmon.c434
-rw-r--r--src/main.c54
-rw-r--r--src/propmon.c7
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
diff --git a/src/app.h b/src/app.h
index b54f15e..005bd2a 100644
--- a/src/app.h
+++ b/src/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,
diff --git a/src/conn.c b/src/conn.c
index 673fda5..ec06221 100644
--- a/src/conn.c
+++ b/src/conn.c
@@ -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);
+ }
+}
diff --git a/src/main.c b/src/main.c
index 94360c5..d890889 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);