diff --git a/run/hotkey.run b/run/hotkey.run
new file mode 100644
index 0000000..f81b67e
--- /dev/null
+++ b/run/hotkey.run
@@ -0,0 +1,173 @@
+create_boot_directory
+
+#
+# To use the themed decorator instead of the default one, replace 'pkg/wm'
+# with 'pkg/themed_wm'.
+#
+
+import_from_depot genodelabs/src/[base_src] \
+ genodelabs/pkg/[drivers_interactive_pkg] \
+ genodelabs/pkg/wm \
+ genodelabs/src/init \
+ genodelabs/src/nitpicker \
+ genodelabs/src/ram_fs \
+ genodelabs/src/fs_rom \
+
+install_config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+build {
+ app/hotkey_edit
+ app/xml_editor
+ test/nitpicker
+ init
+}
+
+build_boot_image {
+ init
+ hotkey_edit
+ testnit
+ xml_editor
+ libc.lib.so
+}
+
+run_genode_until forever
diff --git a/src/app/hotkey_edit/main.nim b/src/app/hotkey_edit/main.nim
new file mode 100644
index 0000000..3c5f45c
--- /dev/null
+++ b/src/app/hotkey_edit/main.nim
@@ -0,0 +1,67 @@
+#
+# \brief Hotkey XML editor
+# \author Emery Hemingway
+# \date 2017-09-28
+#
+
+#
+# Copyright (C) 2017 Genode Labs GmbH
+#
+# This file is part of the Genode OS framework, which is distributed
+# under the terms of the GNU General Public License version 2.
+#
+
+import streams, tables, strtabs, xmlparser, xmltree,
+ genode, reportclient, romclient, nitpickerclient, inputclient
+
+var
+ keyActions = initTable[KeyCode, XmlNode]()
+let
+ configRom = newRomClient("config")
+ editReport = newReportClient("xml_editor")
+ nitClient = newNitpickerClient("input")
+
+let
+ input = nitClient.input
+ inputDispatch = newSignalDispatcher()
+
+input.sigh inputDispatch.cap
+
+proc submit(action: XmlNode) =
+ editReport.stream.setPosition 0
+ editReport.stream.writeLine "", action, "", 0.char
+ submit editReport
+
+inputDispatch.handler = proc() =
+ for ev in input.events:
+ if ev.typ == RELEASE and keyActions.contains ev.code:
+ submit keyActions[ev.code]
+
+proc xml(rom: RomClient): XmlNode =
+ ## Parse ROM content as XML.
+ rom.stream.setPosition 0
+ try: result = parseXml rom.stream
+ except XmlError: result = newElement "empty"
+
+proc configHandler() =
+ update configRom
+ clear keyActions
+ let config = configRom.xml
+ for node in config.findAll("key").items:
+ try:
+ let
+ keyName = node.attrs["name"]
+ keyCode = lookupKey keyName
+ if keyActions.contains keyCode:
+ echo "discarding duplicate action for key ", keyName
+ else:
+ keyActions[keyCode] = node[0]
+ except:
+ discard
+
+configRom.handler = configHandler
+ # set the ROM callback
+configHandler()
+ # parse the config
+
+echo "--- hotkey_edit returning to entrypoint ---"
diff --git a/src/app/hotkey_edit/target.mk b/src/app/hotkey_edit/target.mk
new file mode 100644
index 0000000..af30fce
--- /dev/null
+++ b/src/app/hotkey_edit/target.mk
@@ -0,0 +1,6 @@
+TARGET = hotkey_edit
+LIBS = base libc
+SRC_NIM = main.nim
+
+# Peek inside Input::Client
+CC_OPT += -Dprivate=public