diff --git a/flake.nix b/flake.nix
index 220ad15..a9116b0 100644
--- a/flake.nix
+++ b/flake.nix
@@ -129,5 +129,8 @@
legacyPackages = self.legacyPackages.${system};
});
in { x86_64-linux = checks'.x86_64-linux-x86_64-genode; };
+
+ nixosModules = import ./nixos-modules { inherit self; };
+
};
}
diff --git a/nixos-modules/buildBootDescription.nix b/nixos-modules/buildBootDescription.nix
new file mode 100644
index 0000000..fc2323a
--- /dev/null
+++ b/nixos-modules/buildBootDescription.nix
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: CC0-1.0
+
+# Generate a total boot description by matching the binaries referred to by an init
+# configuration with a list of input packages.
+
+{ lib, writeText, dhall-json }:
+
+{ name, initConfig, imageInputs, extraBinaries ? [ ], extraRoms ? { } }:
+
+with builtins;
+let
+ extractDrv = lib.runDhallCommand "binaries.json" {
+ nativeBuildInputs = [ dhall-json ];
+ } ''
+ dhall-to-json << TRUE_DEATH > $out
+ let Genode = env:DHALL_GENODE
+ let init = ${initConfig}
+ in Genode.Init.Child.binaries (Genode.Init.toChild init Genode.Init.Attributes::{=})
+ TRUE_DEATH
+ '';
+ binariesJSON = readFile (toString extractDrv);
+ binaries = lib.unique (fromJSON binariesJSON ++ extraBinaries);
+
+ matches = let
+ f = binary: {
+ name = binary;
+ value = let maybeNull = map (drv: toPath "${drv}/${binary}") imageInputs;
+ in filter pathExists maybeNull;
+ };
+ in map f binaries;
+
+ binaryPaths = let
+ f = { name, value }:
+ let l = length value;
+ in if l == 1 then {
+ inherit name;
+ value = elemAt value 0;
+ } else if l == 0 then
+ throw "${name} not found in imageInputs"
+ else
+ throw "${name} found in multiple imageInputs, ${toString value}";
+ in map f matches;
+
+ extraList =
+ lib.mapAttrsToList (name: value: { inherit name value; }) extraRoms;
+
+in writeText "${name}.boot.dhall" ''
+ let Genode = env:DHALL_GENODE
+ in { config = ${initConfig}
+ , rom = Genode.BootModules.toRomPaths ([
+ ${
+ toString (map ({ name, value }: ''
+ , { mapKey = "${name}", mapValue = "${value}" }
+ '') (binaryPaths ++ extraList))
+ }
+ ])
+ }
+''
diff --git a/nixos-modules/default.nix b/nixos-modules/default.nix
new file mode 100644
index 0000000..5899f5c
--- /dev/null
+++ b/nixos-modules/default.nix
@@ -0,0 +1,5 @@
+{ self }:
+
+{
+ vbox = import ./vbox.nix { inherit self; };
+}
diff --git a/nixos-modules/partition-type b/nixos-modules/partition-type
new file mode 100644
index 0000000..2cca25d
--- /dev/null
+++ b/nixos-modules/partition-type
@@ -0,0 +1 @@
+"24b69406-18a1-428d-908e-d21a1437122c"
diff --git a/nixos-modules/vbox-host.dhall b/nixos-modules/vbox-host.dhall
new file mode 100644
index 0000000..de23307
--- /dev/null
+++ b/nixos-modules/vbox-host.dhall
@@ -0,0 +1,509 @@
+-- SPDX-License-Identifier: CC0-1.0
+
+let Genode = env:DHALL_GENODE
+
+let Prelude = Genode.Prelude
+
+let XML = Prelude.XML
+
+let Init = Genode.Init
+
+let Child = Init.Child
+
+let Resources = Init.Resources
+
+let ServiceRoute = Init.ServiceRoute
+
+let label =
+ λ(label : Text)
+ → { local = label, route = label } : Child.Attributes.Label
+
+let inlineConfig =
+ λ(name : Text)
+ → λ(config : Init.Config.Type)
+ → XML.element
+ { name = "inline"
+ , attributes = toMap { name = name }
+ , content =
+ Prelude.Optional.toList XML.Type (Init.Config.toXML config)
+ }
+
+let Vfs/inline =
+ λ(name : Text)
+ → λ(body : Text)
+ → XML.element
+ { name = "inline"
+ , attributes = XML.emptyAttributes
+ , content = XML.Text body
+ }
+
+let rootInit =
+ λ ( params
+ : { vdiFilename : Text
+ , vdiUuid : Text
+ , memorySize : Natural
+ , vmName : Text
+ }
+ )
+ → let vboxConfig =
+ ''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ''
+
+ in Init::{
+ , routes = [ ServiceRoute.child "Timer" "timer" ]
+ , children = toMap
+ { timer =
+ Child.flat
+ Child.Attributes::{
+ , binary = "timer_drv"
+ , provides = [ "Timer" ]
+ }
+ , rtc =
+ Child.flat
+ Child.Attributes::{
+ , binary = "rtc_drv"
+ , provides = [ "Rtc" ]
+ , routes =
+ [ ServiceRoute.parent "IO_PORT"
+ , ServiceRoute.parent "IO_MEM"
+ ]
+ }
+ , acpi_drv =
+ Child.flat
+ Child.Attributes::{
+ , binary = "acpi_drv"
+ , priority = 1
+ , resources = Resources::{
+ , caps = 350
+ , ram = Genode.units.MiB 4
+ }
+ , romReports = [ label "acpi", label "smbios_table" ]
+ , routes = [ ServiceRoute.parent "IO_MEM" ]
+ }
+ , platform_drv =
+ Child.flat
+ Child.Attributes::{
+ , binary = "platform_drv"
+ , resources = Resources::{
+ , caps = 400
+ , ram = Genode.units.MiB 4
+ , constrainPhys = True
+ }
+ , reportRoms = [ label "acpi" ]
+ , romReports = [ label "pci" ]
+ , provides = [ "Acpi", "Platform" ]
+ , routes =
+ [ ServiceRoute.parent "IRQ"
+ , ServiceRoute.parent "IO_MEM"
+ , ServiceRoute.parent "IO_PORT"
+ , ServiceRoute.parentLabel
+ "ROM"
+ (Some "system")
+ (Some "system")
+ ]
+ , config = Init.Config::{
+ , attributes = toMap { system = "yes" }
+ , content =
+ [ XML.text
+ ''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ''
+ ]
+ }
+ }
+ , framebuffer =
+ Child.flat
+ Child.Attributes::{
+ , binary = "vesa_fb_drv"
+ , provides = [ "Framebuffer" ]
+ , resources = Init.Resources::{
+ , caps = 256
+ , ram = Genode.units.MiB 16
+ }
+ , routes =
+ [ ServiceRoute.parent "IO_MEM"
+ , ServiceRoute.parent "IO_PORT"
+ , ServiceRoute.childLabel
+ "Platform"
+ "platform_drv"
+ (None Text)
+ (Some "vesa_fb_drv")
+ ]
+ }
+ , input_filter =
+ Child.flat
+ Child.Attributes::{
+ , binary = "input_filter"
+ , config = Init.Config::{
+ , content =
+ [ XML.leaf
+ { name = "input"
+ , attributes = toMap { label = "ps2" }
+ }
+ , XML.element
+ { name = "output"
+ , attributes = XML.emptyAttributes
+ , content =
+ [ XML.element
+ { name = "chargen"
+ , attributes = XML.emptyAttributes
+ , content =
+ [ XML.element
+ { name = "remap"
+ , attributes = XML.emptyAttributes
+ , content =
+ [ remap
+ "KEY_LEFTMETA"
+ "KEY_SCREEN"
+ , XML.leaf
+ { name = "input"
+ , attributes = toMap
+ { name = "ps2" }
+ }
+ ]
+ }
+ , XML.element
+ { name = "mod1"
+ , attributes = XML.emptyAttributes
+ , content =
+ [ key "KEY_LEFTSHIFT"
+ , key "KEY_RIGHTSHIFT"
+ ]
+ }
+ , XML.element
+ { name = "mod2"
+ , attributes = XML.emptyAttributes
+ , content =
+ [ key "KEY_LEFTCTRL"
+ , key "KEY_RIGHTCTRL"
+ ]
+ }
+ , XML.element
+ { name = "mod3"
+ , attributes = XML.emptyAttributes
+ , content = [ key "KEY_RIGHTALT" ]
+ }
+ ]
+ # ./workman.map.dhall
+ }
+ ]
+ }
+ ]
+ }
+ , provides = [ "Input" ]
+ , routes =
+ [ ServiceRoute.parentLabel
+ "ROM"
+ (Some "config")
+ (Some "config -> input_filter.config")
+ , ServiceRoute.childLabel
+ "Input"
+ "ps2_drv"
+ (Some "ps2")
+ (None Text)
+ ]
+ }
+ , ps2_drv =
+ Child.flat
+ Child.Attributes::{
+ , binary = "ps2_drv"
+ , provides = [ "Input" ]
+ , routes =
+ [ ServiceRoute.childLabel
+ "Platform"
+ "platform_drv"
+ (None Text)
+ (Some "ps2_drv")
+ ]
+ }
+ , nitpicker =
+ Child.flat
+ Child.Attributes::{
+ , binary = "nitpicker"
+ , config = Init.Config::{
+ , content =
+ [ XML.text
+ ''
+
+
+
+
+ ''
+ ]
+ }
+ , provides = [ "Nitpicker" ]
+ , routes =
+ [ ServiceRoute.child "Framebuffer" "framebuffer"
+ , ServiceRoute.child "Input" "input_filter"
+ ]
+ }
+ , pointer =
+ Child.flat
+ Child.Attributes::{
+ , binary = "pointer"
+ , routes = [ ServiceRoute.child "Nitpicker" "nitpicker" ]
+ }
+ , log_console =
+ Init.toChild
+ ./log-console.dhall
+ Init.Attributes::{
+ , routes = [ ServiceRoute.child "Nitpicker" "nitpicker" ]
+ }
+ , block =
+ Child.flat
+ Child.Attributes::{
+ , binary = "ahci_drv"
+ , config = Init.Config::{
+ , content =
+ [ Genode.Prelude.XML.leaf
+ { name = "default-policy"
+ , attributes = toMap
+ { device = "0", writeable = "yes" }
+ }
+ ]
+ }
+ , provides = [ "Block" ]
+ , resources = Init.Resources::{
+ , caps = 256
+ , ram = Genode.units.MiB 10
+ }
+ , routes =
+ [ ServiceRoute.child
+ "Platform"
+ "platform_drv"
+ (None Text)
+ (Some "ahci_drv")
+ ]
+ }
+ , block_partitions =
+ Child.flat
+ Child.Attributes::{
+ , binary = "part_block"
+ , config = Init.Config::{
+ , content =
+ Prelude.List.map
+ Natural
+ XML.Type
+ ( λ(i : Natural)
+ → XML.leaf
+ { name = "policy"
+ , attributes =
+ let partition =
+ Prelude.Natural.show (i + 1)
+
+ in toMap
+ { label_suffix = " ${partition}"
+ , partition = partition
+ , writeable = "yes"
+ }
+ }
+ )
+ (Prelude.Natural.enumerate 128)
+ # [ XML.leaf
+ { name = "report"
+ , attributes = toMap { partitions = "yes" }
+ }
+ ]
+ }
+ , resources = Resources::{
+ , caps = 256
+ , ram = Genode.units.MiB 8
+ }
+ , provides = [ "Block" ]
+ , routes =
+ [ ServiceRoute.child "Block" "block"
+ , ServiceRoute.child "Report" "block_router"
+ ]
+ }
+ , block_router =
+ Child.flat
+ Child.Attributes::{
+ , binary = "block_router"
+ , config = Init.Config::{
+ , attributes = toMap { verbose = "yes" }
+ , content =
+ [ XML.element
+ { name = "default-policy"
+ , attributes = XML.emptyAttributes
+ , content =
+ [ XML.leaf
+ { name = "partition"
+ , attributes = toMap
+ { type = ./partition-type
+ , writeable = "yes"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ , resources = Resources::{
+ , caps = 256
+ , ram = Genode.units.MiB 8
+ }
+ , provides = [ "Block", "Report" ]
+ , routes =
+ [ ServiceRoute.child "Block" "block_partitions" ]
+ }
+ , file_system =
+ Child.flat
+ Child.Attributes::{
+ , binary = "vfs"
+ , config = Init.Config::{
+ , content =
+ [ XML.element
+ { name = "vfs"
+ , attributes = XML.emptyAttributes
+ , content =
+ [ XML.leaf
+ { name = "rump"
+ , attributes = toMap
+ { fs = "ext2fs"
+ , writeable = "yes"
+ , ram = "8M"
+ }
+ }
+ ]
+ }
+ , XML.leaf
+ { name = "default-policy"
+ , attributes = toMap { root = "/" }
+ }
+ ]
+ }
+ , provides = [ "File_system" ]
+ , resources = Init.Resources::{
+ , caps = 256
+ , ram = Genode.units.MiB 12
+ }
+ , routes = [ ServiceRoute.child "Block" "block_router" ]
+ }
+ , vbox =
+ Child.flat
+ Child.Attributes::{
+ , binary = "virtualbox5"
+ , config = Init.Config::{
+ , attributes = toMap
+ { vbox_file = "nixos.vbox", vm_name = "nixos" }
+ , content =
+ [ XML.leaf
+ { name = "libc"
+ , attributes = toMap
+ { stdout = "/dev/log"
+ , stderr = "/dev/log"
+ , rtc = "/dev/rtc"
+ }
+ }
+ , XML.element
+ { name = "vfs"
+ , attributes = XML.emptyAttributes
+ , content =
+ let tag =
+ λ(name : Text)
+ → XML.leaf
+ { name = name
+ , attributes = XML.emptyAttributes
+ }
+
+ in [ XML.element
+ { name = "dev"
+ , attributes = XML.emptyAttributes
+ , content = [ tag log, tag rtc ]
+ }
+ , Vfs/inline "nixos.vbox" vboxConfig
+ , tag "file_system"
+ ]
+ }
+ ]
+ }
+ , resources = Resources::{
+ , caps = 1024
+ , ram = Genode.units.MiB 256
+ }
+ , routes =
+ [ ServiceRoute.parent "VM"
+ , ServiceRoute.child "Nitpicker" "nitpicker"
+ , ServiceRoute.child "File_system" "file_system"
+ , ServiceRoute.child "Rtc" "rtc"
+ ]
+ }
+ }
+ }
+
+in rootInit
diff --git a/nixos-modules/vbox.nix b/nixos-modules/vbox.nix
new file mode 100644
index 0000000..fb7b268
--- /dev/null
+++ b/nixos-modules/vbox.nix
@@ -0,0 +1,76 @@
+{ self }:
+let lib' = self.lib.x86_64-linux-x86_64-genode;
+in { config, lib, pkgs, ... }:
+
+let
+ buildBootDescription =
+ self.legacyPackages.x86_64-linux.callPackage ./buildBootDescription.nix {
+ lib = lib';
+ };
+
+ genodeParams = lib'.runDhallCommand "params.dhall" { } ''
+ vdiUuid=$(${pkgs.virtualbox}/bin/VBoxManage showmediuminfo ${config.system.build.virtualBoxVDI}/nixos.vdi | awk '/^UUID:/ {print $2}')
+ dhall > $out << EOD
+ { vdiFilename = "nixos.vdi"
+ , vidUuid = "$vdiUuid"
+ , memorySize = ${toString config.virtualbox.memorySize}
+ , vmName = "${config.virtualbox.vmName}"
+ }
+ EOD
+ '';
+
+ bootDescription = buildBootDescription {
+ name = "boot-description";
+ initConfig = "./vbox-host.dhall ${genodeParams}";
+ imageInputs = (map pkgs.genodeSources.depot [
+ "acpi_drv"
+ "ahci_drv"
+ "init"
+ "input_filter"
+ "libc"
+ "nitpicker"
+ "part_block"
+ "platform_drv"
+ "ps2_drv"
+ "report_rom"
+ "rtc_drv"
+ "vesa_drv"
+ "vfs"
+ ]) ++ (map pkgs.genodeSources.make [ "drivers/input/dummy" ])
+ ++ [ pkgs.base-nova pkgs.block_router ];
+ extraBinaries = [
+ "ld.lib.so"
+ "libc.lib.so"
+ "libm.lib.so"
+ "rump.lib.so"
+ "rump_fs.lib.so"
+ "vfs_rump.lib.so"
+ ];
+ };
+
+ novaImage = lib'.novaImage "stage0" { } bootDescription;
+
+ # add system.build.virtualBoxVDI/nixos.vdi to disk image
+
+ genodeDiskImage = with pkgs;
+ runCommand "genode.disk" {
+ nativeBuildInputs = [ e2fsprogs gptfdisk lzip vmTools.qemu ];
+ } ''
+ qemu-img create -f raw fs.raw 16M
+ qemu-img create -f raw zero.raw 1M
+
+ mke2fs fs.raw
+
+ cat zero.raw fs.raw zero.raw > gpt.raw
+
+ sgdisk gpt.raw --new=partnum 1:34:-34 \
+ --typecode=1:${import ./partition-type} \
+ --change-name=1:genode
+
+ mkdir -p $out
+ lzip -c gpt.raw > "$out/genode-nixos-${config.system.nixos.label}.disk"
+ '';
+
+in {
+ system.build.genodeDiskImage = novaImage;
+}
diff --git a/nixos-modules/workman.map.dhall b/nixos-modules/workman.map.dhall
new file mode 100644
index 0000000..90885a1
--- /dev/null
+++ b/nixos-modules/workman.map.dhall
@@ -0,0 +1,245 @@
+let Genode = ./Genode.dhall
+
+let Prelude = Genode.Prelude
+
+let XML = Prelude.XML
+
+let Key = < Ascii : Natural | Char : Text | Code : Natural > : Type
+
+let Map =
+ { Type =
+ { keys : Prelude.Map.Type Text Key
+ , mod1 : Bool
+ , mod2 : Bool
+ , mod3 : Bool
+ , mod4 : Bool
+ }
+ , default = { mod1 = False, mod2 = False, mod3 = False, mod4 = False }
+ }
+
+let boolToAttr = λ(_ : Bool) → if _ then "yes" else "no"
+
+let keyToXML =
+ λ(x : Prelude.Map.Entry Text Key)
+ → XML.leaf
+ { name = "key"
+ , attributes =
+ [ merge
+ { Ascii =
+ λ(_ : Natural)
+ → { mapKey = "ascii", mapValue = Prelude.Natural.show _ }
+ , Char = λ(_ : Text) → { mapKey = "char", mapValue = _ }
+ , Code =
+ λ(_ : Natural)
+ → { mapKey = "code", mapValue = Prelude.Natural.show _ }
+ }
+ x.mapValue
+ , { mapKey = "name", mapValue = x.mapKey }
+ ]
+ }
+
+let mapToXML =
+ λ(map : Map.Type)
+ → XML.element
+ { name = "map"
+ , attributes = toMap
+ { mod1 = boolToAttr map.mod1
+ , mod2 = boolToAttr map.mod2
+ , mod3 = boolToAttr map.mod3
+ , mod4 = boolToAttr map.mod4
+ }
+ , content =
+ Prelude.List.map
+ (Prelude.Map.Entry Text Key)
+ XML.Type
+ keyToXML
+ map.keys
+ }
+
+let workman =
+ [ Map::{
+ , keys = toMap
+ { KEY_ESC = Key.Ascii 27
+ , KEY_1 = Key.Char "1"
+ , KEY_2 = Key.Char "2"
+ , KEY_3 = Key.Char "3"
+ , KEY_4 = Key.Char "4"
+ , KEY_5 = Key.Char "5"
+ , KEY_6 = Key.Char "6"
+ , KEY_7 = Key.Char "7"
+ , KEY_8 = Key.Char "8"
+ , KEY_9 = Key.Char "9"
+ , KEY_0 = Key.Char "0"
+ , KEY_MINUS = Key.Char "-"
+ , KEY_EQUAL = Key.Char "="
+ , KEY_BACKSPACE = Key.Ascii 8
+ , KEY_TAB = Key.Ascii 9
+ , KEY_Q = Key.Char "q"
+ , KEY_W = Key.Char "d"
+ , KEY_E = Key.Char "r"
+ , KEY_R = Key.Char "w"
+ , KEY_T = Key.Char "b"
+ , KEY_Y = Key.Char "j"
+ , KEY_U = Key.Char "f"
+ , KEY_I = Key.Char "u"
+ , KEY_O = Key.Char "p"
+ , KEY_P = Key.Char ";"
+ , KEY_LEFTBRACE = Key.Char "["
+ , KEY_RIGHTBRACE = Key.Char "]"
+ , KEY_ENTER = Key.Ascii 10
+ , KEY_A = Key.Char "a"
+ , KEY_S = Key.Char "s"
+ , KEY_D = Key.Char "h"
+ , KEY_F = Key.Char "t"
+ , KEY_G = Key.Char "g"
+ , KEY_H = Key.Char "y"
+ , KEY_J = Key.Char "n"
+ , KEY_K = Key.Char "e"
+ , KEY_L = Key.Char "o"
+ , KEY_SEMICOLON = Key.Char "i"
+ , KEY_APOSTROPHE = Key.Char "'"
+ , KEY_GRAVE = Key.Char "`"
+ , KEY_BACKSLASH = Key.Ascii 92
+ , KEY_Z = Key.Char "z"
+ , KEY_X = Key.Char "x"
+ , KEY_C = Key.Char "m"
+ , KEY_V = Key.Char "c"
+ , KEY_B = Key.Char "v"
+ , KEY_N = Key.Char "k"
+ , KEY_M = Key.Char "l"
+ , KEY_COMMA = Key.Char ","
+ , KEY_DOT = Key.Char "."
+ , KEY_SLASH = Key.Char "/"
+ , KEY_SPACE = Key.Char " "
+ , KEY_KP7 = Key.Char "7"
+ , KEY_KP8 = Key.Char "8"
+ , KEY_KP9 = Key.Char "9"
+ , KEY_KPMINUS = Key.Char "-"
+ , KEY_KP4 = Key.Char "4"
+ , KEY_KP5 = Key.Char "5"
+ , KEY_KP6 = Key.Char "6"
+ , KEY_KPPLUS = Key.Char "+"
+ , KEY_KP1 = Key.Char "1"
+ , KEY_KP2 = Key.Char "2"
+ , KEY_KP3 = Key.Char "3"
+ , KEY_KP0 = Key.Char "0"
+ , KEY_KPDOT = Key.Char "."
+ , KEY_KPENTER = Key.Ascii 10
+ , KEY_KPSLASH = Key.Char "/"
+ }
+ }
+ , Map::{
+ , mod1 = True
+ , keys = toMap
+ { KEY_1 = Key.Char "!"
+ , KEY_2 = Key.Char "@"
+ , KEY_3 = Key.Char "#"
+ , KEY_4 = Key.Char "\$"
+ , KEY_5 = Key.Char "%"
+ , KEY_6 = Key.Char "^"
+ , KEY_7 = Key.Ascii 38
+ , KEY_8 = Key.Char "*"
+ , KEY_9 = Key.Char "("
+ , KEY_0 = Key.Char ")"
+ , KEY_MINUS = Key.Char "_"
+ , KEY_EQUAL = Key.Char "+"
+ , KEY_Q = Key.Char "Q"
+ , KEY_W = Key.Char "D"
+ , KEY_E = Key.Char "R"
+ , KEY_R = Key.Char "W"
+ , KEY_T = Key.Char "B"
+ , KEY_Y = Key.Char "J"
+ , KEY_U = Key.Char "F"
+ , KEY_I = Key.Char "U"
+ , KEY_O = Key.Char "P"
+ , KEY_P = Key.Char ":"
+ , KEY_LEFTBRACE = Key.Char "{"
+ , KEY_RIGHTBRACE = Key.Char "}"
+ , KEY_A = Key.Char "A"
+ , KEY_S = Key.Char "S"
+ , KEY_D = Key.Char "H"
+ , KEY_F = Key.Char "T"
+ , KEY_G = Key.Char "G"
+ , KEY_H = Key.Char "Y"
+ , KEY_J = Key.Char "N"
+ , KEY_K = Key.Char "E"
+ , KEY_L = Key.Char "O"
+ , KEY_SEMICOLON = Key.Char "I"
+ , KEY_APOSTROPHE = Key.Ascii 34
+ , KEY_GRAVE = Key.Char "~"
+ , KEY_BACKSLASH = Key.Char "|"
+ , KEY_Z = Key.Char "Z"
+ , KEY_X = Key.Char "X"
+ , KEY_C = Key.Char "M"
+ , KEY_V = Key.Char "C"
+ , KEY_B = Key.Char "V"
+ , KEY_N = Key.Char "K"
+ , KEY_M = Key.Char "L"
+ , KEY_COMMA = Key.Ascii 60
+ , KEY_DOT = Key.Ascii 62
+ , KEY_SLASH = Key.Char "?"
+ }
+ }
+ , Map::{
+ , mod2 = True
+ , keys = toMap
+ { KEY_A = Key.Ascii 1
+ , KEY_B = Key.Ascii 22
+ , KEY_C = Key.Ascii 13
+ , KEY_D = Key.Ascii 8
+ , KEY_E = Key.Ascii 18
+ , KEY_F = Key.Ascii 20
+ , KEY_G = Key.Ascii 7
+ , KEY_H = Key.Ascii 25
+ , KEY_I = Key.Ascii 21
+ , KEY_J = Key.Ascii 14
+ , KEY_K = Key.Ascii 5
+ , KEY_L = Key.Ascii 15
+ , KEY_M = Key.Ascii 12
+ , KEY_N = Key.Ascii 11
+ , KEY_O = Key.Ascii 16
+ , KEY_P = Key.Ascii 9
+ , KEY_Q = Key.Ascii 17
+ , KEY_R = Key.Ascii 23
+ , KEY_S = Key.Ascii 19
+ , KEY_T = Key.Ascii 2
+ , KEY_U = Key.Ascii 6
+ , KEY_V = Key.Ascii 3
+ , KEY_W = Key.Ascii 4
+ , KEY_X = Key.Ascii 24
+ , KEY_Y = Key.Ascii 10
+ , KEY_Z = Key.Ascii 26
+ }
+ }
+ , Map::{
+ , mod3 = True
+ , keys = toMap
+ { KEY_4 = Key.Code 8364
+ , KEY_A = Key.Code 228
+ , KEY_S = Key.Code 223
+ , KEY_I = Key.Code 252
+ , KEY_DOT = Key.Code 8230
+ , KEY_K = Key.Code 235
+ , KEY_C = Key.Code 181
+ , KEY_L = Key.Code 246
+ }
+ }
+ , Map::{
+ , mod1 = True
+ , mod3 = True
+ , keys = toMap
+ { KEY_0 = Key.Code 8320
+ , KEY_1 = Key.Code 8321
+ , KEY_2 = Key.Code 8322
+ , KEY_3 = Key.Code 8323
+ , KEY_4 = Key.Code 8324
+ , KEY_5 = Key.Code 8325
+ , KEY_6 = Key.Code 8326
+ , KEY_7 = Key.Code 8327
+ , KEY_8 = Key.Code 8328
+ , KEY_9 = Key.Code 8329
+ }
+ }
+ ]
+
+in Prelude.List.map Map.Type XML.Type mapToXML workman