2
0
Fork 0

WiP! nixos-module

This commit is contained in:
Ehmry - 2020-05-11 15:42:13 +05:30
parent f289a4b88f
commit b99986c6cf
7 changed files with 897 additions and 0 deletions

View File

@ -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; };
};
}

View File

@ -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))
}
])
}
''

View File

@ -0,0 +1,5 @@
{ self }:
{
vbox = import ./vbox.nix { inherit self; };
}

View File

@ -0,0 +1 @@
"24b69406-18a1-428d-908e-d21a1437122c"

View File

@ -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 =
''
<?xml version="1.0"?>
<VirtualBox xmlns="http://www.virtualbox.org/" version="1.14-freebsd">
<Machine uuid="{37ab43a5-38d8-4491-93f5-5b0b077f5c32}" name="${params.vmName}" OSType="Linux26_64" snapshotFolder="Snapshots" lastStateChange="2018-01-23T18:40:00Z">
<MediaRegistry>
<HardDisks>
<HardDisk uuid="{${params.vdiUuid}}" location="${params.vdiFilename}" format="VDI" type="Normal"/>
</HardDisks>
<DVDImages/>
</MediaRegistry>
<Hardware>
<CPU count="1">
<PAE enabled="true"/>
<LongMode enabled="true"/>
<HardwareVirtExLargePages enabled="false"/>
</CPU>
<Memory RAMSize="${Prelude.Natural.show params.memorySize}"/>
<HID Pointing="USBTablet"/>
<Display VRAMSize="20"/>
<RemoteDisplay enabled="false"/>
<BIOS>
<IOAPIC enabled="true"/>
</BIOS>
<USB/>
<Network/>
<UART>
<Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/>
<Port slot="1" enabled="false" IOBase="0x2f8" IRQ="3" hostMode="Disconnected"/>
</UART>
<LPT>
<Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/>
<Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/>
</LPT>
<AudioAdapter controller="HDA" driver="OSS" enabled="false"/>
<RTC localOrUTC="UTC"/>
<SharedFolders/>
</Hardware>
<StorageControllers>
<StorageController name="SATA" type="AHCI" PortCount="4" useHostIOCache="true" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3">
<AttachedDevice type="HardDisk" port="0" device="0">
<Image uuid="{${params.vdiUuid}}"/>
</AttachedDevice>
</StorageController>
</StorageControllers>
</Machine>
</VirtualBox>
''
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
''
<report pci="yes"/>
<policy label_suffix="ps2_drv">
<device name="PS2"/>
</policy>
<policy label_suffix="vesa_fb_drv">
<pci class="VGA"/>
</policy>
<policy label_suffix="ahci_drv">
<pci class="AHCI"/>
</policy>
<policy label_suffix="nvme_drv">
<pci class="NVME"/>
</policy>
<policy label_suffix="usb_drv">
<pci class="USB"/>
</policy>
<policy label_suffix="intel_fb_drv">
<pci class="VGA"/>
<pci bus="0" device="0" function="0"/>
<pci class="ISABRIDGE"/>
</policy>
<policy label_suffix="-&gt; wifi">
<pci class="WIFI"/>
</policy>
<policy label_suffix="-&gt; nic">
<pci class="ETHERNET"/>
</policy>
<policy label_suffix="-&gt; audio">
<pci class="AUDIO"/>
<pci class="HDAUDIO"/>
</policy>
<policy label="acpica"/>
''
]
}
}
, 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
''
<domain name="pointer" layer="1" content="client" label="no" origin="pointer" />
<domain name="default" layer="2" content="client" label="yes" hover="always" focus="click"/>
<policy label_prefix="pointer" domain="pointer"/>
<default-policy domain="default"/>
''
]
}
, 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

76
nixos-modules/vbox.nix Normal file
View File

@ -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;
}

View File

@ -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