summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--RemoteControlManager.xml6
-rw-r--r--rcm-client-advanced.c201
-rw-r--r--rcm-client-main.c15
-rw-r--r--rcm-client-main.h35
-rw-r--r--rcm-client.ui377
-rw-r--r--rcm-server-evdev.c77
-rw-r--r--rcm-server-evdev.h6
-rw-r--r--rcm-server-main.c390
-rw-r--r--shared.c16
-rw-r--r--shared.h2
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">&lt;b&gt;&lt;big&gt;Edit Kernel Keytable&lt;/big&gt;&lt;/b&gt;
+ <property name="label" translatable="yes">&lt;b&gt;&lt;big&gt;Edit Kernel Keymap&lt;/big&gt;&lt;/b&gt;
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">&lt;b&gt;&lt;big&gt;Edit Kernel Keymap&lt;/big&gt;&lt;/b&gt;
+Edit the in-kernel protocol/scancode
+to keycode mappings
+&lt;i&gt;Note: changes are not persistent&lt;/i&gt;</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),
diff --git a/shared.c b/shared.c
index a9875bc..396ed1a 100644
--- a/shared.c
+++ b/shared.c
@@ -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)
{
diff --git a/shared.h b/shared.h
index 1857555..9ae7e76 100644
--- a/shared.h
+++ b/shared.h
@@ -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);