diff options
-rw-r--r-- | RemoteControlManager.xml | 6 | ||||
-rw-r--r-- | rcm-client-advanced.c | 201 | ||||
-rw-r--r-- | rcm-client-main.c | 15 | ||||
-rw-r--r-- | rcm-client-main.h | 35 | ||||
-rw-r--r-- | rcm-client.ui | 377 | ||||
-rw-r--r-- | rcm-server-evdev.c | 77 | ||||
-rw-r--r-- | rcm-server-evdev.h | 6 | ||||
-rw-r--r-- | rcm-server-main.c | 390 | ||||
-rw-r--r-- | shared.c | 16 | ||||
-rw-r--r-- | shared.h | 2 |
10 files changed, 959 insertions, 166 deletions
diff --git a/RemoteControlManager.xml b/RemoteControlManager.xml index 8f80b19..0462b7d 100644 --- a/RemoteControlManager.xml +++ b/RemoteControlManager.xml @@ -5,6 +5,12 @@ <method name='ListKeymaps'> <arg type='as' name='keymap_ids' direction='out'/> </method> + <method name='GetKernelMappings'> + <arg type='aa{sv}' name='keymap_entries' direction='out'/> + </method> + <method name='SetKernelMappings'> + <arg type='aa{sv}' name='keymap_entries' direction='in'/> + </method> <method name='GetKeymap'> <arg type='s' name='keymap_id' direction='in'/> <arg type='s' name='keymap_name' direction='out'/> diff --git a/rcm-client-advanced.c b/rcm-client-advanced.c index 3374ba3..31ac86b 100644 --- a/rcm-client-advanced.c +++ b/rcm-client-advanced.c @@ -1,6 +1,8 @@ #include <stdint.h> #include <string.h> #include <gtk/gtk.h> +#include <inttypes.h> +#include <stdint.h> #include "generated.h" #include "shared.h" @@ -58,8 +60,8 @@ advanced_transmit_cb(GtkButton *button, gpointer user_data) GError *error = NULL; gboolean r; - protocolw = GTK_COMBO_BOX_TEXT(gtk_builder_get_object(global->builder, "advanced_transmit_protocol")); - scancodew = GTK_ENTRY(gtk_builder_get_object(global->builder, "advanced_transmit_scancode")); + protocolw = GTK_COMBO_BOX_TEXT(get_object("advanced_transmit_protocol")); + scancodew = GTK_ENTRY(get_object("advanced_transmit_scancode")); protocol = gtk_combo_box_text_get_active_text(protocolw); scancode = gtk_entry_get_text(scancodew); @@ -89,7 +91,7 @@ local_create_header_button(const gchar *tooltip, const gchar *icon_name, gboolean end, GCallback callback, gpointer user_data) { - GtkHeaderBar *header = GTK_HEADER_BAR(gtk_builder_get_object(global->builder, "advanced_headerbar")); + GtkHeaderBar *header = GTK_HEADER_BAR(get_object("advanced_headerbar")); GtkWidget *button; button = create_header_button(header, tooltip, icon_name, end, callback, user_data); @@ -114,7 +116,7 @@ advanced_show_main_cb(GtkButton *button, gpointer user_data) GtkStack *stack; remove_header_buttons(); - stack = GTK_STACK(gtk_builder_get_object(global->builder, "advanced_stack")); + stack = GTK_STACK(get_object("advanced_stack")); gtk_stack_set_visible_child_name(stack, "advanced_main_page"); } @@ -123,35 +125,202 @@ advanced_show_transmit_cb(GtkButton *button, gpointer user_data) { RCDevice *object = user_data; GtkStack *stack; - GtkWidget *transmit_button; - stack = GTK_STACK(gtk_builder_get_object(global->builder, "advanced_stack")); + stack = GTK_STACK(get_object("advanced_stack")); gtk_stack_set_visible_child_name(stack, "advanced_transmit_page"); - transmit_button = GTK_WIDGET(gtk_builder_get_object(global->builder, "advanced_transmit_button")); - g_signal_connect(transmit_button, "clicked", G_CALLBACK(advanced_transmit_cb), object); + g_signal_replace_id("advanced_transmit_button", "clicked", G_CALLBACK(advanced_transmit_cb), object); + local_create_header_button("Return to advanced menu", "go-previous-symbolic", false, G_CALLBACK(advanced_show_main_cb), NULL); +} + +static void advanced_show_keymap_cb(GtkButton *button, gpointer user_data); + +static void +advanced_keymap_remove_cb(GtkButton *button, gpointer user_data) +{ + RCDevice *object = user_data; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *protocol; + guint64 scancode; + gchar *keycode; + GVariantBuilder builder; + + selection = GTK_TREE_SELECTION(get_object("advanced_keymap_treeselection")); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + gtk_tree_model_get(model, &iter, 0, &protocol, 1, &scancode, 2, &keycode, -1); + printf("Row to be deleted: %s:%lux:%s\n", protocol, scancode, keycode); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + g_variant_builder_open(&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "protocol", + g_variant_new_string(protocol)); + g_variant_builder_add(&builder, "{sv}", "scancode", + g_variant_new_uint64(scancode)); + g_variant_builder_add(&builder, "{sv}", "keycode", + g_variant_new_string("KEY_RESERVED")); + g_variant_builder_close(&builder); + + if (!rcdevice_call_set_kernel_mappings_sync(object, + g_variant_builder_end(&builder), + NULL, NULL)) + return; + + advanced_show_keymap_cb(NULL, object); +} + +static void +advanced_keymap_row_selected_cb(GtkTreeSelection *selection, gpointer user_data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkWidget *remove; + + remove = GTK_WIDGET(get_object("advanced_keymap_remove")); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) { + g_signal_replace(remove, "clicked", G_CALLBACK(advanced_keymap_remove_cb), user_data); + gtk_widget_set_sensitive(remove, true); + } else + gtk_widget_set_sensitive(remove, false); +} + +static void +advanced_keymap_add_cb(GtkButton *button, gpointer user_data) +{ + RCDevice *object = user_data; + GtkWidget *dialog; + gchar *protocol; + const char *scanstr; + guint64 scancode; + gchar *keycode; + GVariantBuilder builder; + int r; + + dialog = GTK_WIDGET(get_object("advanced_add")); + gtk_widget_show_all(dialog); + r = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_hide(dialog); + + if (r != 1) + return; + + scanstr = gtk_entry_get_text(GTK_ENTRY(get_object("advanced_add_scancode"))); + r = strtoull_strict(scanstr, &scancode); + if (r < 0) + return; + + protocol = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(get_object("advanced_add_protocol"))); + keycode = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(get_object("advanced_add_keycode"))); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); + g_variant_builder_open(&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "protocol", + g_variant_new_string(protocol)); + g_variant_builder_add(&builder, "{sv}", "scancode", + g_variant_new_uint64(scancode)); + g_variant_builder_add(&builder, "{sv}", "keycode", + g_variant_new_string(keycode)); + g_variant_builder_close(&builder); + + g_free(protocol); + g_free(keycode); + + if (!rcdevice_call_set_kernel_mappings_sync(object, + g_variant_builder_end(&builder), + NULL, NULL)) + return; + + advanced_show_keymap_cb(NULL, object); +} + +static void +advanced_show_keymap_cb(GtkButton *button, gpointer user_data) +{ + RCDevice *object = user_data; + GtkListStore *store; + GVariant *keymap_entries = NULL; + GtkStack *stack; + + g_signal_replace_id("advanced_keymap_treeselection", "changed", G_CALLBACK(advanced_keymap_row_selected_cb), object); + g_signal_replace_id("advanced_keymap_add", "clicked", G_CALLBACK(advanced_keymap_add_cb), object); + g_signal_replace_id("advanced_keymap_refresh", "clicked", G_CALLBACK(advanced_show_keymap_cb), object); + store = GTK_LIST_STORE(get_object("advanced_keymap_liststore")); + gtk_list_store_clear(store); + + rcdevice_call_get_kernel_mappings_sync(object, &keymap_entries, NULL, NULL); + + g_assert(g_variant_is_of_type(keymap_entries, G_VARIANT_TYPE("aa{sv}"))); + + GVariantIter iter; + gsize n_items; + GVariant *item; + + n_items = g_variant_iter_init(&iter, keymap_entries); + g_print("Keymap items: %zu\n", n_items); + while (g_variant_iter_loop (&iter, "@a{sv}", &item)) { + gchar *protocol; + guint64 scancode; + gchar *keycode; + GtkTreeIter iter; + + g_variant_lookup(item, "protocol", "s", &protocol); + g_variant_lookup(item, "scancode", "t", &scancode); + g_variant_lookup(item, "keycode", "s", &keycode); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, protocol, 1, scancode, 2, keycode, -1); + } + g_variant_unref(keymap_entries); + + stack = GTK_STACK(get_object("advanced_stack")); + gtk_stack_set_visible_child_name(stack, "advanced_keymap_page"); + remove_header_buttons(); local_create_header_button("Return to advanced menu", "go-previous-symbolic", false, G_CALLBACK(advanced_show_main_cb), NULL); } +static void +render_cell_as_hexadecimal(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, + GtkTreeModel *tree_model, GtkTreeIter *iter, + gpointer user_data) +{ + guint64 val; + gchar *text; + + gtk_tree_model_get(tree_model, iter, 1, &val, -1); + if (val > UINT32_MAX) + text = g_strdup_printf("0x%016" PRIX64, val); + else + text = g_strdup_printf("0x%08" PRIX64, val); + g_object_set(cell, "text", text, NULL); + g_free (text); +} + void advanced_init_ui(RCDevice *object) { - GtkWidget *dialog; GtkWidget *title; - GtkWidget *transmit; gchar *titlestr; + GtkWidget *dialog; - dialog = GTK_WIDGET(gtk_builder_get_object(global->builder, "advanced")); - - title = GTK_WIDGET(gtk_builder_get_object(global->builder, "advanced_main_title_label")); + title = GTK_WIDGET(get_object("advanced_main_title_label")); titlestr = g_markup_printf_escaped("<b><big>Advanced Actions</big></b>\nFor device %s", rcdevice_get_sys_name(object)); gtk_label_set_markup(GTK_LABEL(title), titlestr); g_free(titlestr); - transmit = GTK_WIDGET(gtk_builder_get_object(global->builder, "advanced_main_transmit")); - g_signal_connect(transmit, "clicked", G_CALLBACK(advanced_show_transmit_cb), object); + g_signal_replace_id("advanced_main_transmit", "clicked", G_CALLBACK(advanced_show_transmit_cb), object); + g_signal_replace_id("advanced_main_keymap", "clicked", G_CALLBACK(advanced_show_keymap_cb), object); + + gtk_tree_view_column_set_cell_data_func(GTK_TREE_VIEW_COLUMN(get_object("advanced_keymap_treecol_scancode")), + GTK_CELL_RENDERER(get_object("advanced_keymap_treecell_scancode")), + render_cell_as_hexadecimal, + NULL, NULL); - g_signal_connect_swapped(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), dialog); + dialog = GTK_WIDGET(get_object("advanced")); + g_signal_replace_swapped(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), dialog); + advanced_show_main_cb(NULL, NULL); gtk_widget_show(dialog); } diff --git a/rcm-client-main.c b/rcm-client-main.c index 420ab6c..3595722 100644 --- a/rcm-client-main.c +++ b/rcm-client-main.c @@ -165,6 +165,21 @@ create_header_button(GtkHeaderBar *header, const gchar *tooltip, return button; } +gulong +_g_signal_replace(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GConnectFlags flags) +{ + g_signal_handlers_disconnect_matched(instance, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, c_handler, NULL); + return g_signal_connect_data(instance, detailed_signal, c_handler, data, NULL, flags); +} + +gulong +_g_signal_replace_id(const gchar *widget_id, const gchar *detailed_signal, GCallback c_handler, gpointer data, GConnectFlags flags) +{ + GObject *w = G_OBJECT(gtk_builder_get_object(global->builder, widget_id)); + + return w ? _g_signal_replace(w, detailed_signal, c_handler, data, flags) : 0; +} + int main(int argc, char *argv[]) { GtkCssProvider *css; diff --git a/rcm-client-main.h b/rcm-client-main.h index f48be19..49c96b4 100644 --- a/rcm-client-main.h +++ b/rcm-client-main.h @@ -11,4 +11,39 @@ create_header_button(GtkHeaderBar *header, const gchar *tooltip, const gchar *icon_name, gboolean end, GCallback callback, gpointer user_data); +gulong _g_signal_replace(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GConnectFlags flags); + +static inline gulong +g_signal_replace(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data) +{ + return _g_signal_replace(instance, detailed_signal, c_handler, data, 0); +} + +static inline gulong +g_signal_replace_swapped(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data) +{ + return _g_signal_replace(instance, detailed_signal, c_handler, data, G_CONNECT_SWAPPED); +} + +gulong _g_signal_replace_id(const gchar *widget_id, const gchar *detailed_signal, GCallback c_handler, gpointer data, GConnectFlags flags); + +static inline gulong +g_signal_replace_id(const gchar *widget_id, const gchar *detailed_signal, GCallback c_handler, gpointer data) +{ + return _g_signal_replace_id(widget_id, detailed_signal, c_handler, data, 0); +} + +static inline gulong +g_signal_replace_id_swapped(const gchar *widget_id, const gchar *detailed_signal, GCallback c_handler, gpointer data) +{ + return _g_signal_replace_id(widget_id, detailed_signal, c_handler, data, G_CONNECT_SWAPPED); +} + extern struct global_variables *global; + +static inline GObject * +get_object(const gchar *object_id) +{ + return gtk_builder_get_object(global->builder, object_id); +} + diff --git a/rcm-client.ui b/rcm-client.ui index 0e3fae1..4edd537 100644 --- a/rcm-client.ui +++ b/rcm-client.ui @@ -2,6 +2,31 @@ <!-- Generated with glade 3.19.0 --> <interface> <requires lib="gtk+" version="3.16"/> + <object class="GtkImage" id="advanced_keymap_add_image"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">list-add</property> + </object> + <object class="GtkListStore" id="advanced_keymap_liststore"> + <columns> + <!-- column-name protocol --> + <column type="gchararray"/> + <!-- column-name scancode --> + <column type="guint64"/> + <!-- column-name keycode --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkImage" id="advanced_keymap_refresh_image"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">view-refresh</property> + </object> + <object class="GtkImage" id="advanced_keymap_remove_image"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">list-remove</property> + </object> <object class="GtkWindow" id="advanced"> <property name="can_focus">False</property> <child> @@ -157,20 +182,19 @@ a keytable</property> </packing> </child> <child> - <object class="GtkButton" id="advanced_main_keytable"> + <object class="GtkButton" id="advanced_main_keymap"> <property name="visible">True</property> - <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="hexpand">True</property> <property name="relief">none</property> <child> - <object class="GtkBox" id="advanced_main_keytable_box"> + <object class="GtkBox" id="advanced_main_keymap_box"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="spacing">12</property> <child> - <object class="GtkImage" id="advanced_main_keytable_icon"> + <object class="GtkImage" id="advanced_main_keymap_icon"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="xalign">1</property> @@ -185,12 +209,12 @@ a keytable</property> </packing> </child> <child> - <object class="GtkLabel" id="advanced_main_keytable_label"> + <object class="GtkLabel" id="advanced_main_keymap_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes"><b><big>Edit Kernel Keytable</big></b> + <property name="label" translatable="yes"><b><big>Edit Kernel Keymap</big></b> Edit the in-kernel protocol/scancode -to keycode table</property> +to keycode mappings</property> <property name="use_markup">True</property> </object> <packing> @@ -324,6 +348,179 @@ Based on protocol and scancode</property> <property name="position">1</property> </packing> </child> + <child> + <object class="GtkGrid" id="advanced_keymap_grid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="row_spacing">12</property> + <property name="column_spacing">6</property> + <child> + <object class="GtkImage" id="advanced_keymap_icon"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="valign">start</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="icon_name">accessories-calculator</property> + <property name="icon_size">6</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="advanced_keymap_title_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes"><b><big>Edit Kernel Keymap</big></b> +Edit the in-kernel protocol/scancode +to keycode mappings +<i>Note: changes are not persistent</i></property> + <property name="use_markup">True</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="advanced_keymap_swin"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="advanced_keymap_treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">advanced_keymap_liststore</property> + <property name="rules_hint">True</property> + <property name="search_column">2</property> + <property name="enable_grid_lines">horizontal</property> + <property name="activate_on_single_click">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="advanced_keymap_treeselection"/> + </child> + <child> + <object class="GtkTreeViewColumn" id="advanced_keymap_treecol_protocol"> + <property name="title" translatable="yes">Protocol</property> + <property name="expand">True</property> + <child> + <object class="GtkCellRendererText" id="advanced_keymap_treecell_protocol"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="advanced_keymap_treecol_scancode"> + <property name="title" translatable="yes">Scancode</property> + <property name="expand">True</property> + <child> + <object class="GtkCellRendererText" id="advanced_keymap_treecell_scancode"> + <property name="family">monospace</property> + </object> + <attributes> + <attribute name="text">1</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="advanced_keymap_treecol_keycode"> + <property name="title" translatable="yes">Keycode</property> + <property name="expand">True</property> + <child> + <object class="GtkCellRendererText" id="advanced_keymap_treecell_keycode1"/> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">2</property> + </packing> + </child> + <child> + <object class="GtkBox" id="advanced_keymap_bbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="spacing">6</property> + <child> + <object class="GtkButton" id="advanced_keymap_add"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip_text" translatable="yes">Add entry</property> + <property name="halign">center</property> + <property name="image">advanced_keymap_add_image</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="advanced_keymap_remove"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip_text" translatable="yes">Remove entry</property> + <property name="halign">center</property> + <property name="image">advanced_keymap_remove_image</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="advanced_keymap_refresh"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip_text" translatable="yes">Fetch keymap from kernel again</property> + <property name="image">advanced_keymap_refresh_image</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">2</property> + </packing> + </child> + </object> + <packing> + <property name="name">advanced_keymap_page</property> + <property name="title" translatable="yes">advanced_keymap_page</property> + <property name="position">2</property> + </packing> + </child> </object> </child> <child type="titlebar"> @@ -339,6 +536,172 @@ Based on protocol and scancode</property> </object> </child> </object> + <object class="GtkDialog" id="advanced_add"> + <property name="can_focus">False</property> + <property name="title" translatable="yes">Add Mapping</property> + <property name="modal">True</property> + <property name="destroy_with_parent">True</property> + <property name="type_hint">dialog</property> + <property name="transient_for">advanced</property> + <child internal-child="vbox"> + <object class="GtkBox" id="advanced_add_vbox"> + <property name="can_focus">False</property> + <property name="margin_left">18</property> + <property name="margin_right">18</property> + <property name="margin_top">18</property> + <property name="margin_bottom">18</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="advanced_add_action_area"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="advanced_add_ok"> + <property name="label" translatable="yes">OK</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="advanced_add_cancel"> + <property name="label" translatable="yes">Cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="advanced_add_grid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_bottom">18</property> + <property name="row_spacing">12</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkImage" id="advanced_add_icon"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="icon_name">preferences-desktop-keyboard</property> + <property name="icon_size">6</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="height">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="advacned_add_protocol_label"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Protocol</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="advanced_add_scancode_label"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Scancode</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="advanced_add_keycode_label"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Keycode</property> + <property name="xalign">1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="advanced_add_protocol"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="active">0</property> + <items> + <item translatable="yes">NEC</item> + </items> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="advanced_add_scancode"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="advanced_add_keycode"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <items> + <item translatable="yes">KEY_OK</item> + <item translatable="yes">KEY_VIDEO</item> + </items> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="1">advanced_add_ok</action-widget> + <action-widget response="0">advanced_add_cancel</action-widget> + </action-widgets> + </object> <object class="GtkAdjustment" id="keymap_properties_height_adjustment"> <property name="lower">1</property> <property name="upper">100</property> diff --git a/rcm-server-evdev.c b/rcm-server-evdev.c index 34014e3..9444b7f 100644 --- a/rcm-server-evdev.c +++ b/rcm-server-evdev.c @@ -31,8 +31,8 @@ struct rc_keymap_entry { __u32 keycode; union { struct rc_scancode rc; - __u8 scancode[32]; - } u; + __u8 raw[32]; + }; char end[]; }; #define RKE_SIZE (sizeof(struct rc_scancode)) @@ -54,21 +54,73 @@ evdev_guess_protocol(struct device *device, uint64_t scancode, uint32_t keycode) if (ioctl(device->evdev_fd, EVIOCGKEYCODE_V2, &rke)) break; - if (rke.u.rc.scancode != scancode) + if (rke.rc.scancode != scancode) continue; if (rke.keycode != keycode) continue; - if (rke.u.rc.protocol > ARRAY_SIZE(rc_protocols)) + if (rke.rc.protocol > ARRAY_SIZE(rc_protocols)) return NULL; - return rc_protocols[rke.u.rc.protocol]; + return rc_protocols[rke.rc.protocol]; } return NULL; } +int +evdev_get_mapping(struct device *device, unsigned index, unsigned *ret_protocol, + uint64_t *ret_scancode, struct linux_input_keycode **ret_lik) +{ + struct rc_keymap_entry rke; + struct linux_input_keycode *lik; + + memset(&rke, 0, sizeof(rke)); + rke.len = RKE_SIZE; + rke.index = index; + rke.flags = INPUT_KEYMAP_BY_INDEX; + + if (ioctl(device->evdev_fd, EVIOCGKEYCODE_V2, &rke)) + return 0; + + lik = get_linux_keycode_by_value(rke.keycode); + if (!lik) { + printf("unknown keycode: %" PRIu32 "\n", rke.keycode); + return -EINVAL; + } + + *ret_protocol = rke.rc.protocol; + *ret_scancode = rke.rc.scancode; + *ret_lik = lik; + return 1; +} + +int +evdev_set_mapping(struct device *device, unsigned protocol, uint64_t scancode, + struct linux_input_keycode *lik) +{ + struct rc_keymap_entry rke; + int r; + + memset(&rke, 0, sizeof(rke)); + rke.len = RKE_SIZE; + rke.rc.protocol = protocol; + rke.rc.scancode = scancode; + + if (lik) + rke.keycode = lik->value; + else + rke.keycode = KEY_RESERVED; + + if (ioctl(device->evdev_fd, EVIOCSKEYCODE_V2, &rke)) { + r = -errno; + printf("EVIOCSKEYCODE_V2 failed (%s)\n", strerror(errno)); + } + + return r; +} + static int evdev_clear_keymap(struct device *device) { @@ -111,12 +163,12 @@ set_keycode_new(struct device *device, __u16 protocol, __u64 scancode, __u32 key memset(&rke, 0, sizeof(rke)); rke.len = RKE_SIZE; - rke.u.rc.protocol = protocol; - rke.u.rc.scancode = scancode; + rke.rc.protocol = protocol; + rke.rc.scancode = scancode; rke.keycode = keycode; printf("Setting keycode (new ioctl) 0x%08llX (0x%02X) to 0x%02X\n", - rke.u.rc.scancode, rke.u.rc.protocol, rke.keycode); + rke.rc.scancode, rke.rc.protocol, rke.keycode); if (ioctl(device->evdev_fd, EVIOCSKEYCODE_V2, &rke)) { printf("Unable to call SETKEYCODE2 ioctl\n"); @@ -150,7 +202,6 @@ evdev_read(sd_event_source *s, int fd, uint32_t revents, void *userdata) static bool pressed = false; static uint32_t scancode; static bool scancode_recv = false; - unsigned i; int r; if (fd != device->evdev_fd) @@ -183,17 +234,13 @@ evdev_read(sd_event_source *s, int fd, uint32_t revents, void *userdata) if (keycode) printf("Reading from evdev - multiple keycodes?\n"); - for (i = 0; linux_input_keycodes[i].name; i++) { - if (linux_input_keycodes[i].value == ev.code) - break; - } + keycode = get_linux_keycode_by_value(ev.code); - if (!linux_input_keycodes[i].name) { + if (!keycode) { printf("evdev - unknown keycode (%u)\n", ev.code); break; } - keycode = &linux_input_keycodes[i]; if (ev.value) pressed = true; break; diff --git a/rcm-server-evdev.h b/rcm-server-evdev.h index 2a38852..e9baa6b 100644 --- a/rcm-server-evdev.h +++ b/rcm-server-evdev.h @@ -1,6 +1,12 @@ #ifndef foorcmserverevdevhfoo #define foorcmserverevdevhfoo +int evdev_set_mapping(struct device *device, unsigned protocol, + uint64_t scancode, struct linux_input_keycode *lik); + +int evdev_get_mapping(struct device *device, unsigned index, unsigned *ret_protocol, + uint64_t *ret_scancode, struct linux_input_keycode **ret_lik); + int evdev_setup(struct device *device, const char *path); #endif diff --git a/rcm-server-main.c b/rcm-server-main.c index 4d23f89..d4b41fe 100644 --- a/rcm-server-main.c +++ b/rcm-server-main.c @@ -61,6 +61,261 @@ property_get(sd_bus *bus, const char *path, const char *interface, return -EINVAL; } +static int +sdbus_get_psk_triplet(sd_bus_message *m, unsigned *ret_protocol, + uint64_t *ret_scancode, struct linux_input_keycode **ret_keycode) +{ + const char *protocol = NULL; + unsigned protocol_numeric; + uint64_t scancode; + bool scancode_found = false; + struct linux_input_keycode *lik = NULL; + const char *keycode = NULL; + unsigned i; + int r; + + r = sd_bus_message_enter_container(m, 'a', "{sv}"); + if (r < 1) + return r; + + while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *name; + const char *contents; + char type; + + r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); + if (r < 0) + return r; + + r = sd_bus_message_peek_type(m, NULL, &contents); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return r; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + + if (!strcmp(name, "protocol")) { + if (type != SD_BUS_TYPE_STRING) { + printf("Invalid type for protocol (%c)\n", type); + return -EINVAL; + } + + if (protocol) { + printf("Protocol specified more than once\n"); + return -EINVAL; + } + + r = sd_bus_message_read_basic(m, type, &protocol); + if (r < 0) + return r; + + for (i = 0; rc_protocols[i]; i++) { + if (!strcmp(rc_protocols[i], protocol)) + break; + } + + if (!rc_protocols[i]) { + printf("Invalid protocol name (%s)\n", protocol); + return -EINVAL; + } + + protocol_numeric = i; + + } else if (!strcmp(name, "scancode")) { + if (type != SD_BUS_TYPE_UINT64) { + printf("Invalid type for scancode (%c)\n", type); + return -EINVAL; + } + + if (scancode_found) { + printf("Scancode specified more than once\n"); + return -EINVAL; + } + + r = sd_bus_message_read_basic(m, type, &scancode); + if (r < 0) + return r; + + scancode_found = true; + + } else if (!strcmp(name, "keycode")) { + if (type != SD_BUS_TYPE_STRING) { + printf("Invalid type for keycode (%c)\n", type); + return -EINVAL; + } + + if (keycode) { + printf("Keycode specified more than once\n"); + return -EINVAL; + } + + r = sd_bus_message_read_basic(m, type, &keycode); + if (r < 0) + return r; + + if (strcmp(keycode, "KEY_RESERVED")) { + lik = get_linux_keycode_by_name(keycode); + if (!lik) { + printf("Invalid keycode name (%s)\n", keycode); + return -EINVAL; + } + } + + } else { + r = sd_bus_message_skip(m, contents); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } + + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + if (!protocol) { + printf("Protocol missing\n"); + return -EINVAL; + } + + if (!scancode_found) { + printf("Scancode missing\n"); + return -EINVAL; + } + + if (!keycode) { + printf("Keycode missing\n"); + return -EINVAL; + } + + *ret_protocol = protocol_numeric; + *ret_scancode = scancode; + *ret_keycode = lik; + return 1; +} + +static int +method_getkernelmappings(sd_bus_message *m, void *userdata, sd_bus_error *error) +{ + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + struct manager *mgr = userdata; + struct device *dev; + unsigned protocol_numeric; + uint64_t scancode; + struct linux_input_keycode *lik; + unsigned i = 0; + int r; + + dev = find_device_by_path(mgr, sd_bus_message_get_path(m)); + if (!dev) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidDevice", "Sorry, invalid device"); + return -EINVAL; + } + + if (dev->evdev_fd < 0) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.EvdevError", "Evdev device not available"); + r = -EINVAL; + goto out; + } + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + goto out; + + r = sd_bus_message_open_container(reply, 'a', "a{sv}"); + if (r < 0) + goto out; + + while ((r = evdev_get_mapping(dev, i, &protocol_numeric, &scancode, &lik)) > 0) { + r = sd_bus_message_append(reply, "a{sv}", 3, + "protocol", "s", rc_protocols[protocol_numeric], + "scancode", "t", scancode, + "keycode", "s", lik->name); + if (r < 0) + goto out; + + i++; + } + + if (r < 0) + goto out; + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto out; + + r = sd_bus_send(NULL, reply, NULL); + if (r < 0) + goto out; + + return 1; + +out: + return sd_bus_error_set_errno(error, r); +} + +static int +method_setkernelmappings(sd_bus_message *m, void *userdata, sd_bus_error *error) +{ + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + struct manager *mgr = userdata; + struct device *dev; + unsigned protocol; + uint64_t scancode; + struct linux_input_keycode *keycode; + int r; + + dev = find_device_by_path(mgr, sd_bus_message_get_path(m)); + if (!dev) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.InvalidDevice", "Sorry, invalid device"); + r = -EINVAL; + goto out; + } + + if (dev->evdev_fd < 0) { + sd_bus_error_set_const(error, "org.gnome.RemoteControlManager.EvdevError", "Evdev device not available"); + r = -EINVAL; + goto out; + } + + r = sd_bus_message_enter_container(m, 'a', "a{sv}"); + if (r < 0) + goto out; + + while ((r = sdbus_get_psk_triplet(m, &protocol, &scancode, &keycode)) > 0) { + printf("%s kernel mapping %s:0x%08lx:%s\n", + keycode ? "Setting" : "Unsetting", rc_protocols[protocol], + scancode, keycode ? keycode->name : "<na>"); + evdev_set_mapping(dev, protocol, scancode, keycode); + } + + if (r < 0) + goto out; + + r = sd_bus_message_exit_container(m); + if (r < 0) + goto out; + + return sd_bus_reply_method_return(m, NULL); + +out: + return sd_bus_error_set_errno(error, r); +} static int method_listkeymaps(sd_bus_message *m, void *userdata, sd_bus_error *error) @@ -212,6 +467,9 @@ method_setkeymap_parse_dbus_msg(sd_bus_message *m, struct keymap *keymap, const char *id; const char *name; const char *description; + unsigned protocol; + uint64_t scancode; + struct linux_input_keycode *lik; uint16_t cols, rows; unsigned keycode_count = 0; unsigned layout_count = 0; @@ -268,145 +526,19 @@ method_setkeymap_parse_dbus_msg(sd_bus_message *m, struct keymap *keymap, if (r < 0) return r; - while ((r = sd_bus_message_enter_container(m, 'a', "{sv}")) > 0) { - const char *protocol = NULL; - unsigned protocol_numeric; - uint64_t scancode; - bool scancode_found = false; - struct linux_input_keycode *lik = NULL; - const char *keycode; - unsigned i; - - while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const char *name; - const char *contents; - char type; - - r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); - if (r < 0) - return r; - - r = sd_bus_message_peek_type(m, NULL, &contents); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); - if (r < 0) - return r; - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return r; - - if (!strcmp(name, "protocol")) { - if (type != SD_BUS_TYPE_STRING) { - printf("Invalid type for protocol (%c)\n", type); - return -EINVAL; - } - - if (protocol) { - printf("Protocol specified more than once\n"); - return -EINVAL; - } - - r = sd_bus_message_read_basic(m, type, &protocol); - if (r < 0) - return r; - - for (i = 0; rc_protocols[i]; i++) { - if (!strcmp(rc_protocols[i], protocol)) - break; - } - - if (!rc_protocols[i]) { - printf("Invalid protocol name (%s)\n", protocol); - return -EINVAL; - } - - protocol_numeric = i; - - } else if (!strcmp(name, "scancode")) { - if (type != SD_BUS_TYPE_UINT64) { - printf("Invalid type for scancode (%c)\n", type); - return -EINVAL; - } - - if (scancode_found) { - printf("Scancode specified more than once\n"); - return -EINVAL; - } - - r = sd_bus_message_read_basic(m, type, &scancode); - if (r < 0) - return r; - - scancode_found = true; - - } else if (!strcmp(name, "keycode")) { - if (type != SD_BUS_TYPE_STRING) { - printf("Invalid type for keycode (%c)\n", type); - return -EINVAL; - } - - if (lik) { - printf("Keycode specified more than once\n"); - return -EINVAL; - } - - r = sd_bus_message_read_basic(m, type, &keycode); - if (r < 0) - return r; - - lik = get_linux_keycode_by_name(keycode); - if (!lik) { - printf("Invalid keycode name (%s)\n", keycode); - return -EINVAL; - } - - } else { - r = sd_bus_message_skip(m, contents); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } - - if (r < 0) - return r; - - if (!protocol) { - printf("Keycode entry without protocol\n"); - return -EINVAL; - } - - if (!scancode_found) { - printf("Keycode entry without scancode\n"); - return -EINVAL; - } - + while ((r = sdbus_get_psk_triplet(m, &protocol, &scancode, &lik)) > 0) { if (!lik) { printf("Keycode entry without keycode\n"); return -EINVAL; } if (keymap) { - keymap->keycodes[keycode_count].protocol = protocol_numeric; + keymap->keycodes[keycode_count].protocol = protocol; keymap->keycodes[keycode_count].scancode = scancode; keymap->keycodes[keycode_count].lik = lik; } keycode_count++; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; } if (r < 0) @@ -709,6 +841,8 @@ static const sd_bus_vtable device_vtable[] = { SD_BUS_PROPERTY("KernelKeymapName", "s", property_get, 0, 0), SD_BUS_PROPERTY("HardwareType", "s", property_get, 0, 0), SD_BUS_PROPERTY("Error", "s", property_get, 0, 0), + SD_BUS_METHOD("GetKernelMappings", NULL, "aa{sv}", method_getkernelmappings, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetKernelMappings", "aa{sv}", NULL, method_setkernelmappings, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListKeymaps", NULL, "as", method_listkeymaps, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetKeymap", "s", "ssqqaa{sv}aa{sv}", method_getkeymap, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetKeymap", "sssqqaa{sv}aa{sv}", NULL, method_setkeymap, SD_BUS_VTABLE_UNPRIVILEGED), @@ -45,6 +45,22 @@ get_linux_keycode_by_name(const char *name) return NULL; } +struct linux_input_keycode * +get_linux_keycode_by_value(uint32_t value) +{ + struct linux_input_keycode *lik = NULL; + unsigned i; + + for (i = 0; linux_input_keycodes[i].name; i++) { + if (linux_input_keycodes[i].value == value) { + lik = &linux_input_keycodes[i]; + break; + } + } + + return lik; +} + int strtol_strict(const char *str, int *result) { @@ -22,6 +22,8 @@ extern struct linux_input_keycode linux_input_keycodes[]; struct linux_input_keycode *get_linux_keycode_by_name(const char *name); +struct linux_input_keycode *get_linux_keycode_by_value(uint32_t value); + int strtol_strict(const char *str, int *result); int strtoull_strict(const char *str, uint64_t *result); |