Compare commits
	
		
			1 Commits
		
	
	
		
			pureos/1.1
			...
			hintclear
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 914c5d4940 | 
							
								
								
									
										52
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										52
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -59,9 +59,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "cc"
 | 
					name = "cc"
 | 
				
			||||||
version = "1.0.67"
 | 
					version = "1.0.65"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
 | 
					checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "clap"
 | 
					name = "clap"
 | 
				
			||||||
@ -76,9 +76,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "dtoa"
 | 
					name = "dtoa"
 | 
				
			||||||
version = "0.4.8"
 | 
					version = "0.4.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
 | 
					checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "fragile"
 | 
					name = "fragile"
 | 
				
			||||||
@ -265,15 +265,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "libc"
 | 
					name = "libc"
 | 
				
			||||||
version = "0.2.94"
 | 
					version = "0.2.80"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
 | 
					checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "linked-hash-map"
 | 
					name = "linked-hash-map"
 | 
				
			||||||
version = "0.5.4"
 | 
					version = "0.5.3"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
 | 
					checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "maplit"
 | 
					name = "maplit"
 | 
				
			||||||
@ -326,18 +326,18 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "proc-macro2"
 | 
					name = "proc-macro2"
 | 
				
			||||||
version = "1.0.26"
 | 
					version = "1.0.24"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
 | 
					checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "unicode-xid",
 | 
					 "unicode-xid",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "quote"
 | 
					name = "quote"
 | 
				
			||||||
version = "1.0.9"
 | 
					version = "1.0.7"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
 | 
					checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@ -353,9 +353,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "regex-syntax"
 | 
					name = "regex-syntax"
 | 
				
			||||||
version = "0.6.25"
 | 
					version = "0.6.21"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 | 
					checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "rs"
 | 
					name = "rs"
 | 
				
			||||||
@ -380,18 +380,18 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde"
 | 
					name = "serde"
 | 
				
			||||||
version = "1.0.126"
 | 
					version = "1.0.117"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
 | 
					checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "serde_derive",
 | 
					 "serde_derive",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde_derive"
 | 
					name = "serde_derive"
 | 
				
			||||||
version = "1.0.126"
 | 
					version = "1.0.117"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
 | 
					checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
@ -400,9 +400,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde_yaml"
 | 
					name = "serde_yaml"
 | 
				
			||||||
version = "0.8.17"
 | 
					version = "0.8.14"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
 | 
					checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "dtoa",
 | 
					 "dtoa",
 | 
				
			||||||
 "linked-hash-map",
 | 
					 "linked-hash-map",
 | 
				
			||||||
@ -412,9 +412,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "syn"
 | 
					name = "syn"
 | 
				
			||||||
version = "1.0.72"
 | 
					version = "1.0.48"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
 | 
					checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
@ -438,9 +438,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "unicode-xid"
 | 
					name = "unicode-xid"
 | 
				
			||||||
version = "0.2.2"
 | 
					version = "0.2.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
 | 
					checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "winapi"
 | 
					name = "winapi"
 | 
				
			||||||
@ -476,9 +476,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "yaml-rust"
 | 
					name = "yaml-rust"
 | 
				
			||||||
version = "0.4.5"
 | 
					version = "0.4.4"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
 | 
					checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "linked-hash-map",
 | 
					 "linked-hash-map",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ if out_path:
 | 
				
			|||||||
    i = args.index(out_path)
 | 
					    i = args.index(out_path)
 | 
				
			||||||
    args.pop(i)    
 | 
					    args.pop(i)    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
subprocess.run(['sh', "{}/cargo.sh".format(source_dir.as_posix()), 'build']
 | 
					subprocess.run(['sh', "{}/cargo.sh".format(shlex.quote(source_dir.as_posix())), 'build']
 | 
				
			||||||
    + args,
 | 
					    + args,
 | 
				
			||||||
    check=True)
 | 
					    check=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,7 +43,7 @@ if out_path:
 | 
				
			|||||||
    out_basename = out_path.name
 | 
					    out_basename = out_path.name
 | 
				
			||||||
    filename = filename or out_basename
 | 
					    filename = filename or out_basename
 | 
				
			||||||
    subprocess.run(['cp', '-a',
 | 
					    subprocess.run(['cp', '-a',
 | 
				
			||||||
        './{}/{}'.format(binary_dir, filename),
 | 
					        './{}/{}'.format(shlex.quote(binary_dir), shlex.quote(filename)),
 | 
				
			||||||
        out_path],
 | 
					        out_path],
 | 
				
			||||||
        check=True)
 | 
					        check=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,87 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 35.33, height: 52 }
 | 
					 | 
				
			||||||
    altline: { width: 52.67, height: 52 }
 | 
					 | 
				
			||||||
    wide: { width: 62, height: 52 }
 | 
					 | 
				
			||||||
    spaceline: { width: 99.67, height: 52 }
 | 
					 | 
				
			||||||
    special: { width: 44, height: 52 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "q w e r t y u i o p"
 | 
					 | 
				
			||||||
        - "a s d f g h j k l ç"
 | 
					 | 
				
			||||||
        - "Shift_L   z x c v b n m  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers show_eschars preferences         space        ? period Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "Q W E R T Y U I O P"
 | 
					 | 
				
			||||||
        - "A S D F G H J K L Ç"
 | 
					 | 
				
			||||||
        - "Shift_L   Z X C V B N M  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers show_eschars preferences         space        ¿ period Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "@ # € % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! = BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters show_eschars preferences         space        ? period Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ $ ¥ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters show_eschars preferences         space        ? period Return"
 | 
					 | 
				
			||||||
    eschars:
 | 
					 | 
				
			||||||
        - "á é í ó ú Á É Í Ó Ú"
 | 
					 | 
				
			||||||
        - "à è ì ò ù À È Ì Ò Ù"
 | 
					 | 
				
			||||||
        - "show_numbers ü ç ï Ü Ç Ï ¡  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters show_eschars preferences         space        « » Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: "erase"
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: "show_prefs"
 | 
					 | 
				
			||||||
        outline: "default"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "abc"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    show_eschars:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "eschars"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "àÀ"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "default"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ -1,71 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 40, height: 60 }
 | 
					 | 
				
			||||||
    altline: { width: 56, height: 60 }
 | 
					 | 
				
			||||||
    wide: { width: 62, height: 60 }
 | 
					 | 
				
			||||||
    spaceline: { width: 142, height: 60 }
 | 
					 | 
				
			||||||
    special: { width: 44, height: 60 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "' - ק  ר א ט ו ן ם פ"
 | 
					 | 
				
			||||||
        - "ש ד ג כ ע י ח ל ך ף"
 | 
					 | 
				
			||||||
        - "ז ס ב ה נ מ צ ת ץ  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers comma preferences         space        period Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "@ # ₪ % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ € $ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "default"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: erase
 | 
					 | 
				
			||||||
    comma:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: ","
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: show_prefs
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "ABC"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ -1,78 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 35.33, height: 52 }
 | 
					 | 
				
			||||||
    altline: { width: 52.67, height: 52 }
 | 
					 | 
				
			||||||
    wide: { width: 62, height: 52 }
 | 
					 | 
				
			||||||
    spaceline: { width: 142, height: 52 }
 | 
					 | 
				
			||||||
    special: { width: 44, height: 52 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "ض ص ق ف غ ع ه خ ح ج"
 | 
					 | 
				
			||||||
        - "ش س ی ب ل ا ت ن م ک"
 | 
					 | 
				
			||||||
        - "Shift_L   ظ ط ز ر ذ د و  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "پ { } [ ] ّ   َ   ِ  ُ چ"
 | 
					 | 
				
			||||||
        - "ؤ ئ ي إ أ آ ة » « گ"
 | 
					 | 
				
			||||||
        - "Shift_L  ك ٓ ژ ء > < ؟  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰"
 | 
					 | 
				
			||||||
        - "@ # ﷼ % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ؛ ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ € ¥ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: erase
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: show_prefs
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "ABC"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
@ -1,78 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 54, height: 42 }
 | 
					 | 
				
			||||||
    altline: { width: 81, height: 42 }
 | 
					 | 
				
			||||||
    wide: { width: 108, height: 42 }
 | 
					 | 
				
			||||||
    spaceline: { width: 216, height: 42 }
 | 
					 | 
				
			||||||
    special: { width: 54, height: 42 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "ض ص ق ف غ ع ه خ ح ج"
 | 
					 | 
				
			||||||
        - "ش س ی ب ل ا ت ن م ک"
 | 
					 | 
				
			||||||
        - "Shift_L   ظ ط ز ر ذ د و  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "پ { } [ ] ّ   َ   ِ  ُ چ"
 | 
					 | 
				
			||||||
        - "ؤ ئ ي إ أ آ ة » « گ"
 | 
					 | 
				
			||||||
        - "Shift_L  ك ٓ ژ ء > < ؟  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰"
 | 
					 | 
				
			||||||
        - "@ # ﷼ % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ؛ ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ € ¥ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: "erase"
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: "show_prefs"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "ABC"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    ".":
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
@ -22,7 +22,7 @@ views:
 | 
				
			|||||||
    numbers:
 | 
					    numbers:
 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					        - "1 2 3 4 5 6 7 8 9 0"
 | 
				
			||||||
        - "@ # € % & - _ + ( )"
 | 
					        - "@ # € % & - _ + ( )"
 | 
				
			||||||
        - "show_symbols   , \" ' : ; ! =  BackSpace"
 | 
					        - "show_symbols   , \" ' colon ; ! =  BackSpace"
 | 
				
			||||||
        - "show_letters show_eschars preferences         space        ? . Return"
 | 
					        - "show_letters show_eschars preferences         space        ? . Return"
 | 
				
			||||||
    symbols:
 | 
					    symbols:
 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					        - "~ ` | · √ π τ ÷ × ¶"
 | 
				
			||||||
@ -86,4 +86,7 @@ buttons:
 | 
				
			|||||||
        outline: "altline"
 | 
					        outline: "altline"
 | 
				
			||||||
        icon: "key-enter"
 | 
					        icon: "key-enter"
 | 
				
			||||||
        keysym: "Return"
 | 
					        keysym: "Return"
 | 
				
			||||||
 | 
					    colon:
 | 
				
			||||||
 | 
					        label: ":"
 | 
				
			||||||
 | 
					    "\"":
 | 
				
			||||||
 | 
					        keysym: "quotedbl"
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,7 @@ views:
 | 
				
			|||||||
    numbers:
 | 
					    numbers:
 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					        - "1 2 3 4 5 6 7 8 9 0"
 | 
				
			||||||
        - "@ # € % & - _ + ( )"
 | 
					        - "@ # € % & - _ + ( )"
 | 
				
			||||||
        - "show_symbols   , \" ' : ; ! ?  BackSpace"
 | 
					        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
				
			||||||
        - "show_letters show_eschars preferences         space        ? . Return"
 | 
					        - "show_letters show_eschars preferences         space        ? . Return"
 | 
				
			||||||
    symbols:
 | 
					    symbols:
 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					        - "~ ` | · √ π τ ÷ × ¶"
 | 
				
			||||||
@ -84,4 +84,7 @@ buttons:
 | 
				
			|||||||
        outline: "altline"
 | 
					        outline: "altline"
 | 
				
			||||||
        icon: "key-enter"
 | 
					        icon: "key-enter"
 | 
				
			||||||
        keysym: "Return"
 | 
					        keysym: "Return"
 | 
				
			||||||
 | 
					    colon:
 | 
				
			||||||
 | 
					        label: ":"
 | 
				
			||||||
 | 
					    "\"":
 | 
				
			||||||
 | 
					        keysym: "quotedbl"
 | 
				
			||||||
 | 
				
			|||||||
@ -438,7 +438,7 @@ buttons:
 | 
				
			|||||||
                unlock_view: "カタカナ"
 | 
					                unlock_view: "カタカナ"
 | 
				
			||||||
        outline: "altline"
 | 
					        outline: "altline"
 | 
				
			||||||
        label: "。"
 | 
					        label: "。"
 | 
				
			||||||
    # Buttons for Latin characters
 | 
					    # Buttons for Latin charachters
 | 
				
			||||||
    RSYM1:
 | 
					    RSYM1:
 | 
				
			||||||
        action:
 | 
					        action:
 | 
				
			||||||
            locking:
 | 
					            locking:
 | 
				
			||||||
 | 
				
			|||||||
@ -438,7 +438,7 @@ buttons:
 | 
				
			|||||||
                unlock_view: "カタカナ"
 | 
					                unlock_view: "カタカナ"
 | 
				
			||||||
        outline: "altline"
 | 
					        outline: "altline"
 | 
				
			||||||
        label: "。"
 | 
					        label: "。"
 | 
				
			||||||
    # Buttons for Latin characters
 | 
					    # Buttons for Latin charachters
 | 
				
			||||||
    RSYM1:
 | 
					    RSYM1:
 | 
				
			||||||
        action:
 | 
					        action:
 | 
				
			||||||
            locking:
 | 
					            locking:
 | 
				
			||||||
 | 
				
			|||||||
@ -52,8 +52,6 @@ buttons:
 | 
				
			|||||||
            locking:
 | 
					            locking:
 | 
				
			||||||
                lock_view: "upper_accents"
 | 
					                lock_view: "upper_accents"
 | 
				
			||||||
                unlock_view: "accents"
 | 
					                unlock_view: "accents"
 | 
				
			||||||
                looks_locked_from:
 | 
					 | 
				
			||||||
                    - "upper"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					        outline: "altline"
 | 
				
			||||||
        icon: "key-shift"
 | 
					        icon: "key-shift"
 | 
				
			||||||
    BackSpace:
 | 
					    BackSpace:
 | 
				
			||||||
@ -96,8 +94,6 @@ buttons:
 | 
				
			|||||||
            locking:
 | 
					            locking:
 | 
				
			||||||
                lock_view: "upper_accents"
 | 
					                lock_view: "upper_accents"
 | 
				
			||||||
                unlock_view: "upper"
 | 
					                unlock_view: "upper"
 | 
				
			||||||
                looks_locked_from:
 | 
					 | 
				
			||||||
                    - "accents"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					        outline: "altline"
 | 
				
			||||||
        label: "ĄĘ"
 | 
					        label: "ĄĘ"
 | 
				
			||||||
    period:
 | 
					    period:
 | 
				
			||||||
 | 
				
			|||||||
@ -147,6 +147,9 @@ buttons:
 | 
				
			|||||||
    Pause:
 | 
					    Pause:
 | 
				
			||||||
        outline: "action"
 | 
					        outline: "action"
 | 
				
			||||||
        keysym: "Pause"
 | 
					        keysym: "Pause"
 | 
				
			||||||
 | 
					    Menu:
 | 
				
			||||||
 | 
					        outline: "action"
 | 
				
			||||||
 | 
					        keysym: "Menu"
 | 
				
			||||||
    Break:
 | 
					    Break:
 | 
				
			||||||
        outline: "action"
 | 
					        outline: "action"
 | 
				
			||||||
        keysym: "Break"
 | 
					        keysym: "Break"
 | 
				
			||||||
@ -198,3 +201,4 @@ buttons:
 | 
				
			|||||||
        modifier: "Alt"
 | 
					        modifier: "Alt"
 | 
				
			||||||
        outline: "small"
 | 
					        outline: "small"
 | 
				
			||||||
        label: "Alt"
 | 
					        label: "Alt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,220 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    action:    { width: 59,    height: 46 }
 | 
					 | 
				
			||||||
    small:     { width: 50,    height: 22 }
 | 
					 | 
				
			||||||
    default:   { width: 35.33, height: 46 }
 | 
					 | 
				
			||||||
    altline:   { width: 48,    height: 46 }
 | 
					 | 
				
			||||||
    wide:      { width: 50,    height: 46 }
 | 
					 | 
				
			||||||
    spaceline: { width: 110,   height: 46 }
 | 
					 | 
				
			||||||
    special:   { width: 44,    height: 46 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "Ctrl Alt Tabsmall ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "a z e r t y u i o p"
 | 
					 | 
				
			||||||
        - "q s d f g h j k l m"
 | 
					 | 
				
			||||||
        - "Shift_L   w x c v b n period   BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "Ctrl Alt Tabsmall PgUp PgDn Home End"
 | 
					 | 
				
			||||||
        - "A Z E R T Y U I O P"
 | 
					 | 
				
			||||||
        - "Q S D F G H J K L M"
 | 
					 | 
				
			||||||
        - "Shift_L   W X C V B N ,  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "Ctrl Alt Tabsmall ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "@ # € % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "Ctrl Alt Tabsmall ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ $ ¥ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    eschars:
 | 
					 | 
				
			||||||
        - "Ctrl Alt Tabsmall ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "à â ç é è ê î ô ù û"
 | 
					 | 
				
			||||||
        - "À Â Ç É È Ê Î Ô Ù Û"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols  æ œ ä ë ï ö ü  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    actions:
 | 
					 | 
				
			||||||
        - "Ctrl Alt PgUp PgDn Home End"
 | 
					 | 
				
			||||||
        - "F1  F2  F3  F4  F5  F6"
 | 
					 | 
				
			||||||
        - "F7  F8  F9  F10 F11 F12"
 | 
					 | 
				
			||||||
        - "Esc Tab Pause Insert Up Del"
 | 
					 | 
				
			||||||
        - "show_letters Menu Break Left Down Right"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    F1:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F1"
 | 
					 | 
				
			||||||
    F2:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F2"
 | 
					 | 
				
			||||||
    F3:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F3"
 | 
					 | 
				
			||||||
    F4:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F4"
 | 
					 | 
				
			||||||
    F5:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F5"
 | 
					 | 
				
			||||||
    F6:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F6"
 | 
					 | 
				
			||||||
    F7:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F7"
 | 
					 | 
				
			||||||
    F8:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F8"
 | 
					 | 
				
			||||||
    F9:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F9"
 | 
					 | 
				
			||||||
    F10:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F10"
 | 
					 | 
				
			||||||
    F11:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F11"
 | 
					 | 
				
			||||||
    F12:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F12"
 | 
					 | 
				
			||||||
    Esc:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Escape"
 | 
					 | 
				
			||||||
    Tab:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Tab"
 | 
					 | 
				
			||||||
    Tabsmall:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Tab"
 | 
					 | 
				
			||||||
        label: "Tab"
 | 
					 | 
				
			||||||
    Del:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Delete"
 | 
					 | 
				
			||||||
    Insert:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Insert"
 | 
					 | 
				
			||||||
    Menu:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Menu"
 | 
					 | 
				
			||||||
    Pause:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Pause"
 | 
					 | 
				
			||||||
    Break:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Break"
 | 
					 | 
				
			||||||
    Home:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Home"
 | 
					 | 
				
			||||||
    End:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "End"
 | 
					 | 
				
			||||||
    PgUp:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Page_Up"
 | 
					 | 
				
			||||||
    PgDn:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Page_Down"
 | 
					 | 
				
			||||||
    "↑":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Up"
 | 
					 | 
				
			||||||
    "↓":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Down"
 | 
					 | 
				
			||||||
    "←":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Left"
 | 
					 | 
				
			||||||
    "→":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Right"
 | 
					 | 
				
			||||||
    Up:
 | 
					 | 
				
			||||||
        label: "↑"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Up"
 | 
					 | 
				
			||||||
    Left:
 | 
					 | 
				
			||||||
        label: "←"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Left"
 | 
					 | 
				
			||||||
    Down:
 | 
					 | 
				
			||||||
        label: "↓"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Down"
 | 
					 | 
				
			||||||
    Right:
 | 
					 | 
				
			||||||
        label: "→"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Right"
 | 
					 | 
				
			||||||
    Ctrl:
 | 
					 | 
				
			||||||
        modifier: "Control"
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        label: "Ctrl"
 | 
					 | 
				
			||||||
    Alt:
 | 
					 | 
				
			||||||
        modifier: "Alt"
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        label: "Alt"
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    show_actions:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "actions"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        label: ">_"
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: erase
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: "show_prefs"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "abc"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    show_eschars:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "eschars"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "âÂ"
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
    "\"":
 | 
					 | 
				
			||||||
        keysym: "quotedbl"
 | 
					 | 
				
			||||||
@ -1,223 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    action:    { width: 90,   height: 37 }
 | 
					 | 
				
			||||||
    small:     { width: 67.4, height: 22 }
 | 
					 | 
				
			||||||
    default:   { width: 54,  height: 37 }
 | 
					 | 
				
			||||||
    altline:   { width: 81,  height: 37 }
 | 
					 | 
				
			||||||
    wide:      { width: 100, height: 37 }
 | 
					 | 
				
			||||||
    spaceline: { width: 110, height: 37 }
 | 
					 | 
				
			||||||
    special:   { width: 54,  height: 37 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "a z e r t y u i o p"
 | 
					 | 
				
			||||||
        - "q s d f g h j k l m"
 | 
					 | 
				
			||||||
        - "Shift_L   w x c v b n period   BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "EscSmall TabSmall Ctrl Alt PgUp PgDn Home End"
 | 
					 | 
				
			||||||
        - "A Z E R T Y U I O P"
 | 
					 | 
				
			||||||
        - "Q S D F G H J K L M"
 | 
					 | 
				
			||||||
        - "Shift_L   W X C V B N ,  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "@ # € % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ $ ¥ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    eschars:
 | 
					 | 
				
			||||||
        - "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
 | 
					 | 
				
			||||||
        - "à â ç é è ê î ô ù û"
 | 
					 | 
				
			||||||
        - "À Â Ç É È Ê Î Ô Ù Û"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols  æ œ ä ë ï ö ü  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        show_eschars show_actions Return"
 | 
					 | 
				
			||||||
    actions:
 | 
					 | 
				
			||||||
        - "EscSmall TabSmall Ctrl Alt PgUp PgDn Home End"
 | 
					 | 
				
			||||||
        - "F1  F2  F3  F4  F5  F6"
 | 
					 | 
				
			||||||
        - "F7  F8  F9  F10 F11 F12"
 | 
					 | 
				
			||||||
        - "Esc Tab Pause Insert Up Del"
 | 
					 | 
				
			||||||
        - "show_letters Menu Break Left Down Right"
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    F1:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F1"
 | 
					 | 
				
			||||||
    F2:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F2"
 | 
					 | 
				
			||||||
    F3:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F3"
 | 
					 | 
				
			||||||
    F4:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F4"
 | 
					 | 
				
			||||||
    F5:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F5"
 | 
					 | 
				
			||||||
    F6:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F6"
 | 
					 | 
				
			||||||
    F7:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F7"
 | 
					 | 
				
			||||||
    F8:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F8"
 | 
					 | 
				
			||||||
    F9:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F9"
 | 
					 | 
				
			||||||
    F10:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F10"
 | 
					 | 
				
			||||||
    F11:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F11"
 | 
					 | 
				
			||||||
    F12:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "F12"
 | 
					 | 
				
			||||||
    Esc:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Escape"
 | 
					 | 
				
			||||||
    EscSmall:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Escape"
 | 
					 | 
				
			||||||
        label: "Esc"
 | 
					 | 
				
			||||||
    Tab:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Tab"
 | 
					 | 
				
			||||||
    TabSmall:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Tab"
 | 
					 | 
				
			||||||
        label: "Tab"
 | 
					 | 
				
			||||||
    Del:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Delete"
 | 
					 | 
				
			||||||
    Insert:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Insert"
 | 
					 | 
				
			||||||
    Menu:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Menu"
 | 
					 | 
				
			||||||
    Pause:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Pause"
 | 
					 | 
				
			||||||
    Break:
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Break"
 | 
					 | 
				
			||||||
    Home:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Home"
 | 
					 | 
				
			||||||
    End:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "End"
 | 
					 | 
				
			||||||
    PgUp:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Page_Up"
 | 
					 | 
				
			||||||
    PgDn:
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Page_Down"
 | 
					 | 
				
			||||||
    "↑":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Up"
 | 
					 | 
				
			||||||
    "↓":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Down"
 | 
					 | 
				
			||||||
    "←":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Left"
 | 
					 | 
				
			||||||
    "→":
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        keysym: "Right"
 | 
					 | 
				
			||||||
    Up:
 | 
					 | 
				
			||||||
        label: "↑"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Up"
 | 
					 | 
				
			||||||
    Left:
 | 
					 | 
				
			||||||
        label: "←"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Left"
 | 
					 | 
				
			||||||
    Down:
 | 
					 | 
				
			||||||
        label: "↓"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Down"
 | 
					 | 
				
			||||||
    Right:
 | 
					 | 
				
			||||||
        label: "→"
 | 
					 | 
				
			||||||
        outline: "action"
 | 
					 | 
				
			||||||
        keysym: "Right"
 | 
					 | 
				
			||||||
    Ctrl:
 | 
					 | 
				
			||||||
        modifier: "Control"
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        label: "Ctrl"
 | 
					 | 
				
			||||||
    Alt:
 | 
					 | 
				
			||||||
        modifier: "Alt"
 | 
					 | 
				
			||||||
        outline: "small"
 | 
					 | 
				
			||||||
        label: "Alt"
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    show_actions:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "actions"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        label: ">_"
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: erase
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: "show_prefs"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "abc"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    show_eschars:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "eschars"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "âÂ"
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
    "\"":
 | 
					 | 
				
			||||||
        keysym: "quotedbl"
 | 
					 | 
				
			||||||
@ -155,6 +155,9 @@ buttons:
 | 
				
			|||||||
    Pause:
 | 
					    Pause:
 | 
				
			||||||
        outline: "action"
 | 
					        outline: "action"
 | 
				
			||||||
        keysym: "Pause"
 | 
					        keysym: "Pause"
 | 
				
			||||||
 | 
					    Menu:
 | 
				
			||||||
 | 
					        outline: "action"
 | 
				
			||||||
 | 
					        keysym: "Menu"
 | 
				
			||||||
    Break:
 | 
					    Break:
 | 
				
			||||||
        outline: "action"
 | 
					        outline: "action"
 | 
				
			||||||
        keysym: "Break"
 | 
					        keysym: "Break"
 | 
				
			||||||
@ -205,4 +208,4 @@ buttons:
 | 
				
			|||||||
    Alt:
 | 
					    Alt:
 | 
				
			||||||
        modifier: "Alt"
 | 
					        modifier: "Alt"
 | 
				
			||||||
        outline: "small"
 | 
					        outline: "small"
 | 
				
			||||||
        label: "Alt"
 | 
					        label: "Alt"
 | 
				
			||||||
@ -1,84 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 75, height: 56 }
 | 
					 | 
				
			||||||
    altline: { width: 75, height: 56 }
 | 
					 | 
				
			||||||
    wide: { width: 135, height: 56 }
 | 
					 | 
				
			||||||
    spaceline: { width: 450, height: 56 }
 | 
					 | 
				
			||||||
    spacelinesymbol: { width: 300, height: 56 }
 | 
					 | 
				
			||||||
    special: { width: 90, height: 56 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "ๅ / _ ภ ถ ุ ึ ค ต จ ข ช"
 | 
					 | 
				
			||||||
        - "ๆ ไ ำ พ ะ ั ี ร น ย บ ล"
 | 
					 | 
				
			||||||
        - "ฟ ห ก ด เ ้ ่ า ส ว ง ฃ"
 | 
					 | 
				
			||||||
        - "Shift_L   ผ ป แ อ ิ ื ท ม ใ ฝ   BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "+ ๑ ๒ ๓ ๔ ู ฿ ๕ ๖ ๗ ๘ ๙"
 | 
					 | 
				
			||||||
        - "๐ \" ฎ ฑ ธ ํ ๊ ณ ฯ ญ ฐ ,"
 | 
					 | 
				
			||||||
        - "ฤ ฆ ฏ โ ฌ ็ ๋ ษ ศ ซ . ฅ"
 | 
					 | 
				
			||||||
        - "Shift_L   ( ) ฉ ฮ ฺ ์ ? ฒ ฬ ฦ  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "@ # $ % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         spacesymbol        period Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ € ¥ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         spacesymbol        period Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: erase
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: show_prefs
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "กขค"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    spacesymbol:
 | 
					 | 
				
			||||||
        outline: "spacelinesymbol"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
@ -1,78 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 54, height: 42 }
 | 
					 | 
				
			||||||
    altline: { width: 81, height: 42 }
 | 
					 | 
				
			||||||
    wide: { width: 108, height: 42 }
 | 
					 | 
				
			||||||
    spaceline: { width: 216, height: 42 }
 | 
					 | 
				
			||||||
    special: { width: 54, height: 42 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "q w f p g j l u y"
 | 
					 | 
				
			||||||
        - "a r s t d h n e i o"
 | 
					 | 
				
			||||||
        - "Shift_L   z x c v b k m  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        . Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "Q W F P G J L U Y"
 | 
					 | 
				
			||||||
        - "A R S T D H N E I O"
 | 
					 | 
				
			||||||
        - "Shift_L   Z X C V B K M  BackSpace"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        . Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "@ # $ % & - _ + ( )"
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        . Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "© ® £ € ¥ ^ ° * { }"
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        . Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: "erase"
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: "show_prefs"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "ABC"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    ".":
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
@ -1,89 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 35.33, height: 52 }
 | 
					 | 
				
			||||||
    altline: { width: 52.67, height: 52 }
 | 
					 | 
				
			||||||
    wide: { width: 62, height: 52 }
 | 
					 | 
				
			||||||
    spaceline: { width: 142, height: 52 }
 | 
					 | 
				
			||||||
    special: { width: 44, height: 52 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "Shift_L   p y f g c r l  BackSpace"
 | 
					 | 
				
			||||||
        - "a o e u i d h t n s"
 | 
					 | 
				
			||||||
        - ", q j k x b m w v z"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "Shift_L   P Y F G C R L  BackSpace"
 | 
					 | 
				
			||||||
        - "A O E U I D H T N S"
 | 
					 | 
				
			||||||
        - ", Q J K X B M W V Z"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "* # $ / & - _ + ( )"
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ % < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "© ® £ € ¥ ^ ° @ { }"
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: "erase"
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: "show_prefs"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "ABC"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# The US QWERTY layout has fewer letters on the third row, and so has
 | 
					 | 
				
			||||||
# the shift & backspace keys placed there. In contrast, the US DVORAK
 | 
					 | 
				
			||||||
# layout has fewer letters on the first row, which makes it a good
 | 
					 | 
				
			||||||
# choice for the shift & backspace keys. That leads to what may be,
 | 
					 | 
				
			||||||
# for many people, an unexpected layout in numbers mode: the numerals
 | 
					 | 
				
			||||||
# are on the third row (not the first) so that the backspace key
 | 
					 | 
				
			||||||
# remains in a consistent location regardless of mode, without
 | 
					 | 
				
			||||||
# sacrificing key width. (Once could argue that in numbers mode, the
 | 
					 | 
				
			||||||
# numerals should be closer to the enter key.) As with any keyboard
 | 
					 | 
				
			||||||
# layout, familiarity comes with repeated use.
 | 
					 | 
				
			||||||
@ -1,89 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
outlines:
 | 
					 | 
				
			||||||
    default: { width: 54, height: 42 }
 | 
					 | 
				
			||||||
    altline: { width: 81, height: 42 }
 | 
					 | 
				
			||||||
    wide: { width: 108, height: 42 }
 | 
					 | 
				
			||||||
    spaceline: { width: 216, height: 42 }
 | 
					 | 
				
			||||||
    special: { width: 54, height: 42 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
views:
 | 
					 | 
				
			||||||
    base:
 | 
					 | 
				
			||||||
        - "Shift_L   p y f g c r l  BackSpace"
 | 
					 | 
				
			||||||
        - "a o e u i d h t n s"
 | 
					 | 
				
			||||||
        - ", q j k x b m w v z"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    upper:
 | 
					 | 
				
			||||||
        - "Shift_L   P Y F G C R L  BackSpace"
 | 
					 | 
				
			||||||
        - "A O E U I D H T N S"
 | 
					 | 
				
			||||||
        - ", Q J K X B M W V Z"
 | 
					 | 
				
			||||||
        - "show_numbers preferences         space        period Return"
 | 
					 | 
				
			||||||
    numbers:
 | 
					 | 
				
			||||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
					 | 
				
			||||||
        - "* # $ / & - _ + ( )"
 | 
					 | 
				
			||||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
    symbols:
 | 
					 | 
				
			||||||
        - "show_numbers_from_symbols   \\ % < > = [ ]  BackSpace"
 | 
					 | 
				
			||||||
        - "© ® £ € ¥ ^ ° @ { }"
 | 
					 | 
				
			||||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
					 | 
				
			||||||
        - "show_letters preferences         space        period Return"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
buttons:
 | 
					 | 
				
			||||||
    Shift_L:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            locking:
 | 
					 | 
				
			||||||
                lock_view: "upper"
 | 
					 | 
				
			||||||
                unlock_view: "base"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "key-shift"
 | 
					 | 
				
			||||||
    BackSpace:
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        icon: "edit-clear-symbolic"
 | 
					 | 
				
			||||||
        action: "erase"
 | 
					 | 
				
			||||||
    preferences:
 | 
					 | 
				
			||||||
        action: "show_prefs"
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        icon: "keyboard-mode-symbolic"
 | 
					 | 
				
			||||||
    show_numbers:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_numbers_from_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "numbers"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "123"
 | 
					 | 
				
			||||||
    show_letters:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "base"
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        label: "ABC"
 | 
					 | 
				
			||||||
    show_symbols:
 | 
					 | 
				
			||||||
        action:
 | 
					 | 
				
			||||||
            set_view: "symbols"
 | 
					 | 
				
			||||||
        outline: "altline"
 | 
					 | 
				
			||||||
        label: "*/="
 | 
					 | 
				
			||||||
    period:
 | 
					 | 
				
			||||||
        outline: "special"
 | 
					 | 
				
			||||||
        text: "."
 | 
					 | 
				
			||||||
    space:
 | 
					 | 
				
			||||||
        outline: "spaceline"
 | 
					 | 
				
			||||||
        text: " "
 | 
					 | 
				
			||||||
    Return:
 | 
					 | 
				
			||||||
        outline: "wide"
 | 
					 | 
				
			||||||
        icon: "key-enter"
 | 
					 | 
				
			||||||
        keysym: "Return"
 | 
					 | 
				
			||||||
    colon:
 | 
					 | 
				
			||||||
        text: ":"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# The US QWERTY layout has fewer letters on the third row, and so has
 | 
					 | 
				
			||||||
# the shift & backspace keys placed there. In contrast, the US DVORAK
 | 
					 | 
				
			||||||
# layout has fewer letters on the first row, which makes it a good
 | 
					 | 
				
			||||||
# choice for the shift & backspace keys. That leads to what may be,
 | 
					 | 
				
			||||||
# for many people, an unexpected layout in numbers mode: the numerals
 | 
					 | 
				
			||||||
# are on the third row (not the first) so that the backspace key
 | 
					 | 
				
			||||||
# remains in a consistent location regardless of mode, without
 | 
					 | 
				
			||||||
# sacrificing key width. (Once could argue that in numbers mode, the
 | 
					 | 
				
			||||||
# numerals should be closer to the enter key.) As with any keyboard
 | 
					 | 
				
			||||||
# layout, familiarity comes with repeated use.
 | 
					 | 
				
			||||||
@ -1,2 +0,0 @@
 | 
				
			|||||||
emoji ایموجی
 | 
					 | 
				
			||||||
terminal ترمینال
 | 
					 | 
				
			||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
be בלגית
 | 
					 | 
				
			||||||
br פורטוגזית (ברזיל)
 | 
					 | 
				
			||||||
cz צ'כית
 | 
					 | 
				
			||||||
de גרמנית
 | 
					 | 
				
			||||||
dk דנית
 | 
					 | 
				
			||||||
es ספרדית
 | 
					 | 
				
			||||||
emoji אימוג'י
 | 
					 | 
				
			||||||
fi פינית
 | 
					 | 
				
			||||||
fr צרפתית
 | 
					 | 
				
			||||||
gr יוונית
 | 
					 | 
				
			||||||
il עברית
 | 
					 | 
				
			||||||
it איטלקית
 | 
					 | 
				
			||||||
no נורווגית
 | 
					 | 
				
			||||||
pl פולנית
 | 
					 | 
				
			||||||
ru רוסית
 | 
					 | 
				
			||||||
se שוודית
 | 
					 | 
				
			||||||
terminal טרמינל
 | 
					 | 
				
			||||||
ua אוקראינית
 | 
					 | 
				
			||||||
us אנגלית (ארה"ב)
 | 
					 | 
				
			||||||
@ -31,14 +31,9 @@ sq_button.wide {
 | 
				
			|||||||
    border-color: #3e3a44;
 | 
					    border-color: #3e3a44;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sq_button.latched {
 | 
					 | 
				
			||||||
    background: #ffffff;
 | 
					 | 
				
			||||||
    color: #2b292f;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
sq_button.locked {
 | 
					sq_button.locked {
 | 
				
			||||||
    background: #ffffff;
 | 
					    background: #ffffff;
 | 
				
			||||||
    color: #1c71d8;
 | 
					    color: #2b292f;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sq_button.action {
 | 
					sq_button.action {
 | 
				
			||||||
 | 
				
			|||||||
@ -34,14 +34,9 @@ sq_button.wide {
 | 
				
			|||||||
    border-color: @borders; /* #3e3a44; */
 | 
					    border-color: @borders; /* #3e3a44; */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sq_button.latched {
 | 
					 | 
				
			||||||
    background: @theme_fg_color; /*#ffffff;*/
 | 
					 | 
				
			||||||
    color: @theme_bg_color; /*#2b292f;*/
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
sq_button.locked {
 | 
					sq_button.locked {
 | 
				
			||||||
    background: @theme_fg_color; /*#ffffff;*/
 | 
					    background: @theme_fg_color; /*#ffffff;*/
 | 
				
			||||||
    color: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#2b292f;*/
 | 
					    color: @theme_bg_color; /*#2b292f;*/
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sq_button.action {
 | 
					sq_button.action {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										140
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										140
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@ -1,143 +1,3 @@
 | 
				
			|||||||
squeekboard (1.14.0pureos0~amber0) amber-phone; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Dorota Czaplejewicz ]
 | 
					 | 
				
			||||||
  * data: Split into loading and parsing
 | 
					 | 
				
			||||||
  * layout: Remove unused code
 | 
					 | 
				
			||||||
  * build: Fix unnecessary shell quotes
 | 
					 | 
				
			||||||
  * popover: Allow spanning outside panel area
 | 
					 | 
				
			||||||
  * cargo: Update dependencies before release
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ undef ]
 | 
					 | 
				
			||||||
  * Fix typos jp keyboard comments
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ anteater ]
 | 
					 | 
				
			||||||
  * use the correct GtkStyleProviderPriority to indicate that the styles are provided by the application
 | 
					 | 
				
			||||||
  * remove some unnecessary unsafe code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>  Sat, 15 May 2021 12:45:20 +0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
squeekboard (1.13.0pureos0~amber0) amber-phone; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Dorota Czaplejewicz ]
 | 
					 | 
				
			||||||
  * layout: Latch keys when clicked twice
 | 
					 | 
				
			||||||
  * layout: Add stateless view switching
 | 
					 | 
				
			||||||
  * layout: Plug in stateless view switching
 | 
					 | 
				
			||||||
  * layout: Remove the little abomination of view change promise
 | 
					 | 
				
			||||||
  * view: Ąto-unlatching when multiple latching buttons pressed
 | 
					 | 
				
			||||||
  * renderer: Bring button drawing closer to Rust
 | 
					 | 
				
			||||||
  * ffi: Eliminate squeek_button and squeek_row
 | 
					 | 
				
			||||||
  * imservice: Increment serials on receiving done, not sending commit
 | 
					 | 
				
			||||||
  * input-method: Fix commit/done mixup in protocol text
 | 
					 | 
				
			||||||
  * CI: fix xheck_tag to be compatible with Amber
 | 
					 | 
				
			||||||
  * italian: Fix colon
 | 
					 | 
				
			||||||
  * popover: Fix prematurely deallocated CString
 | 
					 | 
				
			||||||
  * Rust: Remove unnecessary no_mangle statements to silence warnings
 | 
					 | 
				
			||||||
  * renderer: Reduce reliance on knowing the transform
 | 
					 | 
				
			||||||
  * renderer: Split mutable geometry and place it directly in GtkKeyboard
 | 
					 | 
				
			||||||
  * Revert "moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations."
 | 
					 | 
				
			||||||
  * layout: Make it possible to opt out of latching per-key
 | 
					 | 
				
			||||||
  * renderer: Mark latched buttons differently than locked
 | 
					 | 
				
			||||||
  * appearance: Colour latched/locked according to design
 | 
					 | 
				
			||||||
  * docs: Describe view switching
 | 
					 | 
				
			||||||
  * language-terminal: Place keyboards in a sub-path
 | 
					 | 
				
			||||||
  * layout selection: Fix emoji and number
 | 
					 | 
				
			||||||
  * rust: Fix compiler warnings
 | 
					 | 
				
			||||||
  * layout: Take into account text purpose again
 | 
					 | 
				
			||||||
  * layouts: Make selection testable
 | 
					 | 
				
			||||||
  * layouts: Stop assuming that layout name always changes on switch
 | 
					 | 
				
			||||||
  * Cargo: Version bump
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ J.D. Laub ]
 | 
					 | 
				
			||||||
  * Add US Dvorak layout (and Colemak wide)
 | 
					 | 
				
			||||||
  *     Add US Dvorak layout (and Colemak wide)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Jordi Masip ]
 | 
					 | 
				
			||||||
  * Catalan keyboard layout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Myth ]
 | 
					 | 
				
			||||||
  * Added hebrew keyboard layout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ David96 ]
 | 
					 | 
				
			||||||
  * Add Mod4 (Windows) key
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Panawat Wong-klaew ]
 | 
					 | 
				
			||||||
  * Add wide Thai keyboard layout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Guido Günther ]
 | 
					 | 
				
			||||||
  * server-main: Add quit()
 | 
					 | 
				
			||||||
  * server-main: Properly register to gnome-session (Closes: #274)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Kozova1 ]
 | 
					 | 
				
			||||||
  * Added Hebrew translations for most layouts.
 | 
					 | 
				
			||||||
  * moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations.
 | 
					 | 
				
			||||||
  * Fixed Hebrew layout.
 | 
					 | 
				
			||||||
  * moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ M33 ]
 | 
					 | 
				
			||||||
  * Revert "Update tests/meson.build"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>  Mon, 12 Apr 2021 10:40:32 +0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
squeekboard (1.12.0pureos0~amber0) amber-phone; urgency=medium
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Dorota Czaplejewicz ]
 | 
					 | 
				
			||||||
  * docs: Correct Cargo update instructions
 | 
					 | 
				
			||||||
  * visibility: Centralize keyboard panel visibility policy and handling
 | 
					 | 
				
			||||||
  * build: Fix release
 | 
					 | 
				
			||||||
  * tests: Prefer the env var for finding test layouts
 | 
					 | 
				
			||||||
  * tests: Explicitly pass source directory to tests
 | 
					 | 
				
			||||||
  * debian: Build reproducibly
 | 
					 | 
				
			||||||
  * tests: Allow legacy mode to have much longer tests.
 | 
					 | 
				
			||||||
  * build: Enable unused warnings in C
 | 
					 | 
				
			||||||
  * build: Enable wformat to remove warnings about missing wformat
 | 
					 | 
				
			||||||
  * build: Fail on any C warnings when strict
 | 
					 | 
				
			||||||
  * data: Made data flow in fallback clearer
 | 
					 | 
				
			||||||
  * data: Flattened layout fallback function
 | 
					 | 
				
			||||||
  * layouts: Use base as fallback for alternative layouts
 | 
					 | 
				
			||||||
  * layouts: Simplify the main flow of source list
 | 
					 | 
				
			||||||
  * tests: Add some description to the list of tested layouts
 | 
					 | 
				
			||||||
  * layout_names: Unmess the list of builtin layouts
 | 
					 | 
				
			||||||
  * dbus: Reset hints if text input missing
 | 
					 | 
				
			||||||
  * visibility: Stop calling GTK functions from the visibility manager
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Wannaphong Phatthiyaphaibun ]
 | 
					 | 
				
			||||||
  * Add thai keyboard
 | 
					 | 
				
			||||||
  * Update resources.rs
 | 
					 | 
				
			||||||
  * Update meson.build
 | 
					 | 
				
			||||||
  * escape " on thai keyboard
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ clonex10100 ]
 | 
					 | 
				
			||||||
  * Added US Colemak Keyboard Layout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Henry-Nicolas Tourneur ]
 | 
					 | 
				
			||||||
  * d/rules: fix an FTBFS on mips64el with GOT > 64kb
 | 
					 | 
				
			||||||
  * d/rules: export RUSTFLAGS only on architecture that needs it
 | 
					 | 
				
			||||||
  * d/rules: export RUSTFLAGS only on architecture that needs it
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Jiří Stránský ]
 | 
					 | 
				
			||||||
  * Add Czech keyboard layouts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Stefan Grotz ]
 | 
					 | 
				
			||||||
  * Esperanto keyboard
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Vladimir ]
 | 
					 | 
				
			||||||
  * Bulgarian language keyboard layout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Vladimir Stoilov ]
 | 
					 | 
				
			||||||
  * bulgarian add translation and to needed lists
 | 
					 | 
				
			||||||
  * Fix bulgarian layout size
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ Andreas Rönnquist ]
 | 
					 | 
				
			||||||
  * no: Use wide button switching between numbers, symbols and base
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [ jranaraki ]
 | 
					 | 
				
			||||||
  * Farsi/Persian keyboard layout
 | 
					 | 
				
			||||||
  * Farsi/Persian keyboard layout
 | 
					 | 
				
			||||||
  * Added requirements to resources.rs and meson.build
 | 
					 | 
				
			||||||
  * Updated the layout to provide more convenient and faster typing experience
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 -- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>  Sun, 10 Jan 2021 09:43:42 +0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
squeekboard (1.11.1) amber-phone; urgency=medium
 | 
					squeekboard (1.11.1) amber-phone; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [ Mark Müller ]
 | 
					  [ Mark Müller ]
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								debian/check_release.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								debian/check_release.py
									
									
									
									
										vendored
									
									
								
							@ -5,7 +5,6 @@ Feed it the first changelog line, and then all available tags.
 | 
				
			|||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import re, sys
 | 
					import re, sys
 | 
				
			||||||
version = re.findall("\\((.*)\\)", input())[0]
 | 
					tag = "v" + re.findall("\\((.*)\\)", input())[0]
 | 
				
			||||||
tag = 'v' + re.findall("([0-9]+\\.[0-9]+\\.[0-9]+).*", version)[0]
 | 
					 | 
				
			||||||
if tag not in map(str.strip, sys.stdin.readlines()):
 | 
					if tag not in map(str.strip, sys.stdin.readlines()):
 | 
				
			||||||
    raise Exception("Changelog's current version doesn't have a tag. Push the tag!")
 | 
					    raise Exception("Changelog's current version doesn't have a tag. Push the tag!")
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,6 @@ Contents
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
* [Tutorial](tutorial.md)
 | 
					* [Tutorial](tutorial.md)
 | 
				
			||||||
* [Contributing](hacking.md)
 | 
					* [Contributing](hacking.md)
 | 
				
			||||||
* [Switching views](views.md)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Introduction
 | 
					Introduction
 | 
				
			||||||
------------
 | 
					------------
 | 
				
			||||||
@ -22,8 +21,6 @@ Layouts are created using a text-based format, based on YAML.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
TODO: Provide a description of the format.
 | 
					TODO: Provide a description of the format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Squeekboard layouts are separated into *views* and use a *room metaphor* to [switch views](views.md).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Contributions
 | 
					Contributions
 | 
				
			||||||
-------------
 | 
					-------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										594
									
								
								doc/latching.svg
									
									
									
									
									
								
							
							
						
						
									
										594
									
								
								doc/latching.svg
									
									
									
									
									
								
							@ -1,594 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
					 | 
				
			||||||
<svg
 | 
					 | 
				
			||||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
					 | 
				
			||||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
					 | 
				
			||||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
					 | 
				
			||||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
   xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
					 | 
				
			||||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
					 | 
				
			||||||
   width="177.92439mm"
 | 
					 | 
				
			||||||
   height="88.144363mm"
 | 
					 | 
				
			||||||
   viewBox="0 0 177.92439 88.144364"
 | 
					 | 
				
			||||||
   version="1.1"
 | 
					 | 
				
			||||||
   id="svg8"
 | 
					 | 
				
			||||||
   sodipodi:docname="latching.svg"
 | 
					 | 
				
			||||||
   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
 | 
					 | 
				
			||||||
  <defs
 | 
					 | 
				
			||||||
     id="defs2">
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Mend"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Mend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.4,0,0,-0.4,-4,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1098" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow2Sstart"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow2Sstart"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(0.3,0,0,0.3,-0.69,0)"
 | 
					 | 
				
			||||||
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1119" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow2Mstart"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow2Mstart"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="scale(0.6)"
 | 
					 | 
				
			||||||
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1113" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-5" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect848" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect848-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-9" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect848-8" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect916" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-4" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect951" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect986" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect1061" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-5"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-7" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect1549" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-8"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#101010;fill-opacity:1;fill-rule:evenodd;stroke:#101010;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-8" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-3" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect1845" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-38" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect1960" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-3-9" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect2304" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-3-4" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect2304-7" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-2"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#6c6c6c;fill-opacity:1;fill-rule:evenodd;stroke:#6c6c6c;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-89" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-2-6"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-89-8" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-21" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect2574" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-2-6-1"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-89-8-0" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-2-6-6"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-89-8-2" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10-1-8" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect3021" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-8-3"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#101010;fill-opacity:1;fill-rule:evenodd;stroke:#101010;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-8-5" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
  </defs>
 | 
					 | 
				
			||||||
  <sodipodi:namedview
 | 
					 | 
				
			||||||
     id="base"
 | 
					 | 
				
			||||||
     pagecolor="#ffffff"
 | 
					 | 
				
			||||||
     bordercolor="#666666"
 | 
					 | 
				
			||||||
     borderopacity="1.0"
 | 
					 | 
				
			||||||
     inkscape:pageopacity="0.0"
 | 
					 | 
				
			||||||
     inkscape:pageshadow="2"
 | 
					 | 
				
			||||||
     inkscape:zoom="1.3358025"
 | 
					 | 
				
			||||||
     inkscape:cx="212.63846"
 | 
					 | 
				
			||||||
     inkscape:cy="105.21093"
 | 
					 | 
				
			||||||
     inkscape:document-units="mm"
 | 
					 | 
				
			||||||
     inkscape:current-layer="layer1"
 | 
					 | 
				
			||||||
     inkscape:document-rotation="0"
 | 
					 | 
				
			||||||
     showgrid="false"
 | 
					 | 
				
			||||||
     fit-margin-top="4"
 | 
					 | 
				
			||||||
     fit-margin-left="4"
 | 
					 | 
				
			||||||
     fit-margin-right="4"
 | 
					 | 
				
			||||||
     fit-margin-bottom="4"
 | 
					 | 
				
			||||||
     lock-margins="true"
 | 
					 | 
				
			||||||
     inkscape:window-width="1298"
 | 
					 | 
				
			||||||
     inkscape:window-height="708"
 | 
					 | 
				
			||||||
     inkscape:window-x="0"
 | 
					 | 
				
			||||||
     inkscape:window-y="0"
 | 
					 | 
				
			||||||
     inkscape:window-maximized="1" />
 | 
					 | 
				
			||||||
  <metadata
 | 
					 | 
				
			||||||
     id="metadata5">
 | 
					 | 
				
			||||||
    <rdf:RDF>
 | 
					 | 
				
			||||||
      <cc:Work
 | 
					 | 
				
			||||||
         rdf:about="">
 | 
					 | 
				
			||||||
        <dc:format>image/svg+xml</dc:format>
 | 
					 | 
				
			||||||
        <dc:type
 | 
					 | 
				
			||||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
					 | 
				
			||||||
        <dc:title />
 | 
					 | 
				
			||||||
      </cc:Work>
 | 
					 | 
				
			||||||
    </rdf:RDF>
 | 
					 | 
				
			||||||
  </metadata>
 | 
					 | 
				
			||||||
  <g
 | 
					 | 
				
			||||||
     inkscape:label="Layer 1"
 | 
					 | 
				
			||||||
     inkscape:groupmode="layer"
 | 
					 | 
				
			||||||
     id="layer1"
 | 
					 | 
				
			||||||
     transform="translate(-72.097892,-53.326191)">
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#101010;stroke-width:0.264999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-8-3)"
 | 
					 | 
				
			||||||
       d="m 134.9616,86.141869 c 17.5759,-11.622767 35.93283,0 35.93283,0"
 | 
					 | 
				
			||||||
       id="path1087-1-6" />
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g2948">
 | 
					 | 
				
			||||||
      <circle
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1065-6"
 | 
					 | 
				
			||||||
         cx="129.67093"
 | 
					 | 
				
			||||||
         cy="92.793152"
 | 
					 | 
				
			||||||
         r="7.8844509" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-0"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(46.071199,-1.2662626)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>A</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       style="color:#000000;overflow:visible;fill:#f8f8f8;fill-opacity:1;stroke:#000000;stroke-width:0.555679;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
       id="rect2066"
 | 
					 | 
				
			||||||
       width="1.4562259"
 | 
					 | 
				
			||||||
       height="10.90563"
 | 
					 | 
				
			||||||
       x="81.767418"
 | 
					 | 
				
			||||||
       y="75.519585"
 | 
					 | 
				
			||||||
       ry="0.9693895" />
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905"
 | 
					 | 
				
			||||||
       transform="translate(0,-0.85044703)">
 | 
					 | 
				
			||||||
      <circle
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1065"
 | 
					 | 
				
			||||||
         cx="83.154755"
 | 
					 | 
				
			||||||
         cy="93.6436"
 | 
					 | 
				
			||||||
         r="7.8844509" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan
 | 
					 | 
				
			||||||
             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none">a</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-3-7"
 | 
					 | 
				
			||||||
       transform="translate(92.82116,-0.85044861)">
 | 
					 | 
				
			||||||
      <circle
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1065-6-7"
 | 
					 | 
				
			||||||
         cx="83.557831"
 | 
					 | 
				
			||||||
         cy="93.6436"
 | 
					 | 
				
			||||||
         r="7.8844509" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-0-5"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>Ą</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-1"
 | 
					 | 
				
			||||||
       transform="translate(68.838914,-17.67039)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-8"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.878532"
 | 
					 | 
				
			||||||
         y="85.920006"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-7"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.2686286,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan> ̨̂ ̈</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#101010;stroke-width:0.264999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-8)"
 | 
					 | 
				
			||||||
       d="m 88.446425,85.89509 c 17.575895,-11.622767 35.932835,0 35.932835,0"
 | 
					 | 
				
			||||||
       id="path1087-1" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="99.186691"
 | 
					 | 
				
			||||||
       y="66.221436"
 | 
					 | 
				
			||||||
       id="text1035-89"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan1033-6"
 | 
					 | 
				
			||||||
         x="99.186691"
 | 
					 | 
				
			||||||
         y="66.221436"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">locking</tspan></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-1-4"
 | 
					 | 
				
			||||||
       transform="translate(22.423434,-17.67039)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-8-3"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.878532"
 | 
					 | 
				
			||||||
         y="85.920006"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-7-3"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-3);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>⇧</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.5, 0.5;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
       d="m 83.251484,76.550079 c -0.31498,0 -0.944941,-0.314981 -0.944941,0 0,0.31498 0.770221,0.262079 0.944941,0 0.212555,-0.318834 0.08197,-0.862973 -0.188988,-1.133929 -0.132651,-0.13265 -1.931144,0.354037 -2.078869,0.944941 -0.108038,0.432149 0.877467,-0.188989 1.322916,-0.188989 0.339244,0 0.626121,0.262042 0.944941,0.377977 0.374434,0.136158 0.777569,0.199796 1.133928,0.377976 0.113894,0.05695 -0.0037,0.439732 0.944941,0.755952 0.544156,0.181385 1.78847,0.275251 2.267857,0.377976 0.448438,0.09609 0.881944,0.251984 1.322917,0.377977 0.063,0.188988 0.388199,0.566964 0.188988,0.566964 -0.195153,0 -0.745291,-0.93961 0,-0.566964 0.281726,0.140863 0.485859,0.404908 0.755952,0.566964 1.460327,0.876196 3.039871,1.714659 4.346726,2.834821 0.966187,0.828161 1.495207,2.006597 2.645833,2.645834 2.876101,1.597834 5.000414,0.287454 7.370534,1.133929 0.4783,0.170821 0.85136,0.567325 1.32292,0.755952 1.8557,0.74228 0.94424,0.230907 2.26785,0.377976 1.47287,0.16365 2.67387,1.06159 4.15774,0.566965 1.22233,-0.407443 3.2674,-1.377519 4.15774,-2.267858 0.39842,-0.398422 0.42845,-1.053404 0.75595,-1.511905 0.50136,-0.70189 1.27041,-1.107171 1.7009,-1.889881 2.28615,-4.156632 0.41931,-1.931216 3.2128,-4.724702 0.18898,-0.188988 0.33491,-0.434362 0.56696,-0.566964 0.864,-0.493714 5.11938,-0.281812 6.04762,-0.188989 0.34449,0.03445 3.71968,0.978261 3.96875,1.133929 0.35339,0.220868 1.41179,1.650838 1.88988,1.889881 0.80324,0.40162 2.22057,0.354332 3.02381,0.755952 1.24832,0.624162 -0.45041,0.669376 -0.18899,1.322917 0.1193,0.298244 0.64418,0.0762 0.94494,0.188988 0.28702,0.107634 1.48279,1.113549 1.5119,1.133929 2.56034,1.792236 -0.96522,-0.855663 2.26786,1.133928 1.61516,0.993946 2.82802,2.547938 4.53572,3.401785 2.03418,1.017093 4.38487,-0.122885 6.42559,-0.377975 1.6791,-0.209886 3.23813,0.06137 4.91369,-0.566965 0.50424,-0.18909 1.82709,-1.701063 2.26786,-2.078869 0.45926,-0.393653 1.0853,-0.707329 1.5119,-1.133928 0.81561,-0.815609 1.44764,-1.826091 2.45685,-2.456845 0.28768,-0.179799 0.65726,-0.198178 0.94494,-0.377977 0.28791,-0.179945 0.60349,-0.774216 0.94494,-0.94494 0.23232,-0.116159 0.51479,-0.09252 0.75595,-0.188988 0.21089,-0.08436 0.33983,-0.377976 0.56697,-0.377976 0.14086,0 0.23711,0.188988 0.37797,0.188988 0.0891,0 0.126,-0.125992 0.18899,-0.188988 0.18899,-0.063 0.36976,-0.217161 0.56696,-0.188988 0.77174,0.110248 1.92711,0.94494 3.02381,0.94494 0.16785,0 0.94189,-0.862638 1.13393,-0.94494 0.19546,-0.08377 1.57981,-0.364523 1.7009,-0.377977 0.72233,-0.08026 2.11645,0.340388 2.64583,-0.188988"
 | 
					 | 
				
			||||||
       id="path2010" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="73.802078"
 | 
					 | 
				
			||||||
       y="72.354546"
 | 
					 | 
				
			||||||
       id="text2091"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan2089"
 | 
					 | 
				
			||||||
         x="73.802078"
 | 
					 | 
				
			||||||
         y="72.354546"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">SAVED</tspan></text>
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="164.55956"
 | 
					 | 
				
			||||||
       y="72.354546"
 | 
					 | 
				
			||||||
       id="text2091-8"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan2089-8"
 | 
					 | 
				
			||||||
         x="164.55956"
 | 
					 | 
				
			||||||
         y="72.354546"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">CURRENT</tspan><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="164.55956"
 | 
					 | 
				
			||||||
         y="79.410095"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         id="tspan3432" /></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g2259"
 | 
					 | 
				
			||||||
       transform="matrix(1.0856157,0,0,0.94777147,-19.677062,6.4360598)">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         id="rect2111-9"
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 179.53502,72.72924 h 5.84717 l -1.37074,1.931346 h -3.04292 z"
 | 
					 | 
				
			||||||
         sodipodi:nodetypes="ccccc" />
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         id="rect2111-9-0"
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 179.4049,80.846668 h 5.84717 l -1.23711,-1.931347 h -3.17655 z"
 | 
					 | 
				
			||||||
         sodipodi:nodetypes="ccccc" />
 | 
					 | 
				
			||||||
      <g
 | 
					 | 
				
			||||||
         id="g2247"
 | 
					 | 
				
			||||||
         transform="translate(-0.04032786,-0.04319387)">
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,75.343053 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,76.112065 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-3"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,76.881078 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-0"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,77.65009 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-2"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,78.419102 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-4"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
      </g>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-0"
 | 
					 | 
				
			||||||
       transform="translate(157.32586,-0.83970203)" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="145.70187"
 | 
					 | 
				
			||||||
       y="66.221436"
 | 
					 | 
				
			||||||
       id="text1035-89-2"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan1033-6-9"
 | 
					 | 
				
			||||||
         x="145.70187"
 | 
					 | 
				
			||||||
         y="66.221436"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">locking</tspan></text>
 | 
					 | 
				
			||||||
  </g>
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 26 KiB  | 
@ -1,631 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
					 | 
				
			||||||
<svg
 | 
					 | 
				
			||||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
					 | 
				
			||||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
					 | 
				
			||||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
					 | 
				
			||||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
   xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
					 | 
				
			||||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
					 | 
				
			||||||
   width="169.9455mm"
 | 
					 | 
				
			||||||
   height="98.072433mm"
 | 
					 | 
				
			||||||
   viewBox="0 0 169.94549 98.072434"
 | 
					 | 
				
			||||||
   version="1.1"
 | 
					 | 
				
			||||||
   id="svg8"
 | 
					 | 
				
			||||||
   sodipodi:docname="latching_return.svg"
 | 
					 | 
				
			||||||
   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
 | 
					 | 
				
			||||||
  <defs
 | 
					 | 
				
			||||||
     id="defs2">
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10-1" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-5"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-7" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-38" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-3-9" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-3-4" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-2-6"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-89-8" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-21" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-2-6-1"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-89-8-0" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-2-6-6"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-89-8-2" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10-1-8" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-12" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10-7" />
 | 
					 | 
				
			||||||
  </defs>
 | 
					 | 
				
			||||||
  <sodipodi:namedview
 | 
					 | 
				
			||||||
     id="base"
 | 
					 | 
				
			||||||
     pagecolor="#ffffff"
 | 
					 | 
				
			||||||
     bordercolor="#666666"
 | 
					 | 
				
			||||||
     borderopacity="1.0"
 | 
					 | 
				
			||||||
     inkscape:pageopacity="0.0"
 | 
					 | 
				
			||||||
     inkscape:pageshadow="2"
 | 
					 | 
				
			||||||
     inkscape:zoom="0.94455498"
 | 
					 | 
				
			||||||
     inkscape:cx="314.8465"
 | 
					 | 
				
			||||||
     inkscape:cy="213.42055"
 | 
					 | 
				
			||||||
     inkscape:document-units="mm"
 | 
					 | 
				
			||||||
     inkscape:current-layer="layer1"
 | 
					 | 
				
			||||||
     inkscape:document-rotation="0"
 | 
					 | 
				
			||||||
     showgrid="false"
 | 
					 | 
				
			||||||
     fit-margin-top="4"
 | 
					 | 
				
			||||||
     fit-margin-left="4"
 | 
					 | 
				
			||||||
     fit-margin-right="4"
 | 
					 | 
				
			||||||
     fit-margin-bottom="4"
 | 
					 | 
				
			||||||
     lock-margins="true"
 | 
					 | 
				
			||||||
     inkscape:window-width="1298"
 | 
					 | 
				
			||||||
     inkscape:window-height="708"
 | 
					 | 
				
			||||||
     inkscape:window-x="0"
 | 
					 | 
				
			||||||
     inkscape:window-y="0"
 | 
					 | 
				
			||||||
     inkscape:window-maximized="1"
 | 
					 | 
				
			||||||
     showguides="false" />
 | 
					 | 
				
			||||||
  <metadata
 | 
					 | 
				
			||||||
     id="metadata5">
 | 
					 | 
				
			||||||
    <rdf:RDF>
 | 
					 | 
				
			||||||
      <cc:Work
 | 
					 | 
				
			||||||
         rdf:about="">
 | 
					 | 
				
			||||||
        <dc:format>image/svg+xml</dc:format>
 | 
					 | 
				
			||||||
        <dc:type
 | 
					 | 
				
			||||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
					 | 
				
			||||||
        <dc:title></dc:title>
 | 
					 | 
				
			||||||
      </cc:Work>
 | 
					 | 
				
			||||||
    </rdf:RDF>
 | 
					 | 
				
			||||||
  </metadata>
 | 
					 | 
				
			||||||
  <g
 | 
					 | 
				
			||||||
     inkscape:label="Layer 1"
 | 
					 | 
				
			||||||
     inkscape:groupmode="layer"
 | 
					 | 
				
			||||||
     id="layer1"
 | 
					 | 
				
			||||||
     transform="translate(-70.551731,-46.47634)">
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g3858"
 | 
					 | 
				
			||||||
       transform="translate(1.354821,1.4005714)">
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
         x="204.75821"
 | 
					 | 
				
			||||||
         y="130.07979"
 | 
					 | 
				
			||||||
         id="text2293-4"><tspan
 | 
					 | 
				
			||||||
           sodipodi:role="line"
 | 
					 | 
				
			||||||
           id="tspan2291-1"
 | 
					 | 
				
			||||||
           x="204.75821"
 | 
					 | 
				
			||||||
           y="130.07979"
 | 
					 | 
				
			||||||
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">✄</tspan></text>
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
         x="194.54915"
 | 
					 | 
				
			||||||
         y="128.70164"
 | 
					 | 
				
			||||||
         id="text2293-0-5-8"><tspan
 | 
					 | 
				
			||||||
           sodipodi:role="line"
 | 
					 | 
				
			||||||
           id="tspan2291-6-7-5"
 | 
					 | 
				
			||||||
           x="194.54915"
 | 
					 | 
				
			||||||
           y="128.70164"
 | 
					 | 
				
			||||||
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">cuts</tspan></text>
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 210.34971,123.9389 c 0.0233,0.14006 0.07,0.27818 0.07,0.42017 0,0.0738 -0.07,0.13627 -0.07,0.21009 0,0.11902 0.0582,0.23171 0.07,0.35014 0.049,0.49035 0.0857,0.98109 0.14006,1.4706 0.018,0.1624 -0.0269,0.32902 0,0.4902 0.0435,0.26077 0.11166,0.3369 0,0.56023 -0.0209,0.0418 0.033,0.10705 0,0.14006 -0.0369,0.0369 -0.10315,0.0331 -0.14006,0.07"
 | 
					 | 
				
			||||||
         id="path3816-9" />
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 209.9819,128.43627 c -0.12445,0.48957 -0.1295,0.98859 -0.0743,1.48553 0.0118,0.10583 0.01,0.21643 0.0248,0.32186 -0.002,0.0627 0.0396,0.11405 0.0495,0.17332 0.0144,0.0866 -0.0193,0.077 0,0.17331 0.0124,0.0621 0.20619,0.18971 0.24759,0.22283"
 | 
					 | 
				
			||||||
         id="path3820-7" />
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-2-6-1)"
 | 
					 | 
				
			||||||
       d="m 180.7405,85.345284 c 21.77006,-11.52723 44.50758,0 44.50758,0"
 | 
					 | 
				
			||||||
       id="path1087-3-0-8" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       style="color:#000000;overflow:visible;fill:#f8f8f8;fill-opacity:1;stroke:#000000;stroke-width:0.555679;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
       id="rect2066"
 | 
					 | 
				
			||||||
       width="1.4562259"
 | 
					 | 
				
			||||||
       height="10.90563"
 | 
					 | 
				
			||||||
       x="81.767418"
 | 
					 | 
				
			||||||
       y="75.519585"
 | 
					 | 
				
			||||||
       ry="0.9693895" />
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-3-7"
 | 
					 | 
				
			||||||
       transform="translate(92.629263,-0.8504468)">
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-0-5"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>Ą</tspan></tspan></text>
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-4-9"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.266167"
 | 
					 | 
				
			||||||
         y="86.745537"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.4em;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="174.86499"
 | 
					 | 
				
			||||||
       y="130.79413"
 | 
					 | 
				
			||||||
       id="text1035-8"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="174.86499"
 | 
					 | 
				
			||||||
         y="130.79413"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         id="tspan2277">locking</tspan><tspan
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="174.86499"
 | 
					 | 
				
			||||||
         y="135.11462"
 | 
					 | 
				
			||||||
         id="tspan1606" /></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-1-6"
 | 
					 | 
				
			||||||
       transform="translate(90.668095,26.90725)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-8-0"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.878532"
 | 
					 | 
				
			||||||
         y="85.920006"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-7-4"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-38);fill:#f8f8f8;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.2686286,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan
 | 
					 | 
				
			||||||
             style="fill:#f8f8f8;fill-opacity:1"> ̨̂ ̈</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.5, 0.5;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
       d="m 83.251484,76.550079 c -0.31498,0 -0.944941,-0.314981 -0.944941,0 0,0.31498 0.770221,0.262079 0.944941,0 0.212555,-0.318834 0.08197,-0.862973 -0.188988,-1.133929 -0.132651,-0.13265 -1.931144,0.354037 -2.078869,0.944941 -0.108038,0.432149 0.877467,-0.188989 1.322916,-0.188989 0.339244,0 0.626121,0.262042 0.944941,0.377977 0.374434,0.136158 0.777569,0.199796 1.133928,0.377976 0.113894,0.05695 -0.0037,0.439732 0.944941,0.755952 0.544156,0.181385 1.78847,0.275251 2.267857,0.377976 0.448438,0.09609 0.881944,0.251984 1.322917,0.377977 0.063,0.188988 0.388199,0.566964 0.188988,0.566964 -0.195153,0 -0.745291,-0.93961 0,-0.566964 0.281726,0.140863 0.485859,0.404908 0.755952,0.566964 1.460327,0.876196 3.039871,1.714659 4.346726,2.834821 0.966187,0.828161 1.495207,2.006597 2.645833,2.645834 2.876101,1.597834 5.000414,0.287454 7.370534,1.133929 0.4783,0.170821 0.85136,0.567325 1.32292,0.755952 1.8557,0.74228 0.94424,0.230907 2.26785,0.377976 1.47287,0.16365 2.67387,1.06159 4.15774,0.566965 1.22233,-0.407443 3.2674,-1.377519 4.15774,-2.267858 0.39842,-0.398422 0.42845,-1.053404 0.75595,-1.511905 0.50136,-0.70189 1.27041,-1.107171 1.7009,-1.889881 2.28615,-4.156632 0.41931,-1.931216 3.2128,-4.724702 0.18898,-0.188988 0.33491,-0.434362 0.56696,-0.566964 0.864,-0.493714 5.11938,-0.281812 6.04762,-0.188989 0.34449,0.03445 3.71968,0.978261 3.96875,1.133929 0.35339,0.220868 1.41179,1.650838 1.88988,1.889881 0.80324,0.40162 2.22057,0.354332 3.02381,0.755952 1.24832,0.624162 -0.45041,0.669376 -0.18899,1.322917 0.1193,0.298244 0.64418,0.0762 0.94494,0.188988 0.28702,0.107634 1.48279,1.113549 1.5119,1.133929 2.56034,1.792236 -0.96522,-0.855663 2.26786,1.133928 1.61516,0.993946 2.82802,2.547938 4.53572,3.401785 2.03418,1.017093 4.38487,-0.122885 6.42559,-0.377975 1.6791,-0.209886 3.23813,0.06137 4.91369,-0.566965 0.50424,-0.18909 1.82709,-1.701063 2.26786,-2.078869 0.45926,-0.393653 1.0853,-0.707329 1.5119,-1.133928 0.81561,-0.815609 1.44764,-1.826091 2.45685,-2.456845 0.28768,-0.179799 0.65726,-0.198178 0.94494,-0.377977 0.28791,-0.179945 0.60349,-0.774216 0.94494,-0.94494 0.23232,-0.116159 0.51479,-0.09252 0.75595,-0.188988 0.21089,-0.08436 0.33983,-0.377976 0.56697,-0.377976 0.14086,0 0.23711,0.188988 0.37797,0.188988 0.0891,0 0.126,-0.125992 0.18899,-0.188988 0.18899,-0.063 0.36976,-0.217161 0.56696,-0.188988 0.77174,0.110248 1.92711,0.94494 3.02381,0.94494 0.16785,0 0.94189,-0.862638 1.13393,-0.94494 0.19546,-0.08377 1.57981,-0.364523 1.7009,-0.377977 0.72233,-0.08026 2.11645,0.340388 2.64583,-0.188988"
 | 
					 | 
				
			||||||
       id="path2010" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="74.331245"
 | 
					 | 
				
			||||||
       y="71.825378"
 | 
					 | 
				
			||||||
       id="text2091"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan2089"
 | 
					 | 
				
			||||||
         x="74.331245"
 | 
					 | 
				
			||||||
         y="71.825378"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">SAVED</tspan></text>
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="164.23172"
 | 
					 | 
				
			||||||
       y="70.186089"
 | 
					 | 
				
			||||||
       id="text2091-8"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan2089-8"
 | 
					 | 
				
			||||||
         x="164.23172"
 | 
					 | 
				
			||||||
         y="70.186089"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">CURRENT</tspan><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="164.23172"
 | 
					 | 
				
			||||||
         y="77.241638"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         id="tspan3432" /></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g2259"
 | 
					 | 
				
			||||||
       transform="matrix(1.0856157,0,0,0.94777147,-20.206229,4.319393)">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         id="rect2111-9"
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 179.53502,72.72924 h 5.84717 l -1.37074,1.931346 h -3.04292 z"
 | 
					 | 
				
			||||||
         sodipodi:nodetypes="ccccc" />
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         id="rect2111-9-0"
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 179.4049,80.846668 h 5.84717 l -1.23711,-1.931347 h -3.17655 z"
 | 
					 | 
				
			||||||
         sodipodi:nodetypes="ccccc" />
 | 
					 | 
				
			||||||
      <g
 | 
					 | 
				
			||||||
         id="g2247"
 | 
					 | 
				
			||||||
         transform="translate(-0.04032786,-0.04319387)">
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,75.343053 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,76.112065 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-3"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,76.881078 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-0"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,77.65009 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-2"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,78.419102 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-4"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
      </g>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-5)"
 | 
					 | 
				
			||||||
       d="m 173.01207,99.691217 c 0,0 -8.37295,13.367043 -0.26038,13.813423 9.36786,0.51544 7.89797,-9.16673 6.50059,-13.41252"
 | 
					 | 
				
			||||||
       id="path2269"
 | 
					 | 
				
			||||||
       sodipodi:nodetypes="csc" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="176.93353"
 | 
					 | 
				
			||||||
       y="138.21815"
 | 
					 | 
				
			||||||
       id="text2293"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan2291"
 | 
					 | 
				
			||||||
         x="176.93353"
 | 
					 | 
				
			||||||
         y="138.21815"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">✄</tspan></text>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-2-6)"
 | 
					 | 
				
			||||||
       d="m 181.46685,99.71436 c 21.61425,11.51564 44.18902,0 44.18902,0"
 | 
					 | 
				
			||||||
       id="path1087-3-0" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="195.7271"
 | 
					 | 
				
			||||||
       y="122.96945"
 | 
					 | 
				
			||||||
       id="text1035-89-7"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan1033-6-5"
 | 
					 | 
				
			||||||
         x="195.7271"
 | 
					 | 
				
			||||||
         y="122.96945"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">set_view</tspan></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-1-4-4"
 | 
					 | 
				
			||||||
       transform="translate(120.23594,18.477932)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-8-3-8"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.878532"
 | 
					 | 
				
			||||||
         y="85.920006"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-7-3-1"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-3-4);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>⇧</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.36em;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="196.99921"
 | 
					 | 
				
			||||||
       y="61.359337"
 | 
					 | 
				
			||||||
       id="text1035-89-4"><tspan
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan3987"
 | 
					 | 
				
			||||||
         x="196.99921"
 | 
					 | 
				
			||||||
         y="61.359337">locking</tspan><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="196.99921"
 | 
					 | 
				
			||||||
         y="65.362335"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         id="tspan3985">pops: false</tspan></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-1-4-9"
 | 
					 | 
				
			||||||
       transform="translate(120.23594,-18.828317)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-8-3-2"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.878532"
 | 
					 | 
				
			||||||
         y="85.920006"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-7-3-2"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-3-9);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>⇧</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-0"
 | 
					 | 
				
			||||||
       transform="translate(147.8008,-0.83970203)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-5"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="77.863091"
 | 
					 | 
				
			||||||
         y="86.745537"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-1"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-21);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>ą</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-2-6-6)"
 | 
					 | 
				
			||||||
       d="m 170.49256,99.691217 c -49.40563,31.667983 -82.046134,0 -82.046134,0"
 | 
					 | 
				
			||||||
       id="path1087-3-0-5"
 | 
					 | 
				
			||||||
       sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-3-7-6"
 | 
					 | 
				
			||||||
       transform="translate(45.925334,26.134402)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-4-9-8"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.266167"
 | 
					 | 
				
			||||||
         y="86.745537"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-0-5-2"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1-8);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>Ą</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.4em;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="129.60217"
 | 
					 | 
				
			||||||
       y="131.30838"
 | 
					 | 
				
			||||||
       id="text1035-8-4"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="129.60217"
 | 
					 | 
				
			||||||
         y="131.30838"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         id="tspan2277-2">text</tspan><tspan
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="129.60217"
 | 
					 | 
				
			||||||
         y="135.62888"
 | 
					 | 
				
			||||||
         id="tspan1606-4" /></text>
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.58611px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="123.67847"
 | 
					 | 
				
			||||||
       y="136.8163"
 | 
					 | 
				
			||||||
       id="text2293-0"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan2291-6"
 | 
					 | 
				
			||||||
         x="123.67847"
 | 
					 | 
				
			||||||
         y="136.8163"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.58611px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">returns</tspan></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905"
 | 
					 | 
				
			||||||
       transform="translate(1.1299757e-7,-0.85044747)">
 | 
					 | 
				
			||||||
      <circle
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1065"
 | 
					 | 
				
			||||||
         cx="83.154755"
 | 
					 | 
				
			||||||
         cy="93.6436"
 | 
					 | 
				
			||||||
         r="7.8844509" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-12);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan
 | 
					 | 
				
			||||||
             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none">a</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g2948">
 | 
					 | 
				
			||||||
      <circle
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1065-6"
 | 
					 | 
				
			||||||
         cx="129.67093"
 | 
					 | 
				
			||||||
         cy="92.793152"
 | 
					 | 
				
			||||||
         r="7.8844509" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-0"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-7);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(46.071199,-1.2662626)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>A</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g2259-3"
 | 
					 | 
				
			||||||
       transform="matrix(0.61171471,0,0,0.53404326,8.8050741,94.418409)">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         id="rect2111-9-1"
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 179.53502,72.72924 h 5.84717 l -1.37074,1.931346 h -3.04292 z"
 | 
					 | 
				
			||||||
         sodipodi:nodetypes="ccccc" />
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         id="rect2111-9-0-7"
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 179.4049,80.846668 h 5.84717 l -1.23711,-1.931347 h -3.17655 z"
 | 
					 | 
				
			||||||
         sodipodi:nodetypes="ccccc" />
 | 
					 | 
				
			||||||
      <g
 | 
					 | 
				
			||||||
         id="g2247-5"
 | 
					 | 
				
			||||||
         transform="translate(-0.04032786,-0.04319387)">
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,75.343053 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-9"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,76.112065 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-3-6"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,76.881078 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-0-2"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,77.65009 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-2-1"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
        <path
 | 
					 | 
				
			||||||
           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
           d="m 181.02302,78.419102 2.96688,-0.19452"
 | 
					 | 
				
			||||||
           id="path2145-4-7"
 | 
					 | 
				
			||||||
           sodipodi:nodetypes="cc" />
 | 
					 | 
				
			||||||
      </g>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.06, 0.53;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
       d="m 119.51485,135.86385 c -0.50656,0.62998 -2.26796,0.26701 -2.85105,0.85011 -0.52314,0.52314 2.02401,2.4426 3.96141,3.16913 2.94083,1.1028 4.79827,0.22 7.72476,-0.19807 0.3268,-0.0467 0.66473,0.0543 0.99035,0 1.72749,-0.28792 3.39512,-0.79539 5.14984,-0.99036 1.10207,-0.12245 0.49735,0.0622 1.58457,0.19807 0.91825,0.11479 1.8542,0.0832 2.77299,0.19807 0.86919,0.10865 1.70964,-0.19807 2.57492,-0.19807"
 | 
					 | 
				
			||||||
       id="path3796" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="166.72447"
 | 
					 | 
				
			||||||
       y="136.84001"
 | 
					 | 
				
			||||||
       id="text2293-0-5"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan2291-6-7"
 | 
					 | 
				
			||||||
         x="166.72447"
 | 
					 | 
				
			||||||
         y="136.84001"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">cuts</tspan></text>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529166;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
       d="m 182.52503,132.07727 c 0.0233,0.14006 0.07,0.27818 0.07,0.42017 0,0.0738 -0.07,0.13627 -0.07,0.21009 0,0.11902 0.0582,0.23171 0.07,0.35014 0.049,0.49035 0.0857,0.98109 0.14006,1.4706 0.018,0.1624 -0.0269,0.32902 0,0.4902 0.0435,0.26077 0.11166,0.3369 0,0.56023 -0.0209,0.0418 0.033,0.10705 0,0.14006 -0.0369,0.0369 -0.10315,0.0331 -0.14006,0.07"
 | 
					 | 
				
			||||||
       id="path3816" />
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529166;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
       d="m 182.15722,136.57464 c -0.12445,0.48957 -0.1295,0.98859 -0.0743,1.48553 0.0118,0.10583 0.01,0.21643 0.0248,0.32186 -0.002,0.0627 0.0396,0.11405 0.0495,0.17332 0.0144,0.0866 -0.0193,0.077 0,0.17331 0.0124,0.0621 0.20619,0.18971 0.24759,0.22283"
 | 
					 | 
				
			||||||
       id="path3820" />
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g3858-9"
 | 
					 | 
				
			||||||
       transform="translate(1.354821,-73.436839)">
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
         x="204.75821"
 | 
					 | 
				
			||||||
         y="130.07979"
 | 
					 | 
				
			||||||
         id="text2293-4-6"><tspan
 | 
					 | 
				
			||||||
           sodipodi:role="line"
 | 
					 | 
				
			||||||
           id="tspan2291-1-4"
 | 
					 | 
				
			||||||
           x="204.75821"
 | 
					 | 
				
			||||||
           y="130.07979"
 | 
					 | 
				
			||||||
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">✄</tspan></text>
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
         x="194.54915"
 | 
					 | 
				
			||||||
         y="128.70164"
 | 
					 | 
				
			||||||
         id="text2293-0-5-8-3"><tspan
 | 
					 | 
				
			||||||
           sodipodi:role="line"
 | 
					 | 
				
			||||||
           id="tspan2291-6-7-5-3"
 | 
					 | 
				
			||||||
           x="194.54915"
 | 
					 | 
				
			||||||
           y="128.70164"
 | 
					 | 
				
			||||||
           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">cuts</tspan></text>
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 210.34971,123.9389 c 0.0233,0.14006 0.07,0.27818 0.07,0.42017 0,0.0738 -0.07,0.13627 -0.07,0.21009 0,0.11902 0.0582,0.23171 0.07,0.35014 0.049,0.49035 0.0857,0.98109 0.14006,1.4706 0.018,0.1624 -0.0269,0.32902 0,0.4902 0.0435,0.26077 0.11166,0.3369 0,0.56023 -0.0209,0.0418 0.033,0.10705 0,0.14006 -0.0369,0.0369 -0.10315,0.0331 -0.14006,0.07"
 | 
					 | 
				
			||||||
         id="path3816-9-3" />
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="m 209.9819,128.43627 c -0.12445,0.48957 -0.1295,0.98859 -0.0743,1.48553 0.0118,0.10583 0.01,0.21643 0.0248,0.32186 -0.002,0.0627 0.0396,0.11405 0.0495,0.17332 0.0144,0.0866 -0.0193,0.077 0,0.17331 0.0124,0.0621 0.20619,0.18971 0.24759,0.22283"
 | 
					 | 
				
			||||||
         id="path3820-7-8" />
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
  </g>
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 39 KiB  | 
@ -1,386 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
					 | 
				
			||||||
<svg
 | 
					 | 
				
			||||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
					 | 
				
			||||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
					 | 
				
			||||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
					 | 
				
			||||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
   xmlns="http://www.w3.org/2000/svg"
 | 
					 | 
				
			||||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
					 | 
				
			||||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
					 | 
				
			||||||
   width="70.905495mm"
 | 
					 | 
				
			||||||
   height="78.260262mm"
 | 
					 | 
				
			||||||
   viewBox="0 0 70.905494 78.260262"
 | 
					 | 
				
			||||||
   version="1.1"
 | 
					 | 
				
			||||||
   id="svg8"
 | 
					 | 
				
			||||||
   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
 | 
					 | 
				
			||||||
   sodipodi:docname="switching.svg">
 | 
					 | 
				
			||||||
  <defs
 | 
					 | 
				
			||||||
     id="defs2">
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Mend"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Mend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.4,0,0,-0.4,-4,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1098" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow2Sstart"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow2Sstart"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(0.3,0,0,0.3,-0.69,0)"
 | 
					 | 
				
			||||||
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1119" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow2Mstart"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow2Mstart"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="scale(0.6)"
 | 
					 | 
				
			||||||
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1113" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-5" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect848" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect848-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-9" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect848-8" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect916" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-4" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect951" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-10-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect986" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect1061" />
 | 
					 | 
				
			||||||
    <marker
 | 
					 | 
				
			||||||
       style="overflow:visible"
 | 
					 | 
				
			||||||
       id="Arrow1Lend-5"
 | 
					 | 
				
			||||||
       refX="0"
 | 
					 | 
				
			||||||
       refY="0"
 | 
					 | 
				
			||||||
       orient="auto"
 | 
					 | 
				
			||||||
       inkscape:stockid="Arrow1Lend"
 | 
					 | 
				
			||||||
       inkscape:isstock="true">
 | 
					 | 
				
			||||||
      <path
 | 
					 | 
				
			||||||
         transform="matrix(-0.8,0,0,-0.8,-10,0)"
 | 
					 | 
				
			||||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
 | 
					 | 
				
			||||||
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
 | 
					 | 
				
			||||||
         id="path1092-7" />
 | 
					 | 
				
			||||||
    </marker>
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect835-2-1" />
 | 
					 | 
				
			||||||
    <rect
 | 
					 | 
				
			||||||
       x="80.886902"
 | 
					 | 
				
			||||||
       y="89.202377"
 | 
					 | 
				
			||||||
       width="18.898809"
 | 
					 | 
				
			||||||
       height="20.410713"
 | 
					 | 
				
			||||||
       id="rect1549" />
 | 
					 | 
				
			||||||
  </defs>
 | 
					 | 
				
			||||||
  <sodipodi:namedview
 | 
					 | 
				
			||||||
     id="base"
 | 
					 | 
				
			||||||
     pagecolor="#ffffff"
 | 
					 | 
				
			||||||
     bordercolor="#666666"
 | 
					 | 
				
			||||||
     borderopacity="1.0"
 | 
					 | 
				
			||||||
     inkscape:pageopacity="0.0"
 | 
					 | 
				
			||||||
     inkscape:pageshadow="2"
 | 
					 | 
				
			||||||
     inkscape:zoom="0.98994949"
 | 
					 | 
				
			||||||
     inkscape:cx="136.49044"
 | 
					 | 
				
			||||||
     inkscape:cy="110.83537"
 | 
					 | 
				
			||||||
     inkscape:document-units="mm"
 | 
					 | 
				
			||||||
     inkscape:current-layer="layer1"
 | 
					 | 
				
			||||||
     inkscape:document-rotation="0"
 | 
					 | 
				
			||||||
     showgrid="false"
 | 
					 | 
				
			||||||
     fit-margin-top="4"
 | 
					 | 
				
			||||||
     fit-margin-left="4"
 | 
					 | 
				
			||||||
     fit-margin-right="4"
 | 
					 | 
				
			||||||
     fit-margin-bottom="4"
 | 
					 | 
				
			||||||
     lock-margins="true"
 | 
					 | 
				
			||||||
     inkscape:window-width="1298"
 | 
					 | 
				
			||||||
     inkscape:window-height="708"
 | 
					 | 
				
			||||||
     inkscape:window-x="0"
 | 
					 | 
				
			||||||
     inkscape:window-y="0"
 | 
					 | 
				
			||||||
     inkscape:window-maximized="1" />
 | 
					 | 
				
			||||||
  <metadata
 | 
					 | 
				
			||||||
     id="metadata5">
 | 
					 | 
				
			||||||
    <rdf:RDF>
 | 
					 | 
				
			||||||
      <cc:Work
 | 
					 | 
				
			||||||
         rdf:about="">
 | 
					 | 
				
			||||||
        <dc:format>image/svg+xml</dc:format>
 | 
					 | 
				
			||||||
        <dc:type
 | 
					 | 
				
			||||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
					 | 
				
			||||||
        <dc:title></dc:title>
 | 
					 | 
				
			||||||
      </cc:Work>
 | 
					 | 
				
			||||||
    </rdf:RDF>
 | 
					 | 
				
			||||||
  </metadata>
 | 
					 | 
				
			||||||
  <g
 | 
					 | 
				
			||||||
     inkscape:label="Layer 1"
 | 
					 | 
				
			||||||
     inkscape:groupmode="layer"
 | 
					 | 
				
			||||||
     id="layer1"
 | 
					 | 
				
			||||||
     transform="translate(-70.899889,-58.974186)">
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
 | 
					 | 
				
			||||||
       d="m 88.446426,85.895089 c 17.575894,-11.622767 35.932844,0 35.932844,0"
 | 
					 | 
				
			||||||
       id="path1087" />
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905"
 | 
					 | 
				
			||||||
       transform="translate(0,-0.85044703)">
 | 
					 | 
				
			||||||
      <circle
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1065"
 | 
					 | 
				
			||||||
         cx="83.03434"
 | 
					 | 
				
			||||||
         cy="93.834679"
 | 
					 | 
				
			||||||
         r="7.8844509" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.16231537,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan
 | 
					 | 
				
			||||||
             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none">a</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-3"
 | 
					 | 
				
			||||||
       transform="translate(46.113101,-0.85044703)">
 | 
					 | 
				
			||||||
      <circle
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="path1065-3"
 | 
					 | 
				
			||||||
         cx="83.557831"
 | 
					 | 
				
			||||||
         cy="93.366959"
 | 
					 | 
				
			||||||
         r="7.8844509" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-0"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>A</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-7"
 | 
					 | 
				
			||||||
       transform="translate(-0.94494048,73.232887)">
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-6"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-4);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>ą</tspan></tspan></text>
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-3"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="77.863091"
 | 
					 | 
				
			||||||
         y="86.745537"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-3-7"
 | 
					 | 
				
			||||||
       transform="translate(49.136911,68.319196)">
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-0-5"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>Ą</tspan></tspan></text>
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-4-9"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.266167"
 | 
					 | 
				
			||||||
         y="86.745537"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="90.548096"
 | 
					 | 
				
			||||||
       y="65.654472"
 | 
					 | 
				
			||||||
       id="text1035"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan1033"
 | 
					 | 
				
			||||||
         x="90.548096"
 | 
					 | 
				
			||||||
         y="65.654472"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">set_view: upper</tspan></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-1"
 | 
					 | 
				
			||||||
       transform="translate(22.423437,-17.670388)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-8"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.878532"
 | 
					 | 
				
			||||||
         y="85.920006"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-7"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan>⇧</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
    <path
 | 
					 | 
				
			||||||
       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-5)"
 | 
					 | 
				
			||||||
       d="m 124.74085,99.691217 c -17.5759,11.622773 -35.932849,0 -35.932849,0"
 | 
					 | 
				
			||||||
       id="path1087-4" />
 | 
					 | 
				
			||||||
    <text
 | 
					 | 
				
			||||||
       xml:space="preserve"
 | 
					 | 
				
			||||||
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.4em;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
					 | 
				
			||||||
       x="91.116165"
 | 
					 | 
				
			||||||
       y="123.76147"
 | 
					 | 
				
			||||||
       id="text1035-8"><tspan
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan1033-5"
 | 
					 | 
				
			||||||
         x="91.116165"
 | 
					 | 
				
			||||||
         y="123.76147"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">locking</tspan><tspan
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         id="tspan1604"
 | 
					 | 
				
			||||||
         x="91.116165"
 | 
					 | 
				
			||||||
         y="128.08197">lock_view: upper</tspan><tspan
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
 | 
					 | 
				
			||||||
         sodipodi:role="line"
 | 
					 | 
				
			||||||
         x="91.116165"
 | 
					 | 
				
			||||||
         y="132.40247"
 | 
					 | 
				
			||||||
         id="tspan1606">unlock_view: lower</tspan></text>
 | 
					 | 
				
			||||||
    <g
 | 
					 | 
				
			||||||
       id="g905-1-9"
 | 
					 | 
				
			||||||
       transform="translate(22.423437,17.64464)">
 | 
					 | 
				
			||||||
      <rect
 | 
					 | 
				
			||||||
         style="color:#000000;overflow:visible;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
					 | 
				
			||||||
         id="rect900-8-7"
 | 
					 | 
				
			||||||
         width="10.583333"
 | 
					 | 
				
			||||||
         height="13.79613"
 | 
					 | 
				
			||||||
         x="78.878532"
 | 
					 | 
				
			||||||
         y="85.920006"
 | 
					 | 
				
			||||||
         ry="1.937705" />
 | 
					 | 
				
			||||||
      <text
 | 
					 | 
				
			||||||
         xml:space="preserve"
 | 
					 | 
				
			||||||
         id="text833-7-5"
 | 
					 | 
				
			||||||
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-1);fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
 | 
					 | 
				
			||||||
         transform="translate(-0.04189825,-0.41581583)"><tspan
 | 
					 | 
				
			||||||
           x="80.886719"
 | 
					 | 
				
			||||||
           y="96.225952"><tspan
 | 
					 | 
				
			||||||
             style="fill:#ffffff;fill-opacity:1">⇧</tspan></tspan></text>
 | 
					 | 
				
			||||||
    </g>
 | 
					 | 
				
			||||||
  </g>
 | 
					 | 
				
			||||||
</svg>
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										64
									
								
								doc/views.md
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								doc/views.md
									
									
									
									
									
								
							@ -1,64 +0,0 @@
 | 
				
			|||||||
Switching views
 | 
					 | 
				
			||||||
=========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Squeekboard layout files are separated into *views*.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
What are views?
 | 
					 | 
				
			||||||
-------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A view is the button arrangement which you see on Squeekboard's panel. The view always spans the entire panel area, so it's not possible to see two views at the same time, even if the layout contains multiple views.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Views are useful because they allow to have many more buttons than would fit on the panel at the same time. That works because views can be switched.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Views are different from layouts: they can be switched without affecting the active language, and without touching the globe button. Layouts cannot share views, so switching layouts *always* switches views.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Switching views
 | 
					 | 
				
			||||||
------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The model selected for switching views is less similar to "levels" known from physical keyboards, but closer to "rooms", which may resemble a game map.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Buttons don't have states. It's more of a model where each view is a room, and buttons are doors. Switching means moving to the next room, and buttons highlight according to which view/room they lead to or from.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There are two basic kinds of switching buttons: one way (`set_view`), and two way (`locking`). `locking` is the more sophisticated one. When placed inside `lock_view`, it is drawn highlighted, and goes to `unlock view`. When placed inside any other view, it behaves like `set_view`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 This diagram shows which buttons can switch between two views. Views are shown as circles, and buttons as rounded rectangles.
 | 
					 | 
				
			||||||
The two buttons are separate, and visible only in the view *from which the switch starts*. Note that the `locking` button is shown highlighted. That's because it's in `upper` view, which matches its `lock_view`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Latching
 | 
					 | 
				
			||||||
----------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
`locking` buttons provide a second mode of operation: latching. It's useful when the target view is needed only for a single button press, like entering a single accent or a single capital letter in Latin scripts.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
When a latching button is pressed, the keyboard remembers to come back to the current (source) view, and then the view is switched. If another `locking` button is pressed, the source view stays in memory. If a text button is pressed, the view from memory is shown again, and forgotten.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 In this diagram, the dashed line connects the view the typist is seeing to the view remembered for unlatching.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There are two ways to erase the memory without going back to the remembered view. Pressing the button again will permanently switch to the current view, and `set_view` will permanently switch to its target.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In the room metaphor, it's as if tying a thread inside the room before going through the door to the next one. And another `locking` door while holding the thread. Once the Minotaur is slain (text button pressed), the hero follows the thread back to the starting room.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The typist hero cuts the thread in two circumstances: when staying longer in the current room (press button again), or when moving through a `set_view` door.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 This diagram shows the possible ways to stop latching. One is by pressing a text button, which takes back to the original view. Another is pressing a locking button which appears highlighted (note that it can be any button, what matters is its `lock_view`). Finally, switching to another view using a button that doesn't keep the latch on forgets latching.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The layout author should pay attention that `set_view`'s lack of latching does not come as a surprise to typists.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Differences from keyboard levels
 | 
					 | 
				
			||||||
---------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Views are **not** like keyboard levels.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
On a physical keyboard, the number of buttons can not change when switching levels. In Squeekboard, they can have any arrangement of buttons you could imagine.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
When switching levels on a keyboard, for example by pressing Shift, the key press not only affects the meaning of other keys, but also tells the application that it's pressed down. In Squeekboard, pressing buttons to change layouts *does not* do anything but switch the layout. Pressing the switching button especially *does not* tell the application that it was pressed. (This is the reason Shift and AltGr modifiers are not implemented in Squeekboard.)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Why not use the "views" model?
 | 
					 | 
				
			||||||
-------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Squeekboard's goal is to support as many scripts as possible, and the author of the initial design doesn't know a whole lot. There are two problems with using the levels metaphor:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Firstly, the levels model assumes that there is a "base" and an "active" level. This does not work well with scripts that have different but equivalent modes of writing. An example is the Kana layout with Katakana and Hiragana, which are both "base".
 | 
					 | 
				
			||||||
Both systems could have been combined, but the view switching designer doesn't have enough experience with different scripts to do that. Some scripts may have different non-hierarchical ways to switch character groups (Balinese?), which could make combining hierarchy with free-form switching even harder.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Secondly, when dealing with languages with a hierarchy, we end up with extra work to eliminate nonsensical combinations. With "symbols" and "uppercase" levels, what does it mean to have both engaged? Eliminating that means extra work. Either validating layouts, so that it's not possible to engage "uppercase" from "symbols", or duplicating, so that "uppercase+symbols" is the same as just "symbols". With "accents" in the mix, this could become a challenge to design well.
 | 
					 | 
				
			||||||
@ -45,8 +45,6 @@
 | 
				
			|||||||
typedef struct _EekGtkKeyboardPrivate
 | 
					typedef struct _EekGtkKeyboardPrivate
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekRenderer *renderer; // owned, nullable
 | 
					    EekRenderer *renderer; // owned, nullable
 | 
				
			||||||
    struct render_geometry render_geometry; // mutable
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    EekboardContextService *eekboard_context; // unowned reference
 | 
					    EekboardContextService *eekboard_context; // unowned reference
 | 
				
			||||||
    struct submission *submission; // unowned reference
 | 
					    struct submission *submission; // unowned reference
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -74,23 +72,12 @@ eek_gtk_keyboard_real_realize (GtkWidget      *self)
 | 
				
			|||||||
    GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
 | 
					    GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void set_allocation_size(EekGtkKeyboard *gtk_keyboard,
 | 
					 | 
				
			||||||
    struct squeek_layout *layout, gdouble width, gdouble height)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    // This is where size-dependent surfaces would be released
 | 
					 | 
				
			||||||
    EekGtkKeyboardPrivate *priv =
 | 
					 | 
				
			||||||
        eek_gtk_keyboard_get_instance_private (gtk_keyboard);
 | 
					 | 
				
			||||||
    priv->render_geometry = eek_render_geometry_from_allocation_size(
 | 
					 | 
				
			||||||
        layout, width, height);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gboolean
 | 
					static gboolean
 | 
				
			||||||
eek_gtk_keyboard_real_draw (GtkWidget *self,
 | 
					eek_gtk_keyboard_real_draw (GtkWidget *self,
 | 
				
			||||||
                            cairo_t   *cr)
 | 
					                            cairo_t   *cr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
 | 
					 | 
				
			||||||
    EekGtkKeyboardPrivate *priv =
 | 
					    EekGtkKeyboardPrivate *priv =
 | 
				
			||||||
        eek_gtk_keyboard_get_instance_private (keyboard);
 | 
					        eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
 | 
				
			||||||
    GtkAllocation allocation;
 | 
					    GtkAllocation allocation;
 | 
				
			||||||
    gtk_widget_get_allocation (self, &allocation);
 | 
					    gtk_widget_get_allocation (self, &allocation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -105,14 +92,15 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
 | 
				
			|||||||
                    priv->keyboard,
 | 
					                    priv->keyboard,
 | 
				
			||||||
                    pcontext);
 | 
					                    pcontext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        set_allocation_size (keyboard, priv->keyboard->layout,
 | 
					        eek_renderer_set_allocation_size (priv->renderer,
 | 
				
			||||||
            allocation.width, allocation.height);
 | 
					                                          priv->keyboard->layout,
 | 
				
			||||||
 | 
					                                          allocation.width,
 | 
				
			||||||
 | 
					                                          allocation.height);
 | 
				
			||||||
        eek_renderer_set_scale_factor (priv->renderer,
 | 
					        eek_renderer_set_scale_factor (priv->renderer,
 | 
				
			||||||
                                       gtk_widget_get_scale_factor (self));
 | 
					                                       gtk_widget_get_scale_factor (self));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    eek_renderer_render_keyboard (priv->renderer, priv->render_geometry,
 | 
					    eek_renderer_render_keyboard (priv->renderer, priv->submission, cr, priv->keyboard);
 | 
				
			||||||
        priv->submission, cr, priv->keyboard);
 | 
					 | 
				
			||||||
    return FALSE;
 | 
					    return FALSE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -129,9 +117,8 @@ static void
 | 
				
			|||||||
eek_gtk_keyboard_real_size_allocate (GtkWidget     *self,
 | 
					eek_gtk_keyboard_real_size_allocate (GtkWidget     *self,
 | 
				
			||||||
                                     GtkAllocation *allocation)
 | 
					                                     GtkAllocation *allocation)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
 | 
					 | 
				
			||||||
    EekGtkKeyboardPrivate *priv =
 | 
					    EekGtkKeyboardPrivate *priv =
 | 
				
			||||||
        eek_gtk_keyboard_get_instance_private (keyboard);
 | 
					        eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
 | 
				
			||||||
    // check if the change would switch types
 | 
					    // check if the change would switch types
 | 
				
			||||||
    enum squeek_arrangement_kind new_type = get_type(
 | 
					    enum squeek_arrangement_kind new_type = get_type(
 | 
				
			||||||
                (uint32_t)(allocation->width - allocation->x),
 | 
					                (uint32_t)(allocation->width - allocation->x),
 | 
				
			||||||
@ -143,8 +130,10 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget     *self,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (priv->renderer) {
 | 
					    if (priv->renderer) {
 | 
				
			||||||
        set_allocation_size (keyboard, priv->keyboard->layout,
 | 
					        eek_renderer_set_allocation_size (priv->renderer,
 | 
				
			||||||
            allocation->width, allocation->height);
 | 
					                                          priv->keyboard->layout,
 | 
				
			||||||
 | 
					                                          allocation->width,
 | 
				
			||||||
 | 
					                                          allocation->height);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->
 | 
					    GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->
 | 
				
			||||||
@ -173,7 +162,7 @@ static void depress(EekGtkKeyboard *self,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    squeek_layout_depress(priv->keyboard->layout,
 | 
					    squeek_layout_depress(priv->keyboard->layout,
 | 
				
			||||||
                          priv->submission,
 | 
					                          priv->submission,
 | 
				
			||||||
                          x, y, priv->render_geometry.widget_to_layout, time, self);
 | 
					                          x, y, eek_renderer_get_transformation(priv->renderer), time, self);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void drag(EekGtkKeyboard *self,
 | 
					static void drag(EekGtkKeyboard *self,
 | 
				
			||||||
@ -185,7 +174,7 @@ static void drag(EekGtkKeyboard *self,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
					    squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
				
			||||||
                       priv->submission,
 | 
					                       priv->submission,
 | 
				
			||||||
                       x, y, priv->render_geometry.widget_to_layout, time,
 | 
					                       x, y, eek_renderer_get_transformation(priv->renderer), time,
 | 
				
			||||||
                       priv->eekboard_context, self);
 | 
					                       priv->eekboard_context, self);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -196,7 +185,8 @@ static void release(EekGtkKeyboard *self, guint32 time)
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
					    squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
				
			||||||
                          priv->submission, priv->render_geometry.widget_to_layout, time,
 | 
					                          priv->submission,
 | 
				
			||||||
 | 
					                          eek_renderer_get_transformation(priv->renderer), time,
 | 
				
			||||||
                          priv->eekboard_context, self);
 | 
					                          priv->eekboard_context, self);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -406,24 +396,6 @@ eek_gtk_keyboard_new (EekboardContextService *eekservice,
 | 
				
			|||||||
    priv->submission = submission;
 | 
					    priv->submission = submission;
 | 
				
			||||||
    priv->layout = layout;
 | 
					    priv->layout = layout;
 | 
				
			||||||
    priv->renderer = NULL;
 | 
					    priv->renderer = NULL;
 | 
				
			||||||
    // This should really be done on initialization.
 | 
					 | 
				
			||||||
    // Before the widget is allocated,
 | 
					 | 
				
			||||||
    // we don't really know what geometry it takes.
 | 
					 | 
				
			||||||
    // When it's off the screen, we also kinda don't.
 | 
					 | 
				
			||||||
    struct render_geometry initial_geometry = {
 | 
					 | 
				
			||||||
        // Set to 100 just to make sure if there's any attempt to use it,
 | 
					 | 
				
			||||||
        // it actually gives plausible results instead of blowing up,
 | 
					 | 
				
			||||||
        // e.g. on zero division.
 | 
					 | 
				
			||||||
        .allocation_width = 100,
 | 
					 | 
				
			||||||
        .allocation_height = 100,
 | 
					 | 
				
			||||||
        .widget_to_layout = {
 | 
					 | 
				
			||||||
            .origin_x = 0,
 | 
					 | 
				
			||||||
            .origin_y = 0,
 | 
					 | 
				
			||||||
            .scale = 1,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    priv->render_geometry = initial_geometry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_signal_connect (eekservice,
 | 
					    g_signal_connect (eekservice,
 | 
				
			||||||
                      "notify::keyboard",
 | 
					                      "notify::keyboard",
 | 
				
			||||||
                      G_CALLBACK(on_notify_keyboard),
 | 
					                      G_CALLBACK(on_notify_keyboard),
 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,6 @@
 | 
				
			|||||||
#include <glib.h>
 | 
					#include <glib.h>
 | 
				
			||||||
#include <gtk/gtk.h>
 | 
					#include <gtk/gtk.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "eek/eek-renderer.h"
 | 
					 | 
				
			||||||
#include "eek/eek-types.h"
 | 
					#include "eek/eek-types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct submission;
 | 
					struct submission;
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,8 @@
 | 
				
			|||||||
 * 02110-1301 USA
 | 
					 * 02110-1301 USA
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <math.h>
 | 
					#include <math.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
 | 
					#include <gdk-pixbuf/gdk-pixbuf.h>
 | 
				
			||||||
@ -31,6 +33,10 @@
 | 
				
			|||||||
static void render_button_label (cairo_t *cr, GtkStyleContext *ctx,
 | 
					static void render_button_label (cairo_t *cr, GtkStyleContext *ctx,
 | 
				
			||||||
                                                const gchar *label, EekBounds bounds);
 | 
					                                                const gchar *label, EekBounds bounds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void eek_render_button                         (EekRenderer *self,
 | 
				
			||||||
 | 
					                                                cairo_t     *cr, const struct squeek_button *button,
 | 
				
			||||||
 | 
					                                                gboolean     pressed, gboolean locked);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
render_outline (cairo_t     *cr,
 | 
					render_outline (cairo_t     *cr,
 | 
				
			||||||
                GtkStyleContext *ctx,
 | 
					                GtkStyleContext *ctx,
 | 
				
			||||||
@ -54,21 +60,21 @@ render_outline (cairo_t     *cr,
 | 
				
			|||||||
        position.x, position.y, position.width, position.height);
 | 
					        position.x, position.y, position.width, position.height);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Rust interface
 | 
					static void render_button_in_context(gint scale_factor,
 | 
				
			||||||
void eek_render_button_in_context(uint32_t scale_factor,
 | 
					 | 
				
			||||||
                                     cairo_t     *cr,
 | 
					                                     cairo_t     *cr,
 | 
				
			||||||
                                     GtkStyleContext *ctx,
 | 
					                                     GtkStyleContext *ctx,
 | 
				
			||||||
                                     EekBounds bounds,
 | 
					                                     const struct squeek_button *button) {
 | 
				
			||||||
                                     const char *icon_name,
 | 
					 | 
				
			||||||
                                     const gchar *label) {
 | 
					 | 
				
			||||||
    /* blank background */
 | 
					    /* blank background */
 | 
				
			||||||
    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
 | 
					    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
 | 
				
			||||||
    cairo_paint (cr);
 | 
					    cairo_paint (cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EekBounds bounds = squeek_button_get_bounds(button);
 | 
				
			||||||
    render_outline (cr, ctx, bounds);
 | 
					    render_outline (cr, ctx, bounds);
 | 
				
			||||||
    cairo_paint (cr);
 | 
					    cairo_paint (cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* render icon (if any) */
 | 
					    /* render icon (if any) */
 | 
				
			||||||
 | 
					    const char *icon_name = squeek_button_get_icon_name(button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (icon_name) {
 | 
					    if (icon_name) {
 | 
				
			||||||
        cairo_surface_t *icon_surface =
 | 
					        cairo_surface_t *icon_surface =
 | 
				
			||||||
            eek_renderer_get_icon_surface (icon_name, 16, scale_factor);
 | 
					            eek_renderer_get_icon_surface (icon_name, 16, scale_factor);
 | 
				
			||||||
@ -98,27 +104,25 @@ void eek_render_button_in_context(uint32_t scale_factor,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const gchar *label = squeek_button_get_label(button);
 | 
				
			||||||
    if (label) {
 | 
					    if (label) {
 | 
				
			||||||
        render_button_label (cr, ctx, label, bounds);
 | 
					        render_button_label (cr, ctx, label, squeek_button_get_bounds(button));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Prepare context for drawing the button.
 | 
					void
 | 
				
			||||||
/// The context MUST be released using the corresponing "put" procedure
 | 
					eek_render_button (EekRenderer *self,
 | 
				
			||||||
/// before drawing the next button.
 | 
					            cairo_t     *cr,
 | 
				
			||||||
/// Interface for Rust.
 | 
					            const struct squeek_button *button,
 | 
				
			||||||
GtkStyleContext *
 | 
					               gboolean     pressed,
 | 
				
			||||||
eek_get_style_context_for_button (EekRenderer *self,
 | 
					               gboolean     locked)
 | 
				
			||||||
                                  const char *name,
 | 
					 | 
				
			||||||
                                  const char *outline_name,
 | 
					 | 
				
			||||||
                                  const char *locked_class,
 | 
					 | 
				
			||||||
               uint64_t     pressed)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    GtkStyleContext *ctx = self->button_context;
 | 
					    GtkStyleContext *ctx = self->button_context;
 | 
				
			||||||
    /* Set the name of the button on the widget path, using the name obtained
 | 
					    /* Set the name of the button on the widget path, using the name obtained
 | 
				
			||||||
       from the button's symbol. */
 | 
					       from the button's symbol. */
 | 
				
			||||||
    g_autoptr (GtkWidgetPath) path = NULL;
 | 
					    g_autoptr (GtkWidgetPath) path = NULL;
 | 
				
			||||||
    path = gtk_widget_path_copy (gtk_style_context_get_path (ctx));
 | 
					    path = gtk_widget_path_copy (gtk_style_context_get_path (ctx));
 | 
				
			||||||
 | 
					    const char *name = squeek_button_get_name(button);
 | 
				
			||||||
    gtk_widget_path_iter_set_name (path, -1, name);
 | 
					    gtk_widget_path_iter_set_name (path, -1, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Update the style context with the updated widget path. */
 | 
					    /* Update the style context with the updated widget path. */
 | 
				
			||||||
@ -127,22 +131,19 @@ eek_get_style_context_for_button (EekRenderer *self,
 | 
				
			|||||||
       (pressed) or normal. */
 | 
					       (pressed) or normal. */
 | 
				
			||||||
    gtk_style_context_set_state(ctx,
 | 
					    gtk_style_context_set_state(ctx,
 | 
				
			||||||
        pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
 | 
					        pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
 | 
				
			||||||
    if (locked_class) {
 | 
					    const char *outline_name = squeek_button_get_outline_name(button);
 | 
				
			||||||
        gtk_style_context_add_class(ctx, locked_class);
 | 
					    if (locked) {
 | 
				
			||||||
 | 
					        gtk_style_context_add_class(ctx, "locked");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    gtk_style_context_add_class(ctx, outline_name);
 | 
					    gtk_style_context_add_class(ctx, outline_name);
 | 
				
			||||||
    return ctx;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Interface for Rust.
 | 
					    render_button_in_context(self->scale_factor, cr, ctx, button);
 | 
				
			||||||
void eek_put_style_context_for_button(GtkStyleContext *ctx,
 | 
					
 | 
				
			||||||
                                      const char *outline_name,
 | 
					 | 
				
			||||||
                                      const char *locked_class) {
 | 
					 | 
				
			||||||
    // Save and restore functions don't work if gtk_render_* was used in between
 | 
					    // Save and restore functions don't work if gtk_render_* was used in between
 | 
				
			||||||
    gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
 | 
					    gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
 | 
				
			||||||
    gtk_style_context_remove_class(ctx, outline_name);
 | 
					    gtk_style_context_remove_class(ctx, outline_name);
 | 
				
			||||||
    if (locked_class) {
 | 
					    if (locked) {
 | 
				
			||||||
        gtk_style_context_remove_class(ctx, locked_class);
 | 
					        gtk_style_context_remove_class(ctx, "locked");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -193,23 +194,22 @@ render_button_label (cairo_t     *cr,
 | 
				
			|||||||
// FIXME: Pass just the active modifiers instead of entire submission
 | 
					// FIXME: Pass just the active modifiers instead of entire submission
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
eek_renderer_render_keyboard (EekRenderer *self,
 | 
					eek_renderer_render_keyboard (EekRenderer *self,
 | 
				
			||||||
                              struct render_geometry geometry,
 | 
					 | 
				
			||||||
                              struct submission *submission,
 | 
					                              struct submission *submission,
 | 
				
			||||||
                                   cairo_t     *cr,
 | 
					                                   cairo_t     *cr,
 | 
				
			||||||
                              LevelKeyboard *keyboard)
 | 
					                              LevelKeyboard *keyboard)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    g_return_if_fail (geometry.allocation_width > 0.0);
 | 
					    g_return_if_fail (self->allocation_width > 0.0);
 | 
				
			||||||
    g_return_if_fail (geometry.allocation_height > 0.0);
 | 
					    g_return_if_fail (self->allocation_height > 0.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Paint the background covering the entire widget area */
 | 
					    /* Paint the background covering the entire widget area */
 | 
				
			||||||
    gtk_render_background (self->view_context,
 | 
					    gtk_render_background (self->view_context,
 | 
				
			||||||
                           cr,
 | 
					                           cr,
 | 
				
			||||||
                           0, 0,
 | 
					                           0, 0,
 | 
				
			||||||
                           geometry.allocation_width, geometry.allocation_height);
 | 
					                           self->allocation_width, self->allocation_height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cairo_save(cr);
 | 
					    cairo_save(cr);
 | 
				
			||||||
    cairo_translate (cr, geometry.widget_to_layout.origin_x, geometry.widget_to_layout.origin_y);
 | 
					    cairo_translate (cr, self->widget_to_layout.origin_x, self->widget_to_layout.origin_y);
 | 
				
			||||||
    cairo_scale (cr, geometry.widget_to_layout.scale, geometry.widget_to_layout.scale);
 | 
					    cairo_scale (cr, self->widget_to_layout.scale, self->widget_to_layout.scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    squeek_draw_layout_base_view(keyboard->layout, self, cr);
 | 
					    squeek_draw_layout_base_view(keyboard->layout, self, cr);
 | 
				
			||||||
    squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);
 | 
					    squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);
 | 
				
			||||||
@ -261,6 +261,8 @@ static void
 | 
				
			|||||||
renderer_init (EekRenderer *self)
 | 
					renderer_init (EekRenderer *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    self->pcontext = NULL;
 | 
					    self->pcontext = NULL;
 | 
				
			||||||
 | 
					    self->allocation_width = 0.0;
 | 
				
			||||||
 | 
					    self->allocation_height = 0.0;
 | 
				
			||||||
    self->scale_factor = 1;
 | 
					    self->scale_factor = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self->css_provider = squeek_load_style();
 | 
					    self->css_provider = squeek_load_style();
 | 
				
			||||||
@ -287,7 +289,7 @@ eek_renderer_new (LevelKeyboard  *keyboard,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    gtk_style_context_add_provider (renderer->view_context,
 | 
					    gtk_style_context_add_provider (renderer->view_context,
 | 
				
			||||||
        GTK_STYLE_PROVIDER(renderer->css_provider),
 | 
					        GTK_STYLE_PROVIDER(renderer->css_provider),
 | 
				
			||||||
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 | 
					        GTK_STYLE_PROVIDER_PRIORITY_USER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Create a style context for the buttons */
 | 
					    /* Create a style context for the buttons */
 | 
				
			||||||
    path = gtk_widget_path_new();
 | 
					    path = gtk_widget_path_new();
 | 
				
			||||||
@ -303,22 +305,26 @@ eek_renderer_new (LevelKeyboard  *keyboard,
 | 
				
			|||||||
    gtk_style_context_set_state (renderer->button_context, GTK_STATE_FLAG_NORMAL);
 | 
					    gtk_style_context_set_state (renderer->button_context, GTK_STATE_FLAG_NORMAL);
 | 
				
			||||||
    gtk_style_context_add_provider (renderer->button_context,
 | 
					    gtk_style_context_add_provider (renderer->button_context,
 | 
				
			||||||
        GTK_STYLE_PROVIDER(renderer->css_provider),
 | 
					        GTK_STYLE_PROVIDER(renderer->css_provider),
 | 
				
			||||||
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 | 
					        GTK_STYLE_PROVIDER_PRIORITY_USER);
 | 
				
			||||||
    return renderer;
 | 
					    return renderer;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct render_geometry
 | 
					void
 | 
				
			||||||
eek_render_geometry_from_allocation_size (struct squeek_layout *layout,
 | 
					eek_renderer_set_allocation_size (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                                  struct squeek_layout *layout,
 | 
				
			||||||
                                  gdouble      width,
 | 
					                                  gdouble      width,
 | 
				
			||||||
                                  gdouble      height)
 | 
					                                  gdouble      height)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct render_geometry ret = {
 | 
					    g_return_if_fail (width > 0.0 && height > 0.0);
 | 
				
			||||||
        .allocation_width = width,
 | 
					
 | 
				
			||||||
        .allocation_height = height,
 | 
					    renderer->allocation_width = width;
 | 
				
			||||||
        .widget_to_layout = squeek_layout_calculate_transformation(
 | 
					    renderer->allocation_height = height;
 | 
				
			||||||
            layout, width, height),
 | 
					
 | 
				
			||||||
    };
 | 
					    renderer->widget_to_layout = squeek_layout_calculate_transformation(
 | 
				
			||||||
    return ret;
 | 
					                layout,
 | 
				
			||||||
 | 
					                renderer->allocation_width, renderer->allocation_height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This is where size-dependent surfaces would be released
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
@ -327,11 +333,6 @@ eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale)
 | 
				
			|||||||
    renderer->scale_factor = scale;
 | 
					    renderer->scale_factor = scale;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Rust interface.
 | 
					 | 
				
			||||||
uint32_t eek_renderer_get_scale_factor(EekRenderer *renderer) {
 | 
					 | 
				
			||||||
    return renderer->scale_factor;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cairo_surface_t *
 | 
					cairo_surface_t *
 | 
				
			||||||
eek_renderer_get_icon_surface (const gchar *icon_name,
 | 
					eek_renderer_get_icon_surface (const gchar *icon_name,
 | 
				
			||||||
                               gint size,
 | 
					                               gint size,
 | 
				
			||||||
@ -355,3 +356,8 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return surface;
 | 
					    return surface;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct transformation
 | 
				
			||||||
 | 
					eek_renderer_get_transformation (EekRenderer *renderer) {
 | 
				
			||||||
 | 
					    return renderer->widget_to_layout;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -41,23 +41,22 @@ typedef struct EekRenderer
 | 
				
			|||||||
    gchar *extra_style; // owned
 | 
					    gchar *extra_style; // owned
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Mutable state
 | 
					    // Mutable state
 | 
				
			||||||
    gint scale_factor; /* the outputs scale factor */
 | 
					 | 
				
			||||||
} EekRenderer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Mutable part of the renderer state.
 | 
					 | 
				
			||||||
/// TODO: Possibly should include scale factor.
 | 
					 | 
				
			||||||
struct render_geometry {
 | 
					 | 
				
			||||||
    /// Background extents
 | 
					    /// Background extents
 | 
				
			||||||
    gdouble allocation_width;
 | 
					    gdouble allocation_width;
 | 
				
			||||||
    gdouble allocation_height;
 | 
					    gdouble allocation_height;
 | 
				
			||||||
 | 
					    gint scale_factor; /* the outputs scale factor */
 | 
				
			||||||
    /// Coords transformation
 | 
					    /// Coords transformation
 | 
				
			||||||
    struct transformation widget_to_layout;
 | 
					    struct transformation widget_to_layout;
 | 
				
			||||||
};
 | 
					} EekRenderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GType            eek_renderer_get_type         (void) G_GNUC_CONST;
 | 
					GType            eek_renderer_get_type         (void) G_GNUC_CONST;
 | 
				
			||||||
EekRenderer     *eek_renderer_new              (LevelKeyboard     *keyboard,
 | 
					EekRenderer     *eek_renderer_new              (LevelKeyboard     *keyboard,
 | 
				
			||||||
                                                PangoContext    *pcontext);
 | 
					                                                PangoContext    *pcontext);
 | 
				
			||||||
 | 
					void             eek_renderer_set_allocation_size
 | 
				
			||||||
 | 
					                                               (EekRenderer     *renderer, struct squeek_layout *layout,
 | 
				
			||||||
 | 
					                                                gdouble          width,
 | 
				
			||||||
 | 
					                                                gdouble          height);
 | 
				
			||||||
void             eek_renderer_set_scale_factor (EekRenderer     *renderer,
 | 
					void             eek_renderer_set_scale_factor (EekRenderer     *renderer,
 | 
				
			||||||
                                                gint             scale);
 | 
					                                                gint             scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -65,14 +64,13 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar     *icon_name,
 | 
				
			|||||||
                                                gint             size,
 | 
					                                                gint             size,
 | 
				
			||||||
                                                gint             scale);
 | 
					                                                gint             scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void             eek_renderer_render_keyboard  (EekRenderer     *renderer, struct render_geometry geometry, struct submission *submission,
 | 
					void             eek_renderer_render_keyboard  (EekRenderer     *renderer, struct submission *submission,
 | 
				
			||||||
                                                cairo_t         *cr, LevelKeyboard *keyboard);
 | 
					                                                cairo_t         *cr, LevelKeyboard *keyboard);
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
eek_renderer_free (EekRenderer        *self);
 | 
					eek_renderer_free (EekRenderer        *self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct render_geometry
 | 
					struct transformation
 | 
				
			||||||
eek_render_geometry_from_allocation_size (struct squeek_layout *layout,
 | 
					eek_renderer_get_transformation (EekRenderer *renderer);
 | 
				
			||||||
    gdouble      width, gdouble      height);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
G_END_DECLS
 | 
					G_END_DECLS
 | 
				
			||||||
#endif  /* EEK_RENDERER_H */
 | 
					#endif  /* EEK_RENDERER_H */
 | 
				
			||||||
 | 
				
			|||||||
@ -90,5 +90,13 @@ struct transformation {
 | 
				
			|||||||
    gdouble scale;
 | 
					    gdouble scale;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct squeek_button;
 | 
				
			||||||
 | 
					struct squeek_row;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Represents the path to the button within a view
 | 
				
			||||||
 | 
					struct button_place {
 | 
				
			||||||
 | 
					    const struct squeek_row *row;
 | 
				
			||||||
 | 
					    const struct squeek_button *button;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
G_END_DECLS
 | 
					G_END_DECLS
 | 
				
			||||||
#endif  /* EEK_TYPES_H */
 | 
					#endif  /* EEK_TYPES_H */
 | 
				
			||||||
 | 
				
			|||||||
@ -128,22 +128,30 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state, uint32_t timestamp) {
 | 
					eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state, uint32_t timestamp) {
 | 
				
			||||||
    gchar *layout_name = state->layout_name;
 | 
					    gchar *layout_name = state->overlay_name;
 | 
				
			||||||
    gchar *overlay_name = state->overlay_name;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // try to get the best keyboard layout
 | 
					 | 
				
			||||||
    if (layout_name == NULL) {
 | 
					    if (layout_name == NULL) {
 | 
				
			||||||
        layout_name = "us";
 | 
					        layout_name = state->layout_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (state->purpose) {
 | 
				
			||||||
 | 
					        case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
 | 
				
			||||||
 | 
					        case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
 | 
				
			||||||
 | 
					            layout_name = "number";
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
 | 
				
			||||||
 | 
					            layout_name = "terminal";
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            ;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (layout_name == NULL) {
 | 
				
			||||||
 | 
					            layout_name = "us";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // overlay is "Normal" for most layouts, we will only look for "terminal" in rust code.
 | 
					 | 
				
			||||||
    // for now just avoid passing a null pointer
 | 
					 | 
				
			||||||
    if (overlay_name == NULL) {
 | 
					 | 
				
			||||||
        overlay_name = "";    // fallback to Normal
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // generic part follows
 | 
					    // generic part follows
 | 
				
			||||||
    struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement, state->purpose, overlay_name);
 | 
					    struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement);
 | 
				
			||||||
    LevelKeyboard *keyboard = level_keyboard_new(layout);
 | 
					    LevelKeyboard *keyboard = level_keyboard_new(layout);
 | 
				
			||||||
    // set as current
 | 
					    // set as current
 | 
				
			||||||
    LevelKeyboard *previous_keyboard = context->keyboard;
 | 
					    LevelKeyboard *previous_keyboard = context->keyboard;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
project(
 | 
					project(
 | 
				
			||||||
    'squeekboard',
 | 
					    'squeekboard',
 | 
				
			||||||
    'c', 'rust',
 | 
					    'c', 'rust',
 | 
				
			||||||
    version: '1.14.0',
 | 
					    version: '1.10.0',
 | 
				
			||||||
    license: 'GPLv3',
 | 
					    license: 'GPLv3',
 | 
				
			||||||
    meson_version: '>=0.51.0',
 | 
					    meson_version: '>=0.51.0',
 | 
				
			||||||
    default_options: [
 | 
					    default_options: [
 | 
				
			||||||
 | 
				
			|||||||
@ -294,8 +294,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        The serial number reflects the last state of the zwp_input_method_v2
 | 
					        The serial number reflects the last state of the zwp_input_method_v2
 | 
				
			||||||
        object known to the client. The value of the serial argument must be
 | 
					        object known to the client. The value of the serial argument must be
 | 
				
			||||||
        equal to the number of done events already issued on that object.
 | 
					        equal to the number of commit requests already issued on that object.
 | 
				
			||||||
        When the compositor receives a commit request with a serial different than
 | 
					        When the compositor receives a done event with a serial different than
 | 
				
			||||||
        the number of past commit requests, it must proceed as normal, except
 | 
					        the number of past commit requests, it must proceed as normal, except
 | 
				
			||||||
        it should not change the current state of the zwp_input_method_v2
 | 
					        it should not change the current state of the zwp_input_method_v2
 | 
				
			||||||
        object.
 | 
					        object.
 | 
				
			||||||
 | 
				
			|||||||
@ -10,14 +10,13 @@ pub struct KeySym(pub String);
 | 
				
			|||||||
type View = String;
 | 
					type View = String;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Use to send modified keypresses
 | 
					/// Use to send modified keypresses
 | 
				
			||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum Modifier {
 | 
					pub enum Modifier {
 | 
				
			||||||
    /// Control and Alt are the only modifiers
 | 
					    /// Control and Alt are the only modifiers
 | 
				
			||||||
    /// which doesn't interfere with levels,
 | 
					    /// which doesn't interfere with levels,
 | 
				
			||||||
    /// so it's simple to implement as levels are deprecated in squeekboard.
 | 
					    /// so it's simple to implement as levels are deprecated in squeekboard.
 | 
				
			||||||
    Control,
 | 
					    Control,
 | 
				
			||||||
    Alt,
 | 
					    Alt,
 | 
				
			||||||
    Mod4,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Action to perform on the keypress and, in reverse, on keyrelease
 | 
					/// Action to perform on the keypress and, in reverse, on keyrelease
 | 
				
			||||||
@ -30,11 +29,6 @@ pub enum Action {
 | 
				
			|||||||
        lock: View,
 | 
					        lock: View,
 | 
				
			||||||
        /// When unlocked by pressing it or emitting a key
 | 
					        /// When unlocked by pressing it or emitting a key
 | 
				
			||||||
        unlock: View,
 | 
					        unlock: View,
 | 
				
			||||||
        /// Whether key has a latched state
 | 
					 | 
				
			||||||
        /// that pops when another key is pressed.
 | 
					 | 
				
			||||||
        latches: bool,
 | 
					 | 
				
			||||||
        /// Should take on *locked* appearance whenever latch comes back to those views.
 | 
					 | 
				
			||||||
        looks_locked_from: Vec<View>,
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    /// Hold this modifier for as long as the button is pressed
 | 
					    /// Hold this modifier for as long as the button is pressed
 | 
				
			||||||
    ApplyModifier(Modifier),
 | 
					    ApplyModifier(Modifier),
 | 
				
			||||||
@ -54,24 +48,14 @@ pub enum Action {
 | 
				
			|||||||
impl Action {
 | 
					impl Action {
 | 
				
			||||||
    pub fn is_locked(&self, view_name: &str) -> bool {
 | 
					    pub fn is_locked(&self, view_name: &str) -> bool {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Action::LockView { lock, unlock: _, latches: _, looks_locked_from: _ } => lock == view_name,
 | 
					            Action::LockView { lock, unlock: _ } => lock == view_name,
 | 
				
			||||||
            _ => false,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    pub fn has_locked_appearance_from(&self, locked_view_name: &str) -> bool {
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Action::LockView { lock: _, unlock: _, latches: _, looks_locked_from } => {
 | 
					 | 
				
			||||||
                looks_locked_from.iter()
 | 
					 | 
				
			||||||
                    .find(|view| locked_view_name == view.as_str())
 | 
					 | 
				
			||||||
                    .is_some()
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            _ => false,
 | 
					            _ => false,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub fn is_active(&self, view_name: &str) -> bool {
 | 
					    pub fn is_active(&self, view_name: &str) -> bool {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Action::SetView(view) => view == view_name,
 | 
					            Action::SetView(view) => view == view_name,
 | 
				
			||||||
            Action::LockView { lock, unlock: _, latches: _, looks_locked_from: _ } => lock == view_name,
 | 
					            Action::LockView { lock, unlock: _ } => lock == view_name,
 | 
				
			||||||
            _ => false,
 | 
					            _ => false,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,30 +1,32 @@
 | 
				
			|||||||
/* Copyright (C) 2020-2021 Purism SPC
 | 
					/**! The parsing of the data files for layouts */
 | 
				
			||||||
 * SPDX-License-Identifier: GPL-3.0+
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*! Parsing of the data files containing layouts */
 | 
					// TODO: find a nice way to make sure non-positive sizes don't break layouts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::cell::RefCell;
 | 
					use std::cell::RefCell;
 | 
				
			||||||
use std::collections::{ HashMap, HashSet };
 | 
					use std::collections::{ HashMap, HashSet };
 | 
				
			||||||
 | 
					use std::env;
 | 
				
			||||||
use std::ffi::CString;
 | 
					use std::ffi::CString;
 | 
				
			||||||
 | 
					use std::fmt;
 | 
				
			||||||
use std::fs;
 | 
					use std::fs;
 | 
				
			||||||
 | 
					use std::io;
 | 
				
			||||||
use std::path::PathBuf;
 | 
					use std::path::PathBuf;
 | 
				
			||||||
use std::rc::Rc;
 | 
					use std::rc::Rc;
 | 
				
			||||||
use std::vec::Vec;
 | 
					use std::vec::Vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use xkbcommon::xkb;
 | 
					use xkbcommon::xkb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{ Error, LoadError };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use ::action;
 | 
					use ::action;
 | 
				
			||||||
use ::keyboard::{
 | 
					use ::keyboard::{
 | 
				
			||||||
    KeyState, PressType,
 | 
					    KeyState, PressType,
 | 
				
			||||||
    generate_keymaps, generate_keycodes, KeyCode, FormattingError
 | 
					    generate_keymaps, generate_keycodes, KeyCode, FormattingError
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use ::layout;
 | 
					use ::layout;
 | 
				
			||||||
 | 
					use ::layout::ArrangementKind;
 | 
				
			||||||
use ::logging;
 | 
					use ::logging;
 | 
				
			||||||
use ::util::hash_map_map;
 | 
					 | 
				
			||||||
use ::resources;
 | 
					use ::resources;
 | 
				
			||||||
 | 
					use ::util::c::as_str;
 | 
				
			||||||
 | 
					use ::util::hash_map_map;
 | 
				
			||||||
 | 
					use ::xdg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// traits, derives
 | 
					// traits, derives
 | 
				
			||||||
use serde::Deserialize;
 | 
					use serde::Deserialize;
 | 
				
			||||||
@ -32,7 +34,206 @@ use std::io::BufReader;
 | 
				
			|||||||
use std::iter::FromIterator;
 | 
					use std::iter::FromIterator;
 | 
				
			||||||
use ::logging::Warn;
 | 
					use ::logging::Warn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: find a nice way to make sure non-positive sizes don't break layouts
 | 
					/// Gathers stuff defined in C or called by C
 | 
				
			||||||
 | 
					pub mod c {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    use std::os::raw::c_char;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    pub extern "C"
 | 
				
			||||||
 | 
					    fn squeek_load_layout(
 | 
				
			||||||
 | 
					        name: *const c_char,
 | 
				
			||||||
 | 
					        type_: u32,
 | 
				
			||||||
 | 
					    ) -> *mut ::layout::Layout {
 | 
				
			||||||
 | 
					        let type_ = match type_ {
 | 
				
			||||||
 | 
					            0 => ArrangementKind::Base,
 | 
				
			||||||
 | 
					            1 => ArrangementKind::Wide,
 | 
				
			||||||
 | 
					            _ => panic!("Bad enum value"),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let name = as_str(&name)
 | 
				
			||||||
 | 
					            .expect("Bad layout name")
 | 
				
			||||||
 | 
					            .expect("Empty layout name");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (kind, layout) = load_layout_data_with_fallback(&name, type_);
 | 
				
			||||||
 | 
					        let layout = ::layout::Layout::new(layout, kind);
 | 
				
			||||||
 | 
					        Box::into_raw(Box::new(layout))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const FALLBACK_LAYOUT_NAME: &str = "us";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub enum LoadError {
 | 
				
			||||||
 | 
					    BadData(Error),
 | 
				
			||||||
 | 
					    MissingResource,
 | 
				
			||||||
 | 
					    BadResource(serde_yaml::Error),
 | 
				
			||||||
 | 
					    BadKeyMap(FormattingError),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for LoadError {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        use self::LoadError::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            BadData(e) => write!(f, "Bad data: {}", e),
 | 
				
			||||||
 | 
					            MissingResource => write!(f, "Missing resource"),
 | 
				
			||||||
 | 
					            BadResource(e) => write!(f, "Bad resource: {}", e),
 | 
				
			||||||
 | 
					            BadKeyMap(e) => write!(f, "Bad key map: {}", e),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq)]
 | 
				
			||||||
 | 
					enum DataSource {
 | 
				
			||||||
 | 
					    File(PathBuf),
 | 
				
			||||||
 | 
					    Resource(String),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for DataSource {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            DataSource::File(path) => write!(f, "Path: {:?}", path.display()),
 | 
				
			||||||
 | 
					            DataSource::Resource(name) => write!(f, "Resource: {}", name),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LayoutSource = (ArrangementKind, DataSource);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Lists possible sources, with 0 as the most preferred one
 | 
				
			||||||
 | 
					/// Trying order: native lang of the right kind, native base,
 | 
				
			||||||
 | 
					/// fallback lang of the right kind, fallback base
 | 
				
			||||||
 | 
					fn list_layout_sources(
 | 
				
			||||||
 | 
					    name: &str,
 | 
				
			||||||
 | 
					    kind: ArrangementKind,
 | 
				
			||||||
 | 
					    keyboards_path: Option<PathBuf>,
 | 
				
			||||||
 | 
					) -> Vec<LayoutSource> {
 | 
				
			||||||
 | 
					    // Just a simplification of often called code.
 | 
				
			||||||
 | 
					    let add_by_name = |
 | 
				
			||||||
 | 
					        mut ret: Vec<LayoutSource>,
 | 
				
			||||||
 | 
					        name: &str,
 | 
				
			||||||
 | 
					        kind: &ArrangementKind,
 | 
				
			||||||
 | 
					    | -> Vec<LayoutSource> {
 | 
				
			||||||
 | 
					        if let Some(path) = keyboards_path.clone() {
 | 
				
			||||||
 | 
					            ret.push((
 | 
				
			||||||
 | 
					                kind.clone(),
 | 
				
			||||||
 | 
					                DataSource::File(
 | 
				
			||||||
 | 
					                    path.join(name.to_owned()).with_extension("yaml")
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ret.push((
 | 
				
			||||||
 | 
					            kind.clone(),
 | 
				
			||||||
 | 
					            DataSource::Resource(name.into())
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					        ret
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Another grouping.
 | 
				
			||||||
 | 
					    let add_by_kind = |ret, name: &str, kind| {
 | 
				
			||||||
 | 
					        let ret = match kind {
 | 
				
			||||||
 | 
					            &ArrangementKind::Base => ret,
 | 
				
			||||||
 | 
					            kind => add_by_name(
 | 
				
			||||||
 | 
					                ret,
 | 
				
			||||||
 | 
					                &name_with_arrangement(name.into(), kind),
 | 
				
			||||||
 | 
					                kind,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        add_by_name(ret, name, &ArrangementKind::Base)
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn name_with_arrangement(name: String, kind: &ArrangementKind) -> String {
 | 
				
			||||||
 | 
					        match kind {    
 | 
				
			||||||
 | 
					            ArrangementKind::Base => name,
 | 
				
			||||||
 | 
					            ArrangementKind::Wide => name + "_wide",
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let ret = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Name as given takes priority.
 | 
				
			||||||
 | 
					    let ret = add_by_kind(ret, name, &kind);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Then try non-alternative name if applicable (`us` for `us+colemak`).
 | 
				
			||||||
 | 
					    let ret = {
 | 
				
			||||||
 | 
					        let mut parts = name.splitn(2, '+');
 | 
				
			||||||
 | 
					        match parts.next() {
 | 
				
			||||||
 | 
					            Some(base) => {
 | 
				
			||||||
 | 
					                // The name is already equal to base, so it was already added.
 | 
				
			||||||
 | 
					                if base == name { ret }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    add_by_kind(ret, base, &kind)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            // The layout's base name starts with a "+". Weird but OK.
 | 
				
			||||||
 | 
					            None => {
 | 
				
			||||||
 | 
					                log_print!(logging::Level::Surprise, "Base layout name is empty: {}", name);
 | 
				
			||||||
 | 
					                ret
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // No other choices left, so give anything.
 | 
				
			||||||
 | 
					    add_by_kind(ret, FALLBACK_LAYOUT_NAME.into(), &kind)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn load_layout_data(source: DataSource)
 | 
				
			||||||
 | 
					    -> Result<::layout::LayoutData, LoadError>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let handler = logging::Print {};
 | 
				
			||||||
 | 
					    match source {
 | 
				
			||||||
 | 
					        DataSource::File(path) => {
 | 
				
			||||||
 | 
					            Layout::from_file(path.clone())
 | 
				
			||||||
 | 
					                .map_err(LoadError::BadData)
 | 
				
			||||||
 | 
					                .and_then(|layout|
 | 
				
			||||||
 | 
					                    layout.build(handler).0.map_err(LoadError::BadKeyMap)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        DataSource::Resource(name) => {
 | 
				
			||||||
 | 
					            Layout::from_resource(&name)
 | 
				
			||||||
 | 
					                .and_then(|layout|
 | 
				
			||||||
 | 
					                    layout.build(handler).0.map_err(LoadError::BadKeyMap)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn load_layout_data_with_fallback(
 | 
				
			||||||
 | 
					    name: &str,
 | 
				
			||||||
 | 
					    kind: ArrangementKind,
 | 
				
			||||||
 | 
					) -> (ArrangementKind, ::layout::LayoutData) {
 | 
				
			||||||
 | 
					    let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
 | 
				
			||||||
 | 
					        .map(PathBuf::from)
 | 
				
			||||||
 | 
					        .or_else(|| xdg::data_path("squeekboard/keyboards"));
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for (kind, source) in list_layout_sources(name, kind, path) {
 | 
				
			||||||
 | 
					        let layout = load_layout_data(source.clone());
 | 
				
			||||||
 | 
					        match layout {
 | 
				
			||||||
 | 
					            Err(e) => match (e, source) {
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    LoadError::BadData(Error::Missing(e)),
 | 
				
			||||||
 | 
					                    DataSource::File(file)
 | 
				
			||||||
 | 
					                ) => log_print!(
 | 
				
			||||||
 | 
					                    logging::Level::Debug,
 | 
				
			||||||
 | 
					                    "Tried file {:?}, but it's missing: {}",
 | 
				
			||||||
 | 
					                    file, e
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (e, source) => log_print!(
 | 
				
			||||||
 | 
					                    logging::Level::Warning,
 | 
				
			||||||
 | 
					                    "Failed to load layout from {}: {}, skipping",
 | 
				
			||||||
 | 
					                    source, e
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Ok(layout) => {
 | 
				
			||||||
 | 
					                log_print!(logging::Level::Info, "Loaded layout {}", source);
 | 
				
			||||||
 | 
					                return (kind, layout);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    panic!("No useful layout found!");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The root element describing an entire keyboard
 | 
					/// The root element describing an entire keyboard
 | 
				
			||||||
#[derive(Debug, Deserialize, PartialEq)]
 | 
					#[derive(Debug, Deserialize, PartialEq)]
 | 
				
			||||||
@ -88,13 +289,7 @@ struct ButtonMeta {
 | 
				
			|||||||
#[serde(deny_unknown_fields)]
 | 
					#[serde(deny_unknown_fields)]
 | 
				
			||||||
enum Action {
 | 
					enum Action {
 | 
				
			||||||
    #[serde(rename="locking")]
 | 
					    #[serde(rename="locking")]
 | 
				
			||||||
    Locking {
 | 
					    Locking { lock_view: String, unlock_view: String },
 | 
				
			||||||
        lock_view: String,
 | 
					 | 
				
			||||||
        unlock_view: String,
 | 
					 | 
				
			||||||
        pops: Option<bool>,
 | 
					 | 
				
			||||||
        #[serde(default)]
 | 
					 | 
				
			||||||
        looks_locked_from: Vec<String>,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    #[serde(rename="set_view")]
 | 
					    #[serde(rename="set_view")]
 | 
				
			||||||
    SetView(String),
 | 
					    SetView(String),
 | 
				
			||||||
    #[serde(rename="show_prefs")]
 | 
					    #[serde(rename="show_prefs")]
 | 
				
			||||||
@ -125,6 +320,37 @@ struct Outline {
 | 
				
			|||||||
    height: f64,
 | 
					    height: f64,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Errors encountered loading the layout into yaml
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub enum Error {
 | 
				
			||||||
 | 
					    Yaml(serde_yaml::Error),
 | 
				
			||||||
 | 
					    Io(io::Error),
 | 
				
			||||||
 | 
					    /// The file was missing.
 | 
				
			||||||
 | 
					    /// It's distinct from Io in order to make it matchable
 | 
				
			||||||
 | 
					    /// without calling io::Error::kind()
 | 
				
			||||||
 | 
					    Missing(io::Error),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Error {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Error::Yaml(e) => write!(f, "YAML: {}", e),
 | 
				
			||||||
 | 
					            Error::Io(e) => write!(f, "IO: {}", e),
 | 
				
			||||||
 | 
					            Error::Missing(e) => write!(f, "Missing: {}", e),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<io::Error> for Error {
 | 
				
			||||||
 | 
					    fn from(e: io::Error) -> Self {
 | 
				
			||||||
 | 
					        let kind = e.kind();
 | 
				
			||||||
 | 
					        match kind {
 | 
				
			||||||
 | 
					            io::ErrorKind::NotFound => Error::Missing(e),
 | 
				
			||||||
 | 
					            _ => Error::Io(e),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn add_offsets<'a, I: 'a, T, F: 'a>(iterator: I, get_size: F)
 | 
					pub fn add_offsets<'a, I: 'a, T, F: 'a>(iterator: I, get_size: F)
 | 
				
			||||||
    -> impl Iterator<Item=(f64, T)> + 'a
 | 
					    -> impl Iterator<Item=(f64, T)> + 'a
 | 
				
			||||||
    where I: Iterator<Item=T>,
 | 
					    where I: Iterator<Item=T>,
 | 
				
			||||||
@ -317,7 +543,7 @@ fn create_action<H: logging::Handler>(
 | 
				
			|||||||
        Text(String),
 | 
					        Text(String),
 | 
				
			||||||
        Keysym(String),
 | 
					        Keysym(String),
 | 
				
			||||||
        Modifier(Modifier),
 | 
					        Modifier(Modifier),
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    let submission = match (
 | 
					    let submission = match (
 | 
				
			||||||
        &symbol_meta.action,
 | 
					        &symbol_meta.action,
 | 
				
			||||||
@ -374,9 +600,7 @@ fn create_action<H: logging::Handler>(
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        SubmitData::Action(Action::Locking {
 | 
					        SubmitData::Action(Action::Locking {
 | 
				
			||||||
            lock_view, unlock_view,
 | 
					            lock_view, unlock_view
 | 
				
			||||||
            pops,
 | 
					 | 
				
			||||||
            looks_locked_from,
 | 
					 | 
				
			||||||
        }) => ::action::Action::LockView {
 | 
					        }) => ::action::Action::LockView {
 | 
				
			||||||
            lock: filter_view_name(
 | 
					            lock: filter_view_name(
 | 
				
			||||||
                name,
 | 
					                name,
 | 
				
			||||||
@ -390,8 +614,6 @@ fn create_action<H: logging::Handler>(
 | 
				
			|||||||
                &view_names,
 | 
					                &view_names,
 | 
				
			||||||
                warning_handler,
 | 
					                warning_handler,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            latches: pops.unwrap_or(true),
 | 
					 | 
				
			||||||
            looks_locked_from,
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        SubmitData::Action(
 | 
					        SubmitData::Action(
 | 
				
			||||||
            Action::ShowPrefs
 | 
					            Action::ShowPrefs
 | 
				
			||||||
@ -436,9 +658,6 @@ fn create_action<H: logging::Handler>(
 | 
				
			|||||||
            Modifier::Alt => action::Action::ApplyModifier(
 | 
					            Modifier::Alt => action::Action::ApplyModifier(
 | 
				
			||||||
                action::Modifier::Alt,
 | 
					                action::Modifier::Alt,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            Modifier::Mod4 => action::Action::ApplyModifier(
 | 
					 | 
				
			||||||
                action::Modifier::Mod4,
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            unsupported_modifier => {
 | 
					            unsupported_modifier => {
 | 
				
			||||||
                warning_handler.handle(
 | 
					                warning_handler.handle(
 | 
				
			||||||
                    logging::Level::Bug,
 | 
					                    logging::Level::Bug,
 | 
				
			||||||
@ -544,13 +763,10 @@ fn extract_symbol_names<'a>(actions: &'a [(&str, action::Action)])
 | 
				
			|||||||
        .map(|named_keysym| named_keysym.0)
 | 
					        .map(|named_keysym| named_keysym.0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    use std::env;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    use ::logging::ProblemPanic;
 | 
					    use ::logging::ProblemPanic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn path_from_root(file: &'static str) -> PathBuf {
 | 
					    fn path_from_root(file: &'static str) -> PathBuf {
 | 
				
			||||||
@ -700,6 +916,50 @@ mod tests {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn parsing_fallback() {
 | 
				
			||||||
 | 
					        assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
 | 
				
			||||||
 | 
					            .map(|layout| layout.build(ProblemPanic).0.unwrap())
 | 
				
			||||||
 | 
					            .is_ok()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn fallbacks_order() {
 | 
				
			||||||
 | 
					        let sources = list_layout_sources("nb", ArrangementKind::Base, None);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            sources,
 | 
				
			||||||
 | 
					            vec!(
 | 
				
			||||||
 | 
					                (ArrangementKind::Base, DataSource::Resource("nb".into())),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    ArrangementKind::Base,
 | 
				
			||||||
 | 
					                    DataSource::Resource(FALLBACK_LAYOUT_NAME.into())
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// If layout contains a "+", it should reach for what's in front of it too.
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn fallbacks_order_base() {
 | 
				
			||||||
 | 
					        let sources = list_layout_sources("nb+aliens", ArrangementKind::Base, None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            sources,
 | 
				
			||||||
 | 
					            vec!(
 | 
				
			||||||
 | 
					                (ArrangementKind::Base, DataSource::Resource("nb+aliens".into())),
 | 
				
			||||||
 | 
					                (ArrangementKind::Base, DataSource::Resource("nb".into())),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    ArrangementKind::Base,
 | 
				
			||||||
 | 
					                    DataSource::Resource(FALLBACK_LAYOUT_NAME.into())
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn unicode_keysym() {
 | 
					    fn unicode_keysym() {
 | 
				
			||||||
        let keysym = xkb::keysym_from_name(
 | 
					        let keysym = xkb::keysym_from_name(
 | 
				
			||||||
@ -1,424 +0,0 @@
 | 
				
			|||||||
/* Copyright (C) 2020-2021 Purism SPC
 | 
					 | 
				
			||||||
 * SPDX-License-Identifier: GPL-3.0+
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Loading layout files */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use std::env;
 | 
					 | 
				
			||||||
use std::fmt;
 | 
					 | 
				
			||||||
use std::path::PathBuf;
 | 
					 | 
				
			||||||
use std::convert::TryFrom;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use super::{ Error, LoadError };
 | 
					 | 
				
			||||||
use super::parsing;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use ::layout::ArrangementKind;
 | 
					 | 
				
			||||||
use ::logging;
 | 
					 | 
				
			||||||
use ::util::c::as_str;
 | 
					 | 
				
			||||||
use ::xdg;
 | 
					 | 
				
			||||||
use ::imservice::ContentPurpose;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// traits, derives
 | 
					 | 
				
			||||||
use ::logging::Warn;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Gathers stuff defined in C or called by C
 | 
					 | 
				
			||||||
pub mod c {
 | 
					 | 
				
			||||||
    use super::*;
 | 
					 | 
				
			||||||
    use std::os::raw::c_char;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    pub extern "C"
 | 
					 | 
				
			||||||
    fn squeek_load_layout(
 | 
					 | 
				
			||||||
        name: *const c_char,    // name of the keyboard
 | 
					 | 
				
			||||||
        type_: u32,             // type like Wide
 | 
					 | 
				
			||||||
        variant: u32,          // purpose variant like numeric, terminal...
 | 
					 | 
				
			||||||
        overlay: *const c_char, // the overlay (looking for "terminal")
 | 
					 | 
				
			||||||
    ) -> *mut ::layout::Layout {
 | 
					 | 
				
			||||||
        let type_ = match type_ {
 | 
					 | 
				
			||||||
            0 => ArrangementKind::Base,
 | 
					 | 
				
			||||||
            1 => ArrangementKind::Wide,
 | 
					 | 
				
			||||||
            _ => panic!("Bad enum value"),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let name = as_str(&name)
 | 
					 | 
				
			||||||
            .expect("Bad layout name")
 | 
					 | 
				
			||||||
            .expect("Empty layout name");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let variant = ContentPurpose::try_from(variant)
 | 
					 | 
				
			||||||
                    .or_print(
 | 
					 | 
				
			||||||
                        logging::Problem::Warning,
 | 
					 | 
				
			||||||
                        "Received invalid purpose value",
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                    .unwrap_or(ContentPurpose::Normal);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let overlay_str = as_str(&overlay)
 | 
					 | 
				
			||||||
                .expect("Bad overlay name")
 | 
					 | 
				
			||||||
                .expect("Empty overlay name");
 | 
					 | 
				
			||||||
        let overlay_str = match overlay_str {
 | 
					 | 
				
			||||||
            "" => None,
 | 
					 | 
				
			||||||
            other => Some(other),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str);
 | 
					 | 
				
			||||||
        let layout = ::layout::Layout::new(layout, kind);
 | 
					 | 
				
			||||||
        Box::into_raw(Box::new(layout))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const FALLBACK_LAYOUT_NAME: &str = "us";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug, Clone, PartialEq)]
 | 
					 | 
				
			||||||
enum DataSource {
 | 
					 | 
				
			||||||
    File(PathBuf),
 | 
					 | 
				
			||||||
    Resource(String),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for DataSource {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            DataSource::File(path) => write!(f, "Path: {:?}", path.display()),
 | 
					 | 
				
			||||||
            DataSource::Resource(name) => write!(f, "Resource: {}", name),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* All functions in this family carry around ArrangementKind,
 | 
					 | 
				
			||||||
 * because it's not guaranteed to be preserved,
 | 
					 | 
				
			||||||
 * and the resulting layout needs to know which version was loaded.
 | 
					 | 
				
			||||||
 * See `squeek_layout_get_kind`.
 | 
					 | 
				
			||||||
 * Possible TODO: since this is used only in styling,
 | 
					 | 
				
			||||||
 * and makes the below code nastier than needed, maybe it should go.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns ordered names treating `name` as the base name,
 | 
					 | 
				
			||||||
/// ignoring any `+` inside.
 | 
					 | 
				
			||||||
fn _get_arrangement_names(name: &str, arrangement: ArrangementKind)
 | 
					 | 
				
			||||||
    -> Vec<(ArrangementKind, String)>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    let name_with_arrangement = match arrangement {    
 | 
					 | 
				
			||||||
        ArrangementKind::Base => name.into(),
 | 
					 | 
				
			||||||
        ArrangementKind::Wide => format!("{}_wide", name),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    let mut ret = Vec::new();
 | 
					 | 
				
			||||||
    if name_with_arrangement != name {
 | 
					 | 
				
			||||||
        ret.push((arrangement, name_with_arrangement));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    ret.push((ArrangementKind::Base, name.into()));
 | 
					 | 
				
			||||||
    ret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns names accounting for any `+` in the `name`,
 | 
					 | 
				
			||||||
/// including the fallback to the default layout.
 | 
					 | 
				
			||||||
fn get_preferred_names(name: &str, kind: ArrangementKind)
 | 
					 | 
				
			||||||
    -> Vec<(ArrangementKind, String)>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    let mut ret = _get_arrangement_names(name, kind);
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    let base_name_preferences = {
 | 
					 | 
				
			||||||
        let mut parts = name.splitn(2, '+');
 | 
					 | 
				
			||||||
        match parts.next() {
 | 
					 | 
				
			||||||
            Some(base) => {
 | 
					 | 
				
			||||||
                // The name is already equal to base, so nothing to add
 | 
					 | 
				
			||||||
                if base == name {
 | 
					 | 
				
			||||||
                    vec![]
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    _get_arrangement_names(base, kind)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            // The layout's base name starts with a "+". Weird but OK.
 | 
					 | 
				
			||||||
            None => {
 | 
					 | 
				
			||||||
                log_print!(logging::Level::Surprise, "Base layout name is empty: {}", name);
 | 
					 | 
				
			||||||
                vec![]
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    ret.extend(base_name_preferences.into_iter());
 | 
					 | 
				
			||||||
    let fallback_names = _get_arrangement_names(FALLBACK_LAYOUT_NAME, kind);
 | 
					 | 
				
			||||||
    ret.extend(fallback_names.into_iter());
 | 
					 | 
				
			||||||
    ret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Includes the subdirectory before the forward slash.
 | 
					 | 
				
			||||||
type LayoutPath = String;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// This is only used inside iter_fallbacks_with_meta.
 | 
					 | 
				
			||||||
// Placed at the top scope
 | 
					 | 
				
			||||||
// because `use LayoutPurpose::*;`
 | 
					 | 
				
			||||||
// complains about "not in scope" otherwise.
 | 
					 | 
				
			||||||
// This seems to be a Rust 2015 edition problem.
 | 
					 | 
				
			||||||
/// Helper for determining where to look up the layout.
 | 
					 | 
				
			||||||
enum LayoutPurpose<'a> {
 | 
					 | 
				
			||||||
    Default,
 | 
					 | 
				
			||||||
    Special(&'a str),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns the directory string
 | 
					 | 
				
			||||||
/// where the layout should be looked up, including the slash.
 | 
					 | 
				
			||||||
fn get_directory_string(
 | 
					 | 
				
			||||||
    content_purpose: ContentPurpose,
 | 
					 | 
				
			||||||
    overlay: Option<&str>) -> String
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    use self::LayoutPurpose::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let layout_purpose = match overlay {
 | 
					 | 
				
			||||||
        None => match content_purpose {
 | 
					 | 
				
			||||||
            ContentPurpose::Number => Special("number"),
 | 
					 | 
				
			||||||
            ContentPurpose::Digits => Special("number"),
 | 
					 | 
				
			||||||
            ContentPurpose::Phone => Special("number"),
 | 
					 | 
				
			||||||
            ContentPurpose::Terminal => Special("terminal"),
 | 
					 | 
				
			||||||
            _ => Default,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        Some(overlay) => Special(overlay),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // For intuitiveness,
 | 
					 | 
				
			||||||
    // default purpose layouts are stored in the root directory,
 | 
					 | 
				
			||||||
    // as they correspond to typical text
 | 
					 | 
				
			||||||
    // and are seen the most often.
 | 
					 | 
				
			||||||
    match layout_purpose {
 | 
					 | 
				
			||||||
        Default => "".into(),
 | 
					 | 
				
			||||||
        Special(purpose) => format!("{}/", purpose),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns an iterator over all fallback paths.
 | 
					 | 
				
			||||||
fn to_layout_paths(
 | 
					 | 
				
			||||||
    name_fallbacks: Vec<(ArrangementKind, String)>,
 | 
					 | 
				
			||||||
    content_purpose: ContentPurpose,
 | 
					 | 
				
			||||||
    overlay: Option<&str>,
 | 
					 | 
				
			||||||
) -> impl Iterator<Item=(ArrangementKind, LayoutPath)> {
 | 
					 | 
				
			||||||
    let prepend_directory = get_directory_string(content_purpose, overlay);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    name_fallbacks.into_iter()
 | 
					 | 
				
			||||||
        .map(move |(arrangement, name)|
 | 
					 | 
				
			||||||
            (arrangement, format!("{}{}", prepend_directory, name))
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type LayoutSource = (ArrangementKind, DataSource);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn to_layout_sources(
 | 
					 | 
				
			||||||
    layout_paths: impl Iterator<Item=(ArrangementKind, LayoutPath)>,
 | 
					 | 
				
			||||||
    filesystem_path: Option<PathBuf>,
 | 
					 | 
				
			||||||
) -> impl Iterator<Item=LayoutSource> {
 | 
					 | 
				
			||||||
    layout_paths.flat_map(move |(arrangement, layout_path)| {
 | 
					 | 
				
			||||||
        let mut sources = Vec::new();
 | 
					 | 
				
			||||||
        if let Some(path) = &filesystem_path {
 | 
					 | 
				
			||||||
            sources.push((
 | 
					 | 
				
			||||||
                arrangement,
 | 
					 | 
				
			||||||
                DataSource::File(
 | 
					 | 
				
			||||||
                    path.join(&layout_path)
 | 
					 | 
				
			||||||
                        .with_extension("yaml")
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            ));
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        sources.push((arrangement, DataSource::Resource(layout_path.clone())));
 | 
					 | 
				
			||||||
        sources.into_iter()
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Returns possible sources, with first as the most preferred one.
 | 
					 | 
				
			||||||
/// Trying order: native lang of the right kind, native base,
 | 
					 | 
				
			||||||
/// fallback lang of the right kind, fallback base
 | 
					 | 
				
			||||||
fn iter_layout_sources(
 | 
					 | 
				
			||||||
    name: &str,
 | 
					 | 
				
			||||||
    arrangement: ArrangementKind,
 | 
					 | 
				
			||||||
    purpose: ContentPurpose,
 | 
					 | 
				
			||||||
    ui_overlay: Option<&str>,
 | 
					 | 
				
			||||||
    layout_storage: Option<PathBuf>,
 | 
					 | 
				
			||||||
) -> impl Iterator<Item=LayoutSource> {
 | 
					 | 
				
			||||||
    let names = get_preferred_names(name, arrangement);
 | 
					 | 
				
			||||||
    let paths = to_layout_paths(names, purpose, ui_overlay);
 | 
					 | 
				
			||||||
    to_layout_sources(paths, layout_storage)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn load_layout_data(source: DataSource)
 | 
					 | 
				
			||||||
    -> Result<::layout::LayoutData, LoadError>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    let handler = logging::Print {};
 | 
					 | 
				
			||||||
    match source {
 | 
					 | 
				
			||||||
        DataSource::File(path) => {
 | 
					 | 
				
			||||||
            parsing::Layout::from_file(path.clone())
 | 
					 | 
				
			||||||
                .map_err(LoadError::BadData)
 | 
					 | 
				
			||||||
                .and_then(|layout|
 | 
					 | 
				
			||||||
                    layout.build(handler).0.map_err(LoadError::BadKeyMap)
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        DataSource::Resource(name) => {
 | 
					 | 
				
			||||||
            parsing::Layout::from_resource(&name)
 | 
					 | 
				
			||||||
                .and_then(|layout|
 | 
					 | 
				
			||||||
                    layout.build(handler).0.map_err(LoadError::BadKeyMap)
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn load_layout_data_with_fallback(
 | 
					 | 
				
			||||||
    name: &str,
 | 
					 | 
				
			||||||
    kind: ArrangementKind,
 | 
					 | 
				
			||||||
    purpose: ContentPurpose,
 | 
					 | 
				
			||||||
    overlay: Option<&str>,
 | 
					 | 
				
			||||||
) -> (ArrangementKind, ::layout::LayoutData) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Build the path to the right keyboard layout subdirectory
 | 
					 | 
				
			||||||
    let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
 | 
					 | 
				
			||||||
        .map(PathBuf::from)
 | 
					 | 
				
			||||||
        .or_else(|| xdg::data_path("squeekboard/keyboards"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (kind, source) in iter_layout_sources(&name, kind, purpose, overlay, path) {
 | 
					 | 
				
			||||||
        let layout = load_layout_data(source.clone());
 | 
					 | 
				
			||||||
        match layout {
 | 
					 | 
				
			||||||
            Err(e) => match (e, source) {
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    LoadError::BadData(Error::Missing(e)),
 | 
					 | 
				
			||||||
                    DataSource::File(file)
 | 
					 | 
				
			||||||
                ) => log_print!(
 | 
					 | 
				
			||||||
                    logging::Level::Debug,
 | 
					 | 
				
			||||||
                    "Tried file {:?}, but it's missing: {}",
 | 
					 | 
				
			||||||
                    file, e
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                (e, source) => log_print!(
 | 
					 | 
				
			||||||
                    logging::Level::Warning,
 | 
					 | 
				
			||||||
                    "Failed to load layout from {}: {}, skipping",
 | 
					 | 
				
			||||||
                    source, e
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            Ok(layout) => {
 | 
					 | 
				
			||||||
                log_print!(logging::Level::Info, "Loaded layout {}", source);
 | 
					 | 
				
			||||||
                return (kind, layout);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    panic!("No useful layout found!");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(test)]
 | 
					 | 
				
			||||||
mod tests {
 | 
					 | 
				
			||||||
    use super::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    use ::logging::ProblemPanic;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn parsing_fallback() {
 | 
					 | 
				
			||||||
        assert!(parsing::Layout::from_resource(FALLBACK_LAYOUT_NAME)
 | 
					 | 
				
			||||||
            .map(|layout| layout.build(ProblemPanic).0.unwrap())
 | 
					 | 
				
			||||||
            .is_ok()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_fallback_basic_builtin() {
 | 
					 | 
				
			||||||
        let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Normal, None, None);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            sources.collect::<Vec<_>>(),
 | 
					 | 
				
			||||||
            vec!(
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::Resource("nb".into())),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Base,
 | 
					 | 
				
			||||||
                    DataSource::Resource(FALLBACK_LAYOUT_NAME.into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// Prefer loading from file system before builtin.
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_preferences_order_path() {
 | 
					 | 
				
			||||||
        let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Normal, None, Some(".".into()));
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            sources.collect::<Vec<_>>(),
 | 
					 | 
				
			||||||
            vec!(
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::File("./nb.yaml".into())),
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::Resource("nb".into())),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Base,
 | 
					 | 
				
			||||||
                    DataSource::File("./us.yaml".into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Base,
 | 
					 | 
				
			||||||
                    DataSource::Resource("us".into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// If layout contains a "+", it should reach for what's in front of it too.
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_preferences_order_base() {
 | 
					 | 
				
			||||||
        let sources = iter_layout_sources("nb+aliens", ArrangementKind::Base, ContentPurpose::Normal, None, None);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            sources.collect::<Vec<_>>(),
 | 
					 | 
				
			||||||
            vec!(
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::Resource("nb+aliens".into())),
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::Resource("nb".into())),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Base,
 | 
					 | 
				
			||||||
                    DataSource::Resource(FALLBACK_LAYOUT_NAME.into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_preferences_order_arrangement() {
 | 
					 | 
				
			||||||
        let sources = iter_layout_sources("nb", ArrangementKind::Wide, ContentPurpose::Normal, None, None);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            sources.collect::<Vec<_>>(),
 | 
					 | 
				
			||||||
            vec!(
 | 
					 | 
				
			||||||
                (ArrangementKind::Wide, DataSource::Resource("nb_wide".into())),
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::Resource("nb".into())),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Wide,
 | 
					 | 
				
			||||||
                    DataSource::Resource("us_wide".into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Base,
 | 
					 | 
				
			||||||
                    DataSource::Resource("us".into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_preferences_order_overlay() {
 | 
					 | 
				
			||||||
        let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Normal, Some("terminal"), None);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            sources.collect::<Vec<_>>(),
 | 
					 | 
				
			||||||
            vec!(
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::Resource("terminal/nb".into())),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Base,
 | 
					 | 
				
			||||||
                    DataSource::Resource("terminal/us".into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_preferences_order_hint() {
 | 
					 | 
				
			||||||
        let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Terminal, None, None);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            sources.collect::<Vec<_>>(),
 | 
					 | 
				
			||||||
            vec!(
 | 
					 | 
				
			||||||
                (ArrangementKind::Base, DataSource::Resource("terminal/nb".into())),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    ArrangementKind::Base,
 | 
					 | 
				
			||||||
                    DataSource::Resource("terminal/us".into())
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,65 +0,0 @@
 | 
				
			|||||||
/* Copyright (C) 2020-2021 Purism SPC
 | 
					 | 
				
			||||||
 * SPDX-License-Identifier: GPL-3.0+
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! Combined module for dealing with layout files */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mod loading;
 | 
					 | 
				
			||||||
pub mod parsing;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use std::io;
 | 
					 | 
				
			||||||
use std::fmt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use ::keyboard::FormattingError;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Errors encountered loading the layout into yaml
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
pub enum Error {
 | 
					 | 
				
			||||||
    Yaml(serde_yaml::Error),
 | 
					 | 
				
			||||||
    Io(io::Error),
 | 
					 | 
				
			||||||
    /// The file was missing.
 | 
					 | 
				
			||||||
    /// It's distinct from Io in order to make it matchable
 | 
					 | 
				
			||||||
    /// without calling io::Error::kind()
 | 
					 | 
				
			||||||
    Missing(io::Error),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for Error {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Error::Yaml(e) => write!(f, "YAML: {}", e),
 | 
					 | 
				
			||||||
            Error::Io(e) => write!(f, "IO: {}", e),
 | 
					 | 
				
			||||||
            Error::Missing(e) => write!(f, "Missing: {}", e),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl From<io::Error> for Error {
 | 
					 | 
				
			||||||
    fn from(e: io::Error) -> Self {
 | 
					 | 
				
			||||||
        let kind = e.kind();
 | 
					 | 
				
			||||||
        match kind {
 | 
					 | 
				
			||||||
            io::ErrorKind::NotFound => Error::Missing(e),
 | 
					 | 
				
			||||||
            _ => Error::Io(e),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
pub enum LoadError {
 | 
					 | 
				
			||||||
    BadData(Error),
 | 
					 | 
				
			||||||
    MissingResource,
 | 
					 | 
				
			||||||
    BadResource(serde_yaml::Error),
 | 
					 | 
				
			||||||
    BadKeyMap(FormattingError),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for LoadError {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        use self::LoadError::*;
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            BadData(e) => write!(f, "Bad data: {}", e),
 | 
					 | 
				
			||||||
            MissingResource => write!(f, "Missing resource"),
 | 
					 | 
				
			||||||
            BadResource(e) => write!(f, "Bad resource: {}", e),
 | 
					 | 
				
			||||||
            BadKeyMap(e) => write!(f, "Bad key map: {}", e),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -59,7 +59,7 @@ handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (service->context) {
 | 
					    if (service->context) {
 | 
				
			||||||
        if (arg_visible) {
 | 
					        if (arg_visible) {
 | 
				
			||||||
            server_context_service_force_show_keyboard (service->context);
 | 
					            server_context_service_show_keyboard (service->context);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            server_context_service_hide_keyboard (service->context);
 | 
					            server_context_service_hide_keyboard (service->context);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										215
									
								
								src/drawing.rs
									
									
									
									
									
								
							
							
						
						
									
										215
									
								
								src/drawing.rs
									
									
									
									
									
								
							@ -3,24 +3,20 @@
 | 
				
			|||||||
use cairo;
 | 
					use cairo;
 | 
				
			||||||
use std::cell::RefCell;
 | 
					use std::cell::RefCell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::action::{ Action, Modifier };
 | 
					use ::action::Action;
 | 
				
			||||||
use ::keyboard;
 | 
					use ::keyboard;
 | 
				
			||||||
use ::layout::{ Button, Label, LatchedState, Layout };
 | 
					use ::layout::{ Button, Layout };
 | 
				
			||||||
use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
 | 
					use ::layout::c::{ EekGtkKeyboard, Point };
 | 
				
			||||||
use ::submission::Submission;
 | 
					use ::submission::Submission;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use glib::translate::FromGlibPtrNone;
 | 
					use glib::translate::FromGlibPtrNone;
 | 
				
			||||||
use gtk::WidgetExt;
 | 
					use gtk::WidgetExt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::collections::HashSet;
 | 
					 | 
				
			||||||
use std::ffi::CStr;
 | 
					 | 
				
			||||||
use std::ptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mod c {
 | 
					mod c {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use cairo_sys;
 | 
					    use cairo_sys;
 | 
				
			||||||
    use std::os::raw::{ c_char, c_void };
 | 
					    use std::os::raw::c_void;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // This is constructed only in C, no need for warnings
 | 
					    // This is constructed only in C, no need for warnings
 | 
				
			||||||
    #[allow(dead_code)]
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
@ -28,44 +24,18 @@ mod c {
 | 
				
			|||||||
    #[derive(Clone, Copy)]
 | 
					    #[derive(Clone, Copy)]
 | 
				
			||||||
    pub struct EekRenderer(*const c_void);
 | 
					    pub struct EekRenderer(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This is constructed only in C, no need for warnings
 | 
					    #[no_mangle]
 | 
				
			||||||
    /// Just don't clone this for no reason.
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    #[repr(transparent)]
 | 
					 | 
				
			||||||
    #[derive(Clone, Copy)]
 | 
					 | 
				
			||||||
    pub struct GtkStyleContext(*const c_void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
 | 
					        // Button and View inside CButtonPlace are safe to pass to C
 | 
				
			||||||
 | 
					        // as long as they don't outlive the call
 | 
				
			||||||
 | 
					        // and nothing dereferences them
 | 
				
			||||||
        #[allow(improper_ctypes)]
 | 
					        #[allow(improper_ctypes)]
 | 
				
			||||||
        pub fn eek_renderer_get_scale_factor(
 | 
					        pub fn eek_render_button(
 | 
				
			||||||
            renderer: EekRenderer,
 | 
					            renderer: EekRenderer,
 | 
				
			||||||
        ) -> u32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[allow(improper_ctypes)]
 | 
					 | 
				
			||||||
        pub fn eek_render_button_in_context(
 | 
					 | 
				
			||||||
            scale_factor: u32,
 | 
					 | 
				
			||||||
            cr: *mut cairo_sys::cairo_t,
 | 
					            cr: *mut cairo_sys::cairo_t,
 | 
				
			||||||
            ctx: GtkStyleContext,
 | 
					            button: *const Button,
 | 
				
			||||||
            bounds: Bounds,
 | 
					 | 
				
			||||||
            icon_name: *const c_char,
 | 
					 | 
				
			||||||
            label: *const c_char,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[allow(improper_ctypes)]
 | 
					 | 
				
			||||||
        pub fn eek_get_style_context_for_button(
 | 
					 | 
				
			||||||
            renderer: EekRenderer,
 | 
					 | 
				
			||||||
            name: *const c_char,
 | 
					 | 
				
			||||||
            outline_name: *const c_char,
 | 
					 | 
				
			||||||
            locked_class: *const c_char,
 | 
					 | 
				
			||||||
            pressed: u64,
 | 
					            pressed: u64,
 | 
				
			||||||
        ) -> GtkStyleContext;
 | 
					            locked: u64,
 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[allow(improper_ctypes)]
 | 
					 | 
				
			||||||
        pub fn eek_put_style_context_for_button(
 | 
					 | 
				
			||||||
            ctx: GtkStyleContext,
 | 
					 | 
				
			||||||
            outline_name: *const c_char,
 | 
					 | 
				
			||||||
            locked_class: *const c_char,
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -85,16 +55,13 @@ mod c {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        layout.foreach_visible_button(|offset, button| {
 | 
					        layout.foreach_visible_button(|offset, button| {
 | 
				
			||||||
            let state = RefCell::borrow(&button.state).clone();
 | 
					            let state = RefCell::borrow(&button.state).clone();
 | 
				
			||||||
 | 
					            let active_mod = match &state.action {
 | 
				
			||||||
            let locked = LockedStyle::from_action(
 | 
					                Action::ApplyModifier(m) => active_modifiers.contains(m),
 | 
				
			||||||
                &state.action,
 | 
					                _ => false,
 | 
				
			||||||
                &active_modifiers,
 | 
					            };
 | 
				
			||||||
                layout.get_view_latched(),
 | 
					            let locked = state.action.is_active(&layout.current_view)
 | 
				
			||||||
                &layout.current_view,
 | 
					                | active_mod;
 | 
				
			||||||
            );
 | 
					            if state.pressed == keyboard::PressType::Pressed || locked {
 | 
				
			||||||
            if state.pressed == keyboard::PressType::Pressed
 | 
					 | 
				
			||||||
                || locked != LockedStyle::Free
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                render_button_at_position(
 | 
					                render_button_at_position(
 | 
				
			||||||
                    renderer, &cr,
 | 
					                    renderer, &cr,
 | 
				
			||||||
                    offset,
 | 
					                    offset,
 | 
				
			||||||
@ -120,55 +87,20 @@ mod c {
 | 
				
			|||||||
                renderer, &cr,
 | 
					                renderer, &cr,
 | 
				
			||||||
                offset,
 | 
					                offset,
 | 
				
			||||||
                button.as_ref(),
 | 
					                button.as_ref(),
 | 
				
			||||||
                keyboard::PressType::Released,
 | 
					                keyboard::PressType::Released, false,
 | 
				
			||||||
                LockedStyle::Free,
 | 
					 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy, PartialEq, Debug)]
 | 
					 | 
				
			||||||
enum LockedStyle {
 | 
					 | 
				
			||||||
    Free,
 | 
					 | 
				
			||||||
    Latched,
 | 
					 | 
				
			||||||
    Locked,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl LockedStyle {
 | 
					 | 
				
			||||||
    fn from_action(
 | 
					 | 
				
			||||||
        action: &Action,
 | 
					 | 
				
			||||||
        mods: &HashSet<Modifier>,
 | 
					 | 
				
			||||||
        latched_view: &LatchedState,
 | 
					 | 
				
			||||||
        current_view: &str,
 | 
					 | 
				
			||||||
    ) -> LockedStyle {
 | 
					 | 
				
			||||||
        let active_mod = match action {
 | 
					 | 
				
			||||||
            Action::ApplyModifier(m) => mods.contains(m),
 | 
					 | 
				
			||||||
            _ => false,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let active_view = action.is_active(current_view);
 | 
					 | 
				
			||||||
        let latched_button = match latched_view {
 | 
					 | 
				
			||||||
            LatchedState::Not => false,
 | 
					 | 
				
			||||||
            LatchedState::FromView(view) => !action.has_locked_appearance_from(view),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        match (active_mod, active_view, latched_button) {
 | 
					 | 
				
			||||||
            // Modifiers don't latch.
 | 
					 | 
				
			||||||
            (true, _, _) => LockedStyle::Locked,
 | 
					 | 
				
			||||||
            (false, true, false) => LockedStyle::Locked,
 | 
					 | 
				
			||||||
            (false, true, true) => LockedStyle::Latched,
 | 
					 | 
				
			||||||
            _ => LockedStyle::Free,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Renders a button at a position (button's own bounds ignored)
 | 
					/// Renders a button at a position (button's own bounds ignored)
 | 
				
			||||||
fn render_button_at_position(
 | 
					pub fn render_button_at_position(
 | 
				
			||||||
    renderer: c::EekRenderer,
 | 
					    renderer: c::EekRenderer,
 | 
				
			||||||
    cr: &cairo::Context,
 | 
					    cr: &cairo::Context,
 | 
				
			||||||
    position: Point,
 | 
					    position: Point,
 | 
				
			||||||
    button: &Button,
 | 
					    button: &Button,
 | 
				
			||||||
    pressed: keyboard::PressType,
 | 
					    pressed: keyboard::PressType,
 | 
				
			||||||
    locked: LockedStyle,
 | 
					    locked: bool,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    cr.save();
 | 
					    cr.save();
 | 
				
			||||||
    cr.translate(position.x, position.y);
 | 
					    cr.translate(position.x, position.y);
 | 
				
			||||||
@ -177,110 +109,19 @@ fn render_button_at_position(
 | 
				
			|||||||
        button.size.width, button.size.height
 | 
					        button.size.width, button.size.height
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    cr.clip();
 | 
					    cr.clip();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    let scale_factor = unsafe {
 | 
					 | 
				
			||||||
        c::eek_renderer_get_scale_factor(renderer)
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    let bounds = button.get_bounds();
 | 
					 | 
				
			||||||
    let (label_c, icon_name_c) = match &button.label {
 | 
					 | 
				
			||||||
        Label::Text(text) => (text.as_ptr(), ptr::null()),
 | 
					 | 
				
			||||||
        Label::IconName(name) => {
 | 
					 | 
				
			||||||
            let l = unsafe {
 | 
					 | 
				
			||||||
                // CStr doesn't allocate anything, so it only points to
 | 
					 | 
				
			||||||
                // the 'static str, avoiding a memory leak
 | 
					 | 
				
			||||||
                CStr::from_bytes_with_nul_unchecked(b"icon\0")
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            (l.as_ptr(), name.as_ptr())
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    with_button_context(
 | 
					 | 
				
			||||||
        renderer,
 | 
					 | 
				
			||||||
        button,
 | 
					 | 
				
			||||||
        pressed,
 | 
					 | 
				
			||||||
        locked,
 | 
					 | 
				
			||||||
        |ctx| unsafe {
 | 
					 | 
				
			||||||
            // TODO: split into separate procedures:
 | 
					 | 
				
			||||||
            // draw outline, draw label, draw icon.
 | 
					 | 
				
			||||||
            c::eek_render_button_in_context(
 | 
					 | 
				
			||||||
                scale_factor,
 | 
					 | 
				
			||||||
                cairo::Context::to_raw_none(&cr),
 | 
					 | 
				
			||||||
                *ctx,
 | 
					 | 
				
			||||||
                bounds,
 | 
					 | 
				
			||||||
                icon_name_c,
 | 
					 | 
				
			||||||
                label_c,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cr.restore();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn with_button_context<R, F: FnOnce(&c::GtkStyleContext) -> R>(
 | 
					 | 
				
			||||||
    renderer: c::EekRenderer,
 | 
					 | 
				
			||||||
    button: &Button,
 | 
					 | 
				
			||||||
    pressed: keyboard::PressType,
 | 
					 | 
				
			||||||
    locked: LockedStyle,
 | 
					 | 
				
			||||||
    operation: F,
 | 
					 | 
				
			||||||
) -> R {
 | 
					 | 
				
			||||||
    let outline_name_c = button.outline_name.as_ptr();
 | 
					 | 
				
			||||||
    let locked_class_c = match locked {
 | 
					 | 
				
			||||||
        LockedStyle::Free => ptr::null(),
 | 
					 | 
				
			||||||
        LockedStyle::Locked => unsafe {
 | 
					 | 
				
			||||||
            CStr::from_bytes_with_nul_unchecked(b"locked\0").as_ptr()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        LockedStyle::Latched => unsafe {
 | 
					 | 
				
			||||||
            CStr::from_bytes_with_nul_unchecked(b"latched\0").as_ptr()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    let ctx = unsafe {
 | 
					 | 
				
			||||||
        c::eek_get_style_context_for_button(
 | 
					 | 
				
			||||||
            renderer,
 | 
					 | 
				
			||||||
            button.name.as_ptr(),
 | 
					 | 
				
			||||||
            outline_name_c,
 | 
					 | 
				
			||||||
            locked_class_c,
 | 
					 | 
				
			||||||
            pressed as u64,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    let r = operation(&ctx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsafe {
 | 
					    unsafe {
 | 
				
			||||||
        c::eek_put_style_context_for_button(
 | 
					        c::eek_render_button(
 | 
				
			||||||
            ctx,
 | 
					            renderer,
 | 
				
			||||||
            outline_name_c,
 | 
					            cairo::Context::to_raw_none(&cr),
 | 
				
			||||||
            locked_class_c,
 | 
					            button as *const Button,
 | 
				
			||||||
 | 
					            pressed as u64,
 | 
				
			||||||
 | 
					            locked as u64,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					    cr.restore();
 | 
				
			||||||
    r
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn queue_redraw(keyboard: EekGtkKeyboard) {
 | 
					pub fn queue_redraw(keyboard: EekGtkKeyboard) {
 | 
				
			||||||
    let widget = unsafe { gtk::Widget::from_glib_none(keyboard.0) };
 | 
					    let widget = unsafe { gtk::Widget::from_glib_none(keyboard.0) };
 | 
				
			||||||
    widget.queue_draw();
 | 
					    widget.queue_draw();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(test)]
 | 
					 | 
				
			||||||
mod test {
 | 
					 | 
				
			||||||
    use super::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_exit_only() {
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            LockedStyle::from_action(
 | 
					 | 
				
			||||||
                &Action::LockView {
 | 
					 | 
				
			||||||
                    lock: "ab".into(), 
 | 
					 | 
				
			||||||
                    unlock: "a".into(),
 | 
					 | 
				
			||||||
                    latches: true,
 | 
					 | 
				
			||||||
                    looks_locked_from: vec!["b".into()],
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                &HashSet::new(),
 | 
					 | 
				
			||||||
                &LatchedState::FromView("b".into()),
 | 
					 | 
				
			||||||
                "ab",
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            LockedStyle::Locked,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -32,9 +32,9 @@ pub mod c {
 | 
				
			|||||||
    #[repr(transparent)]
 | 
					    #[repr(transparent)]
 | 
				
			||||||
    pub struct InputMethod(*const c_void);
 | 
					    pub struct InputMethod(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
        fn imservice_destroy_im(im: *mut c::InputMethod);
 | 
					        fn imservice_destroy_im(im: *mut c::InputMethod);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[allow(improper_ctypes)] // IMService will never be dereferenced in C
 | 
					        #[allow(improper_ctypes)] // IMService will never be dereferenced in C
 | 
				
			||||||
        pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
 | 
					        pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
 | 
				
			||||||
        pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
 | 
					        pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
 | 
				
			||||||
@ -149,18 +149,21 @@ pub mod c {
 | 
				
			|||||||
            ..IMProtocolState::default()
 | 
					            ..IMProtocolState::default()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        imservice.serial += Wrapping(1u32);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if active_changed {
 | 
					        if active_changed {
 | 
				
			||||||
            (imservice.active_callback)(imservice.current.active);
 | 
					            (imservice.active_callback)(imservice.current.active);
 | 
				
			||||||
            if imservice.current.active {
 | 
					            let (hint, purpose) = if imservice.current.active {(
 | 
				
			||||||
                unsafe {
 | 
					                imservice.current.content_hint,
 | 
				
			||||||
                    eekboard_context_service_set_hint_purpose(
 | 
					                imservice.current.content_purpose.clone(),
 | 
				
			||||||
                        imservice.state_manager,
 | 
					            )} else {(
 | 
				
			||||||
                        imservice.current.content_hint.bits(),
 | 
					                ContentHint::NONE,
 | 
				
			||||||
                        imservice.current.content_purpose.clone() as u32,
 | 
					                ContentPurpose::Normal,
 | 
				
			||||||
                    );
 | 
					            )};
 | 
				
			||||||
                }
 | 
					            unsafe {
 | 
				
			||||||
 | 
					                eekboard_context_service_set_hint_purpose(
 | 
				
			||||||
 | 
					                    imservice.state_manager,
 | 
				
			||||||
 | 
					                    hint.bits(),
 | 
				
			||||||
 | 
					                    purpose as u32,
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -406,6 +409,7 @@ impl IMService {
 | 
				
			|||||||
                unsafe {
 | 
					                unsafe {
 | 
				
			||||||
                    c::eek_input_method_commit(self.im, self.serial.0)
 | 
					                    c::eek_input_method_commit(self.im, self.serial.0)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                self.serial += Wrapping(1u32);
 | 
				
			||||||
                Ok(())
 | 
					                Ok(())
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            false => Err(SubmitError::NotActive),
 | 
					            false => Err(SubmitError::NotActive),
 | 
				
			||||||
 | 
				
			|||||||
@ -26,12 +26,19 @@ struct squeek_layout_state {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct squeek_layout;
 | 
					struct squeek_layout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EekBounds squeek_button_get_bounds(const struct squeek_button*);
 | 
				
			||||||
 | 
					const char *squeek_button_get_label(const struct squeek_button*);
 | 
				
			||||||
 | 
					const char *squeek_button_get_icon_name(const struct squeek_button*);
 | 
				
			||||||
 | 
					const char *squeek_button_get_name(const struct squeek_button*);
 | 
				
			||||||
 | 
					const char *squeek_button_get_outline_name(const struct squeek_button*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void squeek_button_print(const struct squeek_button* button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct transformation squeek_layout_calculate_transformation(
 | 
					struct transformation squeek_layout_calculate_transformation(
 | 
				
			||||||
        const struct squeek_layout *layout,
 | 
					        const struct squeek_layout *layout,
 | 
				
			||||||
        double allocation_width, double allocation_size);
 | 
					        double allocation_width, double allocation_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type, uint32_t variant_type, const char *overlay_name);
 | 
					struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
 | 
				
			||||||
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
 | 
					enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
 | 
				
			||||||
void squeek_layout_free(struct squeek_layout*);
 | 
					void squeek_layout_free(struct squeek_layout*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										546
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										546
									
								
								src/layout.rs
									
									
									
									
									
								
							@ -41,7 +41,9 @@ pub mod c {
 | 
				
			|||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use gtk_sys;
 | 
					    use gtk_sys;
 | 
				
			||||||
    use std::os::raw::c_void;
 | 
					    use std::ffi::CStr;
 | 
				
			||||||
 | 
					    use std::os::raw::{ c_char, c_void };
 | 
				
			||||||
 | 
					    use std::ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use std::ops::{ Add, Sub };
 | 
					    use std::ops::{ Add, Sub };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,6 +52,7 @@ pub mod c {
 | 
				
			|||||||
    #[derive(Copy, Clone)]
 | 
					    #[derive(Copy, Clone)]
 | 
				
			||||||
    pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
 | 
					    pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
        #[allow(improper_ctypes)]
 | 
					        #[allow(improper_ctypes)]
 | 
				
			||||||
        pub fn eek_gtk_keyboard_emit_feedback(
 | 
					        pub fn eek_gtk_keyboard_emit_feedback(
 | 
				
			||||||
@ -159,6 +162,64 @@ pub mod c {
 | 
				
			|||||||
    pub struct LevelKeyboard(*const c_void);
 | 
					    pub struct LevelKeyboard(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
					    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    pub extern "C"
 | 
				
			||||||
 | 
					    fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
 | 
				
			||||||
 | 
					        let button = unsafe { &*button };
 | 
				
			||||||
 | 
					        Bounds {
 | 
				
			||||||
 | 
					            x: 0.0, y: 0.0,
 | 
				
			||||||
 | 
					            width: button.size.width, height: button.size.height
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    pub extern "C"
 | 
				
			||||||
 | 
					    fn squeek_button_get_label(
 | 
				
			||||||
 | 
					        button: *const ::layout::Button
 | 
				
			||||||
 | 
					    ) -> *const c_char {
 | 
				
			||||||
 | 
					        let button = unsafe { &*button };
 | 
				
			||||||
 | 
					        match &button.label {
 | 
				
			||||||
 | 
					            Label::Text(text) => text.as_ptr(),
 | 
				
			||||||
 | 
					            // returning static strings to C is a bit cumbersome
 | 
				
			||||||
 | 
					            Label::IconName(_) => unsafe {
 | 
				
			||||||
 | 
					                // CStr doesn't allocate anything, so it only points to
 | 
				
			||||||
 | 
					                // the 'static str, avoiding a memory leak
 | 
				
			||||||
 | 
					                CStr::from_bytes_with_nul_unchecked(b"icon\0")
 | 
				
			||||||
 | 
					            }.as_ptr(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    pub extern "C"
 | 
				
			||||||
 | 
					    fn squeek_button_get_icon_name(button: *const Button) -> *const c_char {
 | 
				
			||||||
 | 
					        let button = unsafe { &*button };
 | 
				
			||||||
 | 
					        match &button.label {
 | 
				
			||||||
 | 
					            Label::Text(_) => ptr::null(),
 | 
				
			||||||
 | 
					            Label::IconName(name) => name.as_ptr(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    pub extern "C"
 | 
				
			||||||
 | 
					    fn squeek_button_get_name(button: *const Button) -> *const c_char {
 | 
				
			||||||
 | 
					        let button = unsafe { &*button };
 | 
				
			||||||
 | 
					        button.name.as_ptr()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    pub extern "C"
 | 
				
			||||||
 | 
					    fn squeek_button_get_outline_name(button: *const Button) -> *const c_char {
 | 
				
			||||||
 | 
					        let button = unsafe { &*button };
 | 
				
			||||||
 | 
					        button.outline_name.as_ptr()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
 | 
					    pub extern "C"
 | 
				
			||||||
 | 
					    fn squeek_button_print(button: *const ::layout::Button) {
 | 
				
			||||||
 | 
					        let button = unsafe { &*button };
 | 
				
			||||||
 | 
					        println!("{:?}", button);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /// Positions the layout contents within the available space.
 | 
					    /// Positions the layout contents within the available space.
 | 
				
			||||||
    /// The origin of the transformation is the point inside the margins.
 | 
					    /// The origin of the transformation is the point inside the margins.
 | 
				
			||||||
@ -424,15 +485,6 @@ pub struct Button {
 | 
				
			|||||||
    pub state: Rc<RefCell<KeyState>>,
 | 
					    pub state: Rc<RefCell<KeyState>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Button {
 | 
					 | 
				
			||||||
    pub fn get_bounds(&self) -> c::Bounds {
 | 
					 | 
				
			||||||
        c::Bounds {
 | 
					 | 
				
			||||||
            x: 0.0, y: 0.0,
 | 
					 | 
				
			||||||
            width: self.size.width, height: self.size.height,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// The graphical representation of a row of buttons
 | 
					/// The graphical representation of a row of buttons
 | 
				
			||||||
#[derive(Clone, Debug)]
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
pub struct Row {
 | 
					pub struct Row {
 | 
				
			||||||
@ -500,7 +552,6 @@ pub struct Spacing {
 | 
				
			|||||||
    pub button: f64,
 | 
					    pub button: f64,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					 | 
				
			||||||
pub struct View {
 | 
					pub struct View {
 | 
				
			||||||
    /// Rows together with their offsets from the top left
 | 
					    /// Rows together with their offsets from the top left
 | 
				
			||||||
    rows: Vec<(c::Point, Row)>,
 | 
					    rows: Vec<(c::Point, Row)>,
 | 
				
			||||||
@ -600,7 +651,7 @@ impl View {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The physical characteristic of layout for the purpose of styling
 | 
					/// The physical characteristic of layout for the purpose of styling
 | 
				
			||||||
#[derive(Clone, Copy, PartialEq, Debug)]
 | 
					#[derive(Clone, PartialEq, Debug)]
 | 
				
			||||||
pub enum ArrangementKind {
 | 
					pub enum ArrangementKind {
 | 
				
			||||||
    Base = 0,
 | 
					    Base = 0,
 | 
				
			||||||
    Wide = 1,
 | 
					    Wide = 1,
 | 
				
			||||||
@ -614,13 +665,6 @@ pub struct Margins {
 | 
				
			|||||||
    pub right: f64,
 | 
					    pub right: f64,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Debug, PartialEq)]
 | 
					 | 
				
			||||||
pub enum LatchedState {
 | 
					 | 
				
			||||||
    /// Holds view to return to.
 | 
					 | 
				
			||||||
    FromView(String),
 | 
					 | 
				
			||||||
    Not,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO: split into sth like
 | 
					// TODO: split into sth like
 | 
				
			||||||
// Arrangement (views) + details (keymap) + State (keys)
 | 
					// Arrangement (views) + details (keymap) + State (keys)
 | 
				
			||||||
/// State of the UI, contains the backend as well
 | 
					/// State of the UI, contains the backend as well
 | 
				
			||||||
@ -628,12 +672,6 @@ pub struct Layout {
 | 
				
			|||||||
    pub margins: Margins,
 | 
					    pub margins: Margins,
 | 
				
			||||||
    pub kind: ArrangementKind,
 | 
					    pub kind: ArrangementKind,
 | 
				
			||||||
    pub current_view: String,
 | 
					    pub current_view: String,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // If current view is latched,
 | 
					 | 
				
			||||||
    // clicking any button that emits an action (erase, submit, set modifier)
 | 
					 | 
				
			||||||
    // will cause lock buttons to unlatch.
 | 
					 | 
				
			||||||
    view_latched: LatchedState,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Views own the actual buttons which have state
 | 
					    // Views own the actual buttons which have state
 | 
				
			||||||
    // Maybe they should own UI only,
 | 
					    // Maybe they should own UI only,
 | 
				
			||||||
    // and keys should be owned by a dedicated non-UI-State?
 | 
					    // and keys should be owned by a dedicated non-UI-State?
 | 
				
			||||||
@ -680,7 +718,6 @@ impl Layout {
 | 
				
			|||||||
        Layout {
 | 
					        Layout {
 | 
				
			||||||
            kind,
 | 
					            kind,
 | 
				
			||||||
            current_view: "base".to_owned(),
 | 
					            current_view: "base".to_owned(),
 | 
				
			||||||
            view_latched: LatchedState::Not,
 | 
					 | 
				
			||||||
            views: data.views,
 | 
					            views: data.views,
 | 
				
			||||||
            keymaps: data.keymaps,
 | 
					            keymaps: data.keymaps,
 | 
				
			||||||
            pressed_keys: HashSet::new(),
 | 
					            pressed_keys: HashSet::new(),
 | 
				
			||||||
@ -706,12 +743,6 @@ impl Layout {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Layout is passed around mutably,
 | 
					 | 
				
			||||||
    // so better keep the field away from direct access.
 | 
					 | 
				
			||||||
    pub fn get_view_latched(&self) -> &LatchedState {
 | 
					 | 
				
			||||||
        &self.view_latched
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Calculates size without margins
 | 
					    /// Calculates size without margins
 | 
				
			||||||
    fn calculate_inner_size(&self) -> Size {
 | 
					    fn calculate_inner_size(&self) -> Size {
 | 
				
			||||||
        View::calculate_super_size(
 | 
					        View::calculate_super_size(
 | 
				
			||||||
@ -770,117 +801,25 @@ impl Layout {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    fn apply_view_transition(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					 | 
				
			||||||
        action: &Action,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        let (transition, new_latched) = Layout::process_action_for_view(
 | 
					 | 
				
			||||||
            action,
 | 
					 | 
				
			||||||
            &self.current_view,
 | 
					 | 
				
			||||||
            &self.view_latched,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match transition {
 | 
					    pub fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
 | 
				
			||||||
            ViewTransition::UnlatchAll => self.unstick_locks(),
 | 
					        let mut out = Vec::new();
 | 
				
			||||||
            ViewTransition::ChangeTo(view) => try_set_view(self, view.into()),
 | 
					        let view = self.get_current_view();
 | 
				
			||||||
            ViewTransition::NoChange => {},
 | 
					        for (_, row) in view.get_rows() {
 | 
				
			||||||
        };
 | 
					            for (_, button) in &row.buttons {
 | 
				
			||||||
 | 
					                let locked = {
 | 
				
			||||||
        self.view_latched = new_latched;
 | 
					                    let state = RefCell::borrow(&button.state).clone();
 | 
				
			||||||
    }
 | 
					                    state.action.is_locked(&self.current_view)
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
    /// Unlatch all latched keys,
 | 
					                if locked {
 | 
				
			||||||
    /// so that the new view is the one before first press.
 | 
					                    out.push(button.state.clone());
 | 
				
			||||||
    fn unstick_locks(&mut self) {
 | 
					                }
 | 
				
			||||||
        if let LatchedState::FromView(name) = self.view_latched.clone() {
 | 
					 | 
				
			||||||
            match self.set_view(name.clone()) {
 | 
					 | 
				
			||||||
                Ok(_) => { self.view_latched = LatchedState::Not; }
 | 
					 | 
				
			||||||
                Err(e) => log_print!(
 | 
					 | 
				
			||||||
                    logging::Level::Bug,
 | 
					 | 
				
			||||||
                    "Bad view {}, can't unlatch ({:?})",
 | 
					 | 
				
			||||||
                    name,
 | 
					 | 
				
			||||||
                    e,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					        out
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Last bool is new latch state.
 | 
					 | 
				
			||||||
    /// It doesn't make sense when the result carries UnlatchAll,
 | 
					 | 
				
			||||||
    /// but let's not be picky.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// Although the state is not defined at the keys
 | 
					 | 
				
			||||||
    /// (it's in the relationship between view and action),
 | 
					 | 
				
			||||||
    /// keys go through the following stages when clicked repeatedly: 
 | 
					 | 
				
			||||||
    /// unlocked+unlatched -> locked+latched -> locked+unlatched
 | 
					 | 
				
			||||||
    /// -> unlocked+unlatched
 | 
					 | 
				
			||||||
    fn process_action_for_view<'a>(
 | 
					 | 
				
			||||||
        action: &'a Action,
 | 
					 | 
				
			||||||
        current_view: &str,
 | 
					 | 
				
			||||||
        latched: &LatchedState,
 | 
					 | 
				
			||||||
    ) -> (ViewTransition<'a>, LatchedState) {
 | 
					 | 
				
			||||||
        match action {
 | 
					 | 
				
			||||||
            Action::Submit { text: _, keys: _ }
 | 
					 | 
				
			||||||
                | Action::Erase
 | 
					 | 
				
			||||||
                | Action::ApplyModifier(_)
 | 
					 | 
				
			||||||
            => {
 | 
					 | 
				
			||||||
                let t = match latched {
 | 
					 | 
				
			||||||
                    LatchedState::FromView(_) => ViewTransition::UnlatchAll,
 | 
					 | 
				
			||||||
                    LatchedState::Not => ViewTransition::NoChange,
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                (t, LatchedState::Not)
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            Action::SetView(view) => (
 | 
					 | 
				
			||||||
                ViewTransition::ChangeTo(view),
 | 
					 | 
				
			||||||
                LatchedState::Not,
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            Action::LockView { lock, unlock, latches, looks_locked_from: _ } => {
 | 
					 | 
				
			||||||
                use self::ViewTransition as VT;
 | 
					 | 
				
			||||||
                let locked = action.is_locked(current_view);
 | 
					 | 
				
			||||||
                match (locked, latched, latches) {
 | 
					 | 
				
			||||||
                    // Was unlocked, now make locked but latched.
 | 
					 | 
				
			||||||
                    (false, LatchedState::Not, true) => (
 | 
					 | 
				
			||||||
                        VT::ChangeTo(lock),
 | 
					 | 
				
			||||||
                        LatchedState::FromView(current_view.into()),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    // Layout is latched for reason other than this button.
 | 
					 | 
				
			||||||
                    (false, LatchedState::FromView(view), true) => (
 | 
					 | 
				
			||||||
                        VT::ChangeTo(lock),
 | 
					 | 
				
			||||||
                        LatchedState::FromView(view.clone()),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    // Was latched, now only locked.
 | 
					 | 
				
			||||||
                    (true, LatchedState::FromView(_), true)
 | 
					 | 
				
			||||||
                        => (VT::NoChange, LatchedState::Not),
 | 
					 | 
				
			||||||
                    // Was unlocked, can't latch so now make fully locked.
 | 
					 | 
				
			||||||
                    (false, _, false)
 | 
					 | 
				
			||||||
                        => (VT::ChangeTo(lock), LatchedState::Not),
 | 
					 | 
				
			||||||
                    // Was locked, now make unlocked.
 | 
					 | 
				
			||||||
                    (true, _, _)
 | 
					 | 
				
			||||||
                        => (VT::ChangeTo(unlock), LatchedState::Not),
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            _ => (ViewTransition::NoChange, latched.clone()),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, PartialEq)]
 | 
					 | 
				
			||||||
enum ViewTransition<'a> {
 | 
					 | 
				
			||||||
    ChangeTo(&'a str),
 | 
					 | 
				
			||||||
    UnlatchAll,
 | 
					 | 
				
			||||||
    NoChange,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn try_set_view(layout: &mut Layout, view_name: &str) {
 | 
					 | 
				
			||||||
    layout.set_view(view_name.into())
 | 
					 | 
				
			||||||
        .or_print(
 | 
					 | 
				
			||||||
            logging::Problem::Bug,
 | 
					 | 
				
			||||||
            &format!("Bad view {}, ignoring", view_name),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mod procedures {
 | 
					mod procedures {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -959,6 +898,67 @@ pub struct UIBackend {
 | 
				
			|||||||
mod seat {
 | 
					mod seat {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn try_set_view(layout: &mut Layout, view_name: String) {
 | 
				
			||||||
 | 
					        layout.set_view(view_name.clone())
 | 
				
			||||||
 | 
					            .or_print(
 | 
				
			||||||
 | 
					                logging::Problem::Bug,
 | 
				
			||||||
 | 
					                &format!("Bad view {}, ignoring", view_name),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// A vessel holding an obligation to switch view.
 | 
				
			||||||
 | 
					    /// Use with #[must_use]
 | 
				
			||||||
 | 
					    struct ViewChange<'a> {
 | 
				
			||||||
 | 
					        layout: &'a mut Layout,
 | 
				
			||||||
 | 
					        view_name: Option<String>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    impl<'a> ViewChange<'a> {
 | 
				
			||||||
 | 
					        fn choose_view(self, view_name: String) -> ViewChange<'a> {
 | 
				
			||||||
 | 
					            ViewChange {
 | 
				
			||||||
 | 
					                view_name: Some(view_name),
 | 
				
			||||||
 | 
					                ..self
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        fn apply(self) {
 | 
				
			||||||
 | 
					            if let Some(name) = self.view_name {
 | 
				
			||||||
 | 
					                try_set_view(self.layout, name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Find all impermanent view changes and undo them in an arbitrary order.
 | 
				
			||||||
 | 
					    /// Return an obligation to actually switch the view.
 | 
				
			||||||
 | 
					    /// The final view is the "unlock" view
 | 
				
			||||||
 | 
					    /// from one of the currently stuck keys.
 | 
				
			||||||
 | 
					    // As long as only one stuck button is used, this should be fine.
 | 
				
			||||||
 | 
					    // This is guaranteed because pressing a lock button unlocks all others.
 | 
				
			||||||
 | 
					    // TODO: Make some broader guarantee about the resulting view,
 | 
				
			||||||
 | 
					    // e.g. by maintaining a stack of stuck keys.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    fn unstick_locks(layout: &mut Layout) -> ViewChange {
 | 
				
			||||||
 | 
					        let mut new_view = None;
 | 
				
			||||||
 | 
					        for key in layout.get_locked_keys().clone() {
 | 
				
			||||||
 | 
					            let key: &Rc<RefCell<KeyState>> = key.borrow();
 | 
				
			||||||
 | 
					            let key = RefCell::borrow(key);
 | 
				
			||||||
 | 
					            match &key.action {
 | 
				
			||||||
 | 
					                Action::LockView { lock: _, unlock: view } => {
 | 
				
			||||||
 | 
					                    new_view = Some(view.clone());
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                a => log_print!(
 | 
				
			||||||
 | 
					                    logging::Level::Bug,
 | 
				
			||||||
 | 
					                    "Non-locking action {:?} was found inside locked keys",
 | 
				
			||||||
 | 
					                    a,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        ViewChange {
 | 
				
			||||||
 | 
					            layout,
 | 
				
			||||||
 | 
					            view_name: new_view,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn handle_press_key(
 | 
					    pub fn handle_press_key(
 | 
				
			||||||
        layout: &mut Layout,
 | 
					        layout: &mut Layout,
 | 
				
			||||||
        submission: &mut Submission,
 | 
					        submission: &mut Submission,
 | 
				
			||||||
@ -1018,22 +1018,37 @@ mod seat {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        let action = key.action.clone();
 | 
					        let action = key.action.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        layout.apply_view_transition(&action);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // update
 | 
					        // update
 | 
				
			||||||
        let key = key.into_released();
 | 
					        let key = key.into_released();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // process non-view switching
 | 
					        // process changes
 | 
				
			||||||
        match action {
 | 
					        match action {
 | 
				
			||||||
            Action::Submit { text: _, keys: _ }
 | 
					            Action::Submit { text: _, keys: _ }
 | 
				
			||||||
                | Action::Erase
 | 
					                | Action::Erase
 | 
				
			||||||
            => {
 | 
					            => {
 | 
				
			||||||
 | 
					                unstick_locks(layout).apply();
 | 
				
			||||||
                submission.handle_release(KeyState::get_id(rckey), time);
 | 
					                submission.handle_release(KeyState::get_id(rckey), time);
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            Action::SetView(view) => {
 | 
				
			||||||
 | 
					                try_set_view(layout, view)
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Action::LockView { lock, unlock } => {
 | 
				
			||||||
 | 
					                let gets_locked = !key.action.is_locked(&layout.current_view);
 | 
				
			||||||
 | 
					                unstick_locks(layout)
 | 
				
			||||||
 | 
					                    // It doesn't matter what the resulting view should be,
 | 
				
			||||||
 | 
					                    // it's getting changed anyway.
 | 
				
			||||||
 | 
					                    .choose_view(
 | 
				
			||||||
 | 
					                        match gets_locked {
 | 
				
			||||||
 | 
					                            true => lock.clone(),
 | 
				
			||||||
 | 
					                            false => unlock.clone(),
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    .apply()
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            Action::ApplyModifier(modifier) => {
 | 
					            Action::ApplyModifier(modifier) => {
 | 
				
			||||||
                // FIXME: key id is unneeded with stateless locks
 | 
					                // FIXME: key id is unneeded with stateless locks
 | 
				
			||||||
                let key_id = KeyState::get_id(rckey);
 | 
					                let key_id = KeyState::get_id(rckey);
 | 
				
			||||||
                let gets_locked = !submission.is_modifier_active(modifier);
 | 
					                let gets_locked = !submission.is_modifier_active(modifier.clone());
 | 
				
			||||||
                match gets_locked {
 | 
					                match gets_locked {
 | 
				
			||||||
                    true => submission.handle_add_modifier(
 | 
					                    true => submission.handle_add_modifier(
 | 
				
			||||||
                        key_id,
 | 
					                        key_id,
 | 
				
			||||||
@ -1068,8 +1083,6 @@ mod seat {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            // Other keys are handled in view switcher before.
 | 
					 | 
				
			||||||
            _ => {}
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let pointer = ::util::Pointer(rckey.clone());
 | 
					        let pointer = ::util::Pointer(rckey.clone());
 | 
				
			||||||
@ -1087,20 +1100,14 @@ mod test {
 | 
				
			|||||||
    use std::ffi::CString;
 | 
					    use std::ffi::CString;
 | 
				
			||||||
    use ::keyboard::PressType;
 | 
					    use ::keyboard::PressType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn make_state_with_action(action: Action)
 | 
					    pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
				
			||||||
        -> Rc<RefCell<::keyboard::KeyState>>
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Rc::new(RefCell::new(::keyboard::KeyState {
 | 
					        Rc::new(RefCell::new(::keyboard::KeyState {
 | 
				
			||||||
            pressed: PressType::Released,
 | 
					            pressed: PressType::Released,
 | 
				
			||||||
            keycodes: Vec::new(),
 | 
					            keycodes: Vec::new(),
 | 
				
			||||||
            action,
 | 
					            action: Action::SetView("default".into()),
 | 
				
			||||||
        }))
 | 
					        }))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
					 | 
				
			||||||
        make_state_with_action(Action::SetView("default".into()))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn make_button_with_state(
 | 
					    pub fn make_button_with_state(
 | 
				
			||||||
        name: String,
 | 
					        name: String,
 | 
				
			||||||
        state: Rc<RefCell<::keyboard::KeyState>>,
 | 
					        state: Rc<RefCell<::keyboard::KeyState>>,
 | 
				
			||||||
@ -1114,242 +1121,6 @@ mod test {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn latch_lock_unlock() {
 | 
					 | 
				
			||||||
        let action = Action::LockView {
 | 
					 | 
				
			||||||
            lock: "lock".into(),
 | 
					 | 
				
			||||||
            unlock: "unlock".into(),
 | 
					 | 
				
			||||||
            latches: true,
 | 
					 | 
				
			||||||
            looks_locked_from: vec![],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            Layout::process_action_for_view(&action, "unlock", &LatchedState::Not),
 | 
					 | 
				
			||||||
            (ViewTransition::ChangeTo("lock"), LatchedState::FromView("unlock".into())),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            Layout::process_action_for_view(&action, "lock", &LatchedState::FromView("unlock".into())),
 | 
					 | 
				
			||||||
            (ViewTransition::NoChange, LatchedState::Not),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            Layout::process_action_for_view(&action, "lock", &LatchedState::Not),
 | 
					 | 
				
			||||||
            (ViewTransition::ChangeTo("unlock"), LatchedState::Not),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            Layout::process_action_for_view(&Action::Erase, "lock", &LatchedState::FromView("base".into())),
 | 
					 | 
				
			||||||
            (ViewTransition::UnlatchAll, LatchedState::Not),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn latch_pop_layout() {
 | 
					 | 
				
			||||||
        let switch = Action::LockView {
 | 
					 | 
				
			||||||
            lock: "locked".into(),
 | 
					 | 
				
			||||||
            unlock: "base".into(),
 | 
					 | 
				
			||||||
            latches: true,
 | 
					 | 
				
			||||||
            looks_locked_from: vec![],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let submit = Action::Erase;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let view = View::new(vec![(
 | 
					 | 
				
			||||||
            0.0,
 | 
					 | 
				
			||||||
            Row::new(vec![
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    0.0,
 | 
					 | 
				
			||||||
                    make_button_with_state(
 | 
					 | 
				
			||||||
                        "switch".into(),
 | 
					 | 
				
			||||||
                        make_state_with_action(switch.clone())
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    1.0,
 | 
					 | 
				
			||||||
                    make_button_with_state(
 | 
					 | 
				
			||||||
                        "submit".into(),
 | 
					 | 
				
			||||||
                        make_state_with_action(submit.clone())
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            ]),
 | 
					 | 
				
			||||||
        )]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut layout = Layout {
 | 
					 | 
				
			||||||
            current_view: "base".into(),
 | 
					 | 
				
			||||||
            view_latched: LatchedState::Not,
 | 
					 | 
				
			||||||
            keymaps: Vec::new(),
 | 
					 | 
				
			||||||
            kind: ArrangementKind::Base,
 | 
					 | 
				
			||||||
            pressed_keys: HashSet::new(),
 | 
					 | 
				
			||||||
            margins: Margins {
 | 
					 | 
				
			||||||
                top: 0.0,
 | 
					 | 
				
			||||||
                left: 0.0,
 | 
					 | 
				
			||||||
                right: 0.0,
 | 
					 | 
				
			||||||
                bottom: 0.0,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            views: hashmap! {
 | 
					 | 
				
			||||||
                // Both can use the same structure.
 | 
					 | 
				
			||||||
                // Switching doesn't depend on the view shape
 | 
					 | 
				
			||||||
                // as long as the switching button is present.
 | 
					 | 
				
			||||||
                "base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
 | 
					 | 
				
			||||||
                "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        };        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Basic cycle
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&switch);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "locked");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&switch);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "locked");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&submit);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "locked");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&switch);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "base");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&switch);
 | 
					 | 
				
			||||||
        // Unlatch
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "locked");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&submit);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "base");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn reverse_unlatch_layout() {
 | 
					 | 
				
			||||||
        let switch = Action::LockView {
 | 
					 | 
				
			||||||
            lock: "locked".into(),
 | 
					 | 
				
			||||||
            unlock: "base".into(),
 | 
					 | 
				
			||||||
            latches: true,
 | 
					 | 
				
			||||||
            looks_locked_from: vec![],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let unswitch = Action::LockView {
 | 
					 | 
				
			||||||
            lock: "locked".into(),
 | 
					 | 
				
			||||||
            unlock: "unlocked".into(),
 | 
					 | 
				
			||||||
            latches: false,
 | 
					 | 
				
			||||||
            looks_locked_from: vec![],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let submit = Action::Erase;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let view = View::new(vec![(
 | 
					 | 
				
			||||||
            0.0,
 | 
					 | 
				
			||||||
            Row::new(vec![
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    0.0,
 | 
					 | 
				
			||||||
                    make_button_with_state(
 | 
					 | 
				
			||||||
                        "switch".into(),
 | 
					 | 
				
			||||||
                        make_state_with_action(switch.clone())
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    1.0,
 | 
					 | 
				
			||||||
                    make_button_with_state(
 | 
					 | 
				
			||||||
                        "submit".into(),
 | 
					 | 
				
			||||||
                        make_state_with_action(submit.clone())
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            ]),
 | 
					 | 
				
			||||||
        )]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut layout = Layout {
 | 
					 | 
				
			||||||
            current_view: "base".into(),
 | 
					 | 
				
			||||||
            view_latched: LatchedState::Not,
 | 
					 | 
				
			||||||
            keymaps: Vec::new(),
 | 
					 | 
				
			||||||
            kind: ArrangementKind::Base,
 | 
					 | 
				
			||||||
            pressed_keys: HashSet::new(),
 | 
					 | 
				
			||||||
            margins: Margins {
 | 
					 | 
				
			||||||
                top: 0.0,
 | 
					 | 
				
			||||||
                left: 0.0,
 | 
					 | 
				
			||||||
                right: 0.0,
 | 
					 | 
				
			||||||
                bottom: 0.0,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            views: hashmap! {
 | 
					 | 
				
			||||||
                // Both can use the same structure.
 | 
					 | 
				
			||||||
                // Switching doesn't depend on the view shape
 | 
					 | 
				
			||||||
                // as long as the switching button is present.
 | 
					 | 
				
			||||||
                "base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
 | 
					 | 
				
			||||||
                "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
 | 
					 | 
				
			||||||
                "unlocked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        };        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&switch);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "locked");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&unswitch);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "unlocked");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn latch_twopop_layout() {
 | 
					 | 
				
			||||||
        let switch = Action::LockView {
 | 
					 | 
				
			||||||
            lock: "locked".into(),
 | 
					 | 
				
			||||||
            unlock: "base".into(),
 | 
					 | 
				
			||||||
            latches: true,
 | 
					 | 
				
			||||||
            looks_locked_from: vec![],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let switch_again = Action::LockView {
 | 
					 | 
				
			||||||
            lock: "ĄĘ".into(),
 | 
					 | 
				
			||||||
            unlock: "locked".into(),
 | 
					 | 
				
			||||||
            latches: true,
 | 
					 | 
				
			||||||
            looks_locked_from: vec![],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let submit = Action::Erase;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let view = View::new(vec![(
 | 
					 | 
				
			||||||
            0.0,
 | 
					 | 
				
			||||||
            Row::new(vec![
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    0.0,
 | 
					 | 
				
			||||||
                    make_button_with_state(
 | 
					 | 
				
			||||||
                        "switch".into(),
 | 
					 | 
				
			||||||
                        make_state_with_action(switch.clone())
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    1.0,
 | 
					 | 
				
			||||||
                    make_button_with_state(
 | 
					 | 
				
			||||||
                        "submit".into(),
 | 
					 | 
				
			||||||
                        make_state_with_action(submit.clone())
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            ]),
 | 
					 | 
				
			||||||
        )]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut layout = Layout {
 | 
					 | 
				
			||||||
            current_view: "base".into(),
 | 
					 | 
				
			||||||
            view_latched: LatchedState::Not,
 | 
					 | 
				
			||||||
            keymaps: Vec::new(),
 | 
					 | 
				
			||||||
            kind: ArrangementKind::Base,
 | 
					 | 
				
			||||||
            pressed_keys: HashSet::new(),
 | 
					 | 
				
			||||||
            margins: Margins {
 | 
					 | 
				
			||||||
                top: 0.0,
 | 
					 | 
				
			||||||
                left: 0.0,
 | 
					 | 
				
			||||||
                right: 0.0,
 | 
					 | 
				
			||||||
                bottom: 0.0,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            views: hashmap! {
 | 
					 | 
				
			||||||
                // All can use the same structure.
 | 
					 | 
				
			||||||
                // Switching doesn't depend on the view shape
 | 
					 | 
				
			||||||
                // as long as the switching button is present.
 | 
					 | 
				
			||||||
                "base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
 | 
					 | 
				
			||||||
                "locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
 | 
					 | 
				
			||||||
                "ĄĘ".into() => (c::Point { x: 0.0, y: 0.0 }, view),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        };        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Latch twice, then Ąto-unlatch across 2 levels
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&switch);
 | 
					 | 
				
			||||||
        println!("{:?}", layout.view_latched);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "locked");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&switch_again);
 | 
					 | 
				
			||||||
        println!("{:?}", layout.view_latched);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "ĄĘ");
 | 
					 | 
				
			||||||
        layout.apply_view_transition(&submit);
 | 
					 | 
				
			||||||
        println!("{:?}", layout.view_latched);
 | 
					 | 
				
			||||||
        assert_eq!(&layout.current_view, "base");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn check_centering() {
 | 
					    fn check_centering() {
 | 
				
			||||||
        //    A B
 | 
					        //    A B
 | 
				
			||||||
@ -1422,7 +1193,6 @@ mod test {
 | 
				
			|||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        let layout = Layout {
 | 
					        let layout = Layout {
 | 
				
			||||||
            current_view: String::new(),
 | 
					            current_view: String::new(),
 | 
				
			||||||
            view_latched: LatchedState::Not,
 | 
					 | 
				
			||||||
            keymaps: Vec::new(),
 | 
					            keymaps: Vec::new(),
 | 
				
			||||||
            kind: ArrangementKind::Base,
 | 
					            kind: ArrangementKind::Base,
 | 
				
			||||||
            pressed_keys: HashSet::new(),
 | 
					            pressed_keys: HashSet::new(),
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ mod c {
 | 
				
			|||||||
    #[repr(C)]
 | 
					    #[repr(C)]
 | 
				
			||||||
    pub struct GnomeXkbInfo(*const c_void);
 | 
					    pub struct GnomeXkbInfo(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
        // from libc
 | 
					        // from libc
 | 
				
			||||||
        pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 | 
					        pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ pub mod c {
 | 
				
			|||||||
    #[derive(Clone, Copy)]
 | 
					    #[derive(Clone, Copy)]
 | 
				
			||||||
    pub struct Manager(*const c_void);
 | 
					    pub struct Manager(*const c_void);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
        pub fn eekboard_context_service_set_overlay(
 | 
					        pub fn eekboard_context_service_set_overlay(
 | 
				
			||||||
            manager: Manager,
 | 
					            manager: Manager,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								src/popover.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/popover.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					#ifndef POPOVER_H__
 | 
				
			||||||
 | 
					#define POPOVER_H__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gtk/gtk.h>
 | 
				
			||||||
 | 
					#include "eek/eek-keyboard.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void squeek_popover_show(GtkWidget*, struct button_place);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -29,6 +29,7 @@ use ::logging::Warn;
 | 
				
			|||||||
mod c {
 | 
					mod c {
 | 
				
			||||||
    use std::os::raw::c_char;
 | 
					    use std::os::raw::c_char;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
        pub fn popover_open_settings_panel(panel: *const c_char);
 | 
					        pub fn popover_open_settings_panel(panel: *const c_char);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -213,16 +214,7 @@ fn set_visible_layout(
 | 
				
			|||||||
    layout_id: LayoutId,
 | 
					    layout_id: LayoutId,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    match layout_id {
 | 
					    match layout_id {
 | 
				
			||||||
        LayoutId::System { kind, name } => {
 | 
					        LayoutId::System { kind, name } => set_layout(kind, name),
 | 
				
			||||||
            unsafe {
 | 
					 | 
				
			||||||
                use std::ptr;
 | 
					 | 
				
			||||||
                manager::c::eekboard_context_service_set_overlay(
 | 
					 | 
				
			||||||
                    manager,
 | 
					 | 
				
			||||||
                    ptr::null(),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            set_layout(kind, name);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        LayoutId::Local(name) => {
 | 
					        LayoutId::Local(name) => {
 | 
				
			||||||
            let name = CString::new(name.as_str()).unwrap();
 | 
					            let name = CString::new(name.as_str()).unwrap();
 | 
				
			||||||
            let name_ptr = name.as_ptr();
 | 
					            let name_ptr = name.as_ptr();
 | 
				
			||||||
@ -401,7 +393,6 @@ pub fn show(
 | 
				
			|||||||
        width: position.width.floor() as i32,
 | 
					        width: position.width.floor() as i32,
 | 
				
			||||||
        height: position.width.floor() as i32,
 | 
					        height: position.width.floor() as i32,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    menu.set_constrain_to(gtk::PopoverConstraint::None);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Some(current_layout) = get_current_layout(manager, &system_layouts) {
 | 
					    if let Some(current_layout) = get_current_layout(manager, &system_layouts) {
 | 
				
			||||||
        let current_name_variant = choices.iter()
 | 
					        let current_name_variant = choices.iter()
 | 
				
			||||||
@ -446,8 +437,7 @@ pub fn show(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        let settings_action = gio::SimpleAction::new("settings", None);
 | 
					        let settings_action = gio::SimpleAction::new("settings", None);
 | 
				
			||||||
        settings_action.connect_activate(move |_, _| {
 | 
					        settings_action.connect_activate(move |_, _| {
 | 
				
			||||||
            let s = CString::new("region").unwrap();
 | 
					            unsafe { c::popover_open_settings_panel(CString::new("region").unwrap().as_ptr()) };
 | 
				
			||||||
            unsafe { c::popover_open_settings_panel(s.as_ptr()) };
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let action_group = gio::SimpleActionGroup::new();
 | 
					        let action_group = gio::SimpleActionGroup::new();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										101
									
								
								src/resources.rs
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/resources.rs
									
									
									
									
									
								
							@ -10,108 +10,81 @@ use std::iter::FromIterator;
 | 
				
			|||||||
// TODO: keep a list of what is a language layout,
 | 
					// TODO: keep a list of what is a language layout,
 | 
				
			||||||
// and what a convenience layout. "_wide" is not a layout,
 | 
					// and what a convenience layout. "_wide" is not a layout,
 | 
				
			||||||
// neither is "number"
 | 
					// neither is "number"
 | 
				
			||||||
/// List of builtin layouts
 | 
					const KEYBOARDS: &[(*const str, *const str)] = &[
 | 
				
			||||||
static KEYBOARDS: &[(&'static str, &'static str)] = &[
 | 
					 | 
				
			||||||
    // layouts: us must be left as first, as it is the,
 | 
					    // layouts: us must be left as first, as it is the,
 | 
				
			||||||
    // fallback layout.
 | 
					    // fallback layout. The others should be alphabetical.
 | 
				
			||||||
    ("us", include_str!("../data/keyboards/us.yaml")),
 | 
					    ("us", include_str!("../data/keyboards/us.yaml")),
 | 
				
			||||||
 | 
					    ("us+colemak", include_str!("../data/keyboards/us+colemak.yaml")),
 | 
				
			||||||
    ("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
 | 
					    ("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
 | 
				
			||||||
 | 
					    ("br", include_str!("../data/keyboards/br.yaml")),
 | 
				
			||||||
    // Language layouts: keep alphabetical.
 | 
					    ("de", include_str!("../data/keyboards/de.yaml")),
 | 
				
			||||||
    ("be", include_str!("../data/keyboards/be.yaml")),
 | 
					    ("be", include_str!("../data/keyboards/be.yaml")),
 | 
				
			||||||
    ("be_wide", include_str!("../data/keyboards/be_wide.yaml")),
 | 
					    ("be_wide", include_str!("../data/keyboards/be_wide.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("bg", include_str!("../data/keyboards/bg.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("br", include_str!("../data/keyboards/br.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("de", include_str!("../data/keyboards/de.yaml")),
 | 
					 | 
				
			||||||
    ("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
 | 
					    ("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("cz", include_str!("../data/keyboards/cz.yaml")),
 | 
					    ("cz", include_str!("../data/keyboards/cz.yaml")),
 | 
				
			||||||
    ("cz_wide", include_str!("../data/keyboards/cz_wide.yaml")),
 | 
					    ("cz_wide", include_str!("../data/keyboards/cz_wide.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("cz+qwerty", include_str!("../data/keyboards/cz+qwerty.yaml")),
 | 
					    ("cz+qwerty", include_str!("../data/keyboards/cz+qwerty.yaml")),
 | 
				
			||||||
    ("cz+qwerty_wide", include_str!("../data/keyboards/cz+qwerty_wide.yaml")),
 | 
					    ("cz+qwerty_wide", include_str!("../data/keyboards/cz+qwerty_wide.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("dk", include_str!("../data/keyboards/dk.yaml")),
 | 
					    ("dk", include_str!("../data/keyboards/dk.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("epo", include_str!("../data/keyboards/epo.yaml")),
 | 
					    ("epo", include_str!("../data/keyboards/epo.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("es", include_str!("../data/keyboards/es.yaml")),
 | 
					    ("es", include_str!("../data/keyboards/es.yaml")),
 | 
				
			||||||
    ("es+cat", include_str!("../data/keyboards/es+cat.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("fi", include_str!("../data/keyboards/fi.yaml")),
 | 
					    ("fi", include_str!("../data/keyboards/fi.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("fr", include_str!("../data/keyboards/fr.yaml")),
 | 
					    ("fr", include_str!("../data/keyboards/fr.yaml")),
 | 
				
			||||||
    ("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
 | 
					    ("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("gr", include_str!("../data/keyboards/gr.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("il", include_str!("../data/keyboards/il.yaml")),
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    ("ir", include_str!("../data/keyboards/ir.yaml")),
 | 
					 | 
				
			||||||
    ("ir_wide", include_str!("../data/keyboards/ir_wide.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("it", include_str!("../data/keyboards/it.yaml")),
 | 
					 | 
				
			||||||
    ("it+fur", include_str!("../data/keyboards/it+fur.yaml")),
 | 
					    ("it+fur", include_str!("../data/keyboards/it+fur.yaml")),
 | 
				
			||||||
 | 
					    ("gr", include_str!("../data/keyboards/gr.yaml")),
 | 
				
			||||||
 | 
					    ("it", include_str!("../data/keyboards/it.yaml")),
 | 
				
			||||||
    ("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
 | 
					    ("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
 | 
				
			||||||
    ("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")),
 | 
					    ("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("no", include_str!("../data/keyboards/no.yaml")),
 | 
					    ("no", include_str!("../data/keyboards/no.yaml")),
 | 
				
			||||||
 | 
					    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
				
			||||||
    ("pl", include_str!("../data/keyboards/pl.yaml")),
 | 
					    ("pl", include_str!("../data/keyboards/pl.yaml")),
 | 
				
			||||||
    ("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
 | 
					    ("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("ru", include_str!("../data/keyboards/ru.yaml")),
 | 
					    ("ru", include_str!("../data/keyboards/ru.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("se", include_str!("../data/keyboards/se.yaml")),
 | 
					    ("se", include_str!("../data/keyboards/se.yaml")),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("th", include_str!("../data/keyboards/th.yaml")),
 | 
					    ("th", include_str!("../data/keyboards/th.yaml")),
 | 
				
			||||||
    ("th_wide", include_str!("../data/keyboards/th_wide.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("ua", include_str!("../data/keyboards/ua.yaml")),
 | 
					    ("ua", include_str!("../data/keyboards/ua.yaml")),
 | 
				
			||||||
 | 
					    ("bg", include_str!("../data/keyboards/bg.yaml")),
 | 
				
			||||||
    ("us+colemak", include_str!("../data/keyboards/us+colemak.yaml")),
 | 
					    // layout+overlay
 | 
				
			||||||
    ("us+colemak_wide", include_str!("../data/keyboards/us+colemak_wide.yaml")),
 | 
					    ("terminal", include_str!("../data/keyboards/terminal.yaml")),
 | 
				
			||||||
 | 
					    ("terminal_wide", include_str!("../data/keyboards/terminal_wide.yaml")),
 | 
				
			||||||
    ("us+dvorak", include_str!("../data/keyboards/us+dvorak.yaml")),
 | 
					 | 
				
			||||||
    ("us+dvorak_wide", include_str!("../data/keyboards/us+dvorak_wide.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Others
 | 
					 | 
				
			||||||
    ("number/us", include_str!("../data/keyboards/number/us.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Terminal
 | 
					 | 
				
			||||||
    ("terminal/fr", include_str!("../data/keyboards/terminal/fr.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ("terminal/us", include_str!("../data/keyboards/terminal/us.yaml")),
 | 
					 | 
				
			||||||
    ("terminal/us_wide",   include_str!("../data/keyboards/terminal/us_wide.yaml")),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Overlays
 | 
					    // Overlays
 | 
				
			||||||
    ("emoji/us", include_str!("../data/keyboards/emoji/us.yaml")),
 | 
					    ("emoji", include_str!("../data/keyboards/emoji.yaml")),
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn get_keyboard(needle: &str) -> Option<&'static str> {
 | 
					pub fn get_keyboard(needle: &str) -> Option<&'static str> {
 | 
				
			||||||
    KEYBOARDS.iter().find(|(name, _)| *name == needle).map(|(_, layout)| *layout)
 | 
					    // Need to dereference in unsafe code
 | 
				
			||||||
 | 
					    // comparing *const str to &str will compare pointers
 | 
				
			||||||
 | 
					    KEYBOARDS.iter()
 | 
				
			||||||
 | 
					        .find(|(name, _)| {
 | 
				
			||||||
 | 
					            let name: *const str = *name;
 | 
				
			||||||
 | 
					            (unsafe { &*name }) == needle
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .map(|(_, value)| {
 | 
				
			||||||
 | 
					            let value: *const str = *value;
 | 
				
			||||||
 | 
					            unsafe { &*value }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static OVERLAY_NAMES: &[&'static str] = &[
 | 
					const OVERLAY_NAMES: &[*const str] = &[
 | 
				
			||||||
    "emoji",
 | 
					    "emoji",
 | 
				
			||||||
    "terminal",
 | 
					    "terminal",
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn get_overlays() -> Vec<&'static str> {
 | 
					pub fn get_overlays() -> Vec<&'static str> {
 | 
				
			||||||
    OVERLAY_NAMES.to_vec()
 | 
					    OVERLAY_NAMES.iter()
 | 
				
			||||||
 | 
					        .map(|name| {
 | 
				
			||||||
 | 
					            let name: *const str = *name;
 | 
				
			||||||
 | 
					            unsafe { &*name }
 | 
				
			||||||
 | 
					        }).collect()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Translations of the layout identifier strings
 | 
					/// Translations of the layout identifier strings
 | 
				
			||||||
static LAYOUT_NAMES: &[(&'static str, &'static str)] = &[
 | 
					const LAYOUT_NAMES: &[(*const str, *const str)] = &[
 | 
				
			||||||
    ("de-DE", include_str!("../data/langs/de-DE.txt")),
 | 
					    ("de-DE", include_str!("../data/langs/de-DE.txt")),
 | 
				
			||||||
    ("en-US", include_str!("../data/langs/en-US.txt")),
 | 
					    ("en-US", include_str!("../data/langs/en-US.txt")),
 | 
				
			||||||
    ("es-ES", include_str!("../data/langs/es-ES.txt")),
 | 
					    ("es-ES", include_str!("../data/langs/es-ES.txt")),
 | 
				
			||||||
    ("fur-IT", include_str!("../data/langs/fur-IT.txt")),
 | 
					    ("fur-IT", include_str!("../data/langs/fur-IT.txt")),
 | 
				
			||||||
    ("he-IL", include_str!("../data/langs/he-IL.txt")),
 | 
					 | 
				
			||||||
    ("ja-JP", include_str!("../data/langs/ja-JP.txt")),
 | 
					    ("ja-JP", include_str!("../data/langs/ja-JP.txt")),
 | 
				
			||||||
    ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
 | 
					    ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
 | 
				
			||||||
    ("ru-RU", include_str!("../data/langs/ru-RU.txt")),
 | 
					    ("ru-RU", include_str!("../data/langs/ru-RU.txt")),
 | 
				
			||||||
@ -121,8 +94,14 @@ pub fn get_layout_names(lang: &str)
 | 
				
			|||||||
    -> Option<HashMap<&'static str, Translation<'static>>>
 | 
					    -> Option<HashMap<&'static str, Translation<'static>>>
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    let translations = LAYOUT_NAMES.iter()
 | 
					    let translations = LAYOUT_NAMES.iter()
 | 
				
			||||||
        .find(|(name, _data)| *name == lang)
 | 
					        .find(|(name, _data)| {
 | 
				
			||||||
        .map(|(_name, data)| *data);
 | 
					            let name: *const str = *name;
 | 
				
			||||||
 | 
					            (unsafe { &*name }) == lang
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .map(|(_name, data)| {
 | 
				
			||||||
 | 
					            let data: *const str = *data;
 | 
				
			||||||
 | 
					            unsafe { &*data }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    translations.map(make_mapping)
 | 
					    translations.map(make_mapping)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -152,7 +131,7 @@ mod test {
 | 
				
			|||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn check_overlays_present() {
 | 
					    fn check_overlays_present() {
 | 
				
			||||||
        for name in get_overlays() {
 | 
					        for name in get_overlays() {
 | 
				
			||||||
            assert!(get_keyboard(&format!("{}/us", name)).is_some());
 | 
					            assert!(get_keyboard(name).is_some());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -239,13 +239,6 @@ server_context_service_real_show_keyboard (ServerContextService *self)
 | 
				
			|||||||
    gtk_widget_show (GTK_WIDGET(self->window));
 | 
					    gtk_widget_show (GTK_WIDGET(self->window));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static gboolean
 | 
					 | 
				
			||||||
show_keyboard_source_func(ServerContextService *context)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    server_context_service_real_show_keyboard(context);
 | 
					 | 
				
			||||||
    return G_SOURCE_REMOVE;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
					server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -253,13 +246,6 @@ server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
				
			|||||||
    self->visible = FALSE;
 | 
					    self->visible = FALSE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static gboolean
 | 
					 | 
				
			||||||
hide_keyboard_source_func(ServerContextService *context)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    server_context_service_real_hide_keyboard(context);
 | 
					 | 
				
			||||||
    return G_SOURCE_REMOVE;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gboolean
 | 
					static gboolean
 | 
				
			||||||
on_hide (ServerContextService *self)
 | 
					on_hide (ServerContextService *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -269,7 +255,7 @@ on_hide (ServerContextService *self)
 | 
				
			|||||||
    return G_SOURCE_REMOVE;
 | 
					    return G_SOURCE_REMOVE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					void
 | 
				
			||||||
server_context_service_show_keyboard (ServerContextService *self)
 | 
					server_context_service_show_keyboard (ServerContextService *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
					    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
				
			||||||
@ -280,30 +266,17 @@ server_context_service_show_keyboard (ServerContextService *self)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!self->visible) {
 | 
					    if (!self->visible) {
 | 
				
			||||||
        g_idle_add((GSourceFunc)show_keyboard_source_func, self);
 | 
					        server_context_service_real_show_keyboard (self);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					 | 
				
			||||||
server_context_service_force_show_keyboard (ServerContextService *self)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (!submission_hint_available(self->submission)) {
 | 
					 | 
				
			||||||
        eekboard_context_service_set_hint_purpose(
 | 
					 | 
				
			||||||
            self->state,
 | 
					 | 
				
			||||||
            ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE,
 | 
					 | 
				
			||||||
            ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    server_context_service_show_keyboard(self);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
server_context_service_hide_keyboard (ServerContextService *self)
 | 
					server_context_service_hide_keyboard (ServerContextService *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
					    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (self->visible) {
 | 
					    if (self->visible) {
 | 
				
			||||||
        g_idle_add((GSourceFunc)hide_keyboard_source_func, self);
 | 
					        server_context_service_real_hide_keyboard (self);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONT
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct vis_manager *visman);
 | 
					ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct vis_manager *visman);
 | 
				
			||||||
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
 | 
					enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
 | 
				
			||||||
void server_context_service_force_show_keyboard (ServerContextService *self);
 | 
					void server_context_service_show_keyboard (ServerContextService *self);
 | 
				
			||||||
void server_context_service_hide_keyboard (ServerContextService *self);
 | 
					void server_context_service_hide_keyboard (ServerContextService *self);
 | 
				
			||||||
G_END_DECLS
 | 
					G_END_DECLS
 | 
				
			||||||
#endif  /* SERVER_CONTEXT_SERVICE_H */
 | 
					#endif  /* SERVER_CONTEXT_SERVICE_H */
 | 
				
			||||||
 | 
				
			|||||||
@ -50,16 +50,9 @@ struct squeekboard {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GMainLoop *loop;
 | 
					 | 
				
			||||||
static gboolean opt_system = FALSE;
 | 
					static gboolean opt_system = FALSE;
 | 
				
			||||||
static gchar *opt_address = NULL;
 | 
					static gchar *opt_address = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
quit (void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  g_main_loop_quit (loop);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// D-Bus
 | 
					// D-Bus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
@ -138,67 +131,6 @@ static const struct wl_registry_listener registry_listener = {
 | 
				
			|||||||
#define SESSION_NAME "sm.puri.OSK0"
 | 
					#define SESSION_NAME "sm.puri.OSK0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GDBusProxy *_proxy = NULL;
 | 
					GDBusProxy *_proxy = NULL;
 | 
				
			||||||
GDBusProxy *_client_proxy = NULL;
 | 
					 | 
				
			||||||
gchar      *_client_path = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
send_quit_response (GDBusProxy  *proxy)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    g_debug ("Calling EndSessionResponse");
 | 
					 | 
				
			||||||
    g_dbus_proxy_call (proxy, "EndSessionResponse",
 | 
					 | 
				
			||||||
        g_variant_new ("(bs)", TRUE, ""), G_DBUS_CALL_FLAGS_NONE,
 | 
					 | 
				
			||||||
        G_MAXINT, NULL, NULL, NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
unregister_client (void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    g_autoptr (GError) error = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_return_if_fail (G_IS_DBUS_PROXY (_proxy));
 | 
					 | 
				
			||||||
    g_return_if_fail (_client_path != NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_debug ("Unregistering client");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_dbus_proxy_call_sync (_proxy,
 | 
					 | 
				
			||||||
			    "UnregisterClient",
 | 
					 | 
				
			||||||
			    g_variant_new ("(o)", _client_path),
 | 
					 | 
				
			||||||
			    G_DBUS_CALL_FLAGS_NONE,
 | 
					 | 
				
			||||||
			    G_MAXINT,
 | 
					 | 
				
			||||||
			    NULL,
 | 
					 | 
				
			||||||
			    &error);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (error) {
 | 
					 | 
				
			||||||
        g_warning ("Failed to unregister client: %s", error->message);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_clear_object (&_client_proxy);
 | 
					 | 
				
			||||||
    g_clear_pointer (&_client_path, g_free);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void client_proxy_signal (GDBusProxy  *proxy,
 | 
					 | 
				
			||||||
				 const gchar *sender_name,
 | 
					 | 
				
			||||||
				 const gchar *signal_name,
 | 
					 | 
				
			||||||
				 GVariant    *parameters,
 | 
					 | 
				
			||||||
				 gpointer     user_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (g_str_equal (signal_name, "QueryEndSession")) {
 | 
					 | 
				
			||||||
        g_debug ("Received QueryEndSession");
 | 
					 | 
				
			||||||
        send_quit_response (proxy);
 | 
					 | 
				
			||||||
    } else if (g_str_equal (signal_name, "CancelEndSession")) {
 | 
					 | 
				
			||||||
        g_debug ("Received CancelEndSession");
 | 
					 | 
				
			||||||
    } else if (g_str_equal (signal_name, "EndSession")) {
 | 
					 | 
				
			||||||
        g_debug ("Received EndSession");
 | 
					 | 
				
			||||||
        send_quit_response (proxy);
 | 
					 | 
				
			||||||
        unregister_client ();
 | 
					 | 
				
			||||||
	quit ();
 | 
					 | 
				
			||||||
    } else if (g_str_equal (signal_name, "Stop")) {
 | 
					 | 
				
			||||||
        g_debug ("Received Stop");
 | 
					 | 
				
			||||||
        unregister_client ();
 | 
					 | 
				
			||||||
        quit ();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
session_register(void) {
 | 
					session_register(void) {
 | 
				
			||||||
@ -219,8 +151,7 @@ session_register(void) {
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    g_autoptr (GVariant) res = NULL;
 | 
					    g_dbus_proxy_call_sync(_proxy, "RegisterClient",
 | 
				
			||||||
    res = g_dbus_proxy_call_sync(_proxy, "RegisterClient",
 | 
					 | 
				
			||||||
        g_variant_new("(ss)", SESSION_NAME, autostart_id),
 | 
					        g_variant_new("(ss)", SESSION_NAME, autostart_id),
 | 
				
			||||||
        G_DBUS_CALL_FLAGS_NONE, 1000, NULL, &error);
 | 
					        G_DBUS_CALL_FLAGS_NONE, 1000, NULL, &error);
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
@ -229,22 +160,6 @@ session_register(void) {
 | 
				
			|||||||
        g_clear_error(&error);
 | 
					        g_clear_error(&error);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_variant_get (res, "(o)", &_client_path);
 | 
					 | 
				
			||||||
    g_debug ("Registered client at '%s'", _client_path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _client_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
 | 
					 | 
				
			||||||
      0, NULL, "org.gnome.SessionManager", _client_path,
 | 
					 | 
				
			||||||
      "org.gnome.SessionManager.ClientPrivate", NULL, &error);
 | 
					 | 
				
			||||||
    if (error) {
 | 
					 | 
				
			||||||
        g_warning ("Failed to get client proxy: %s", error->message);
 | 
					 | 
				
			||||||
	g_clear_error (&error);
 | 
					 | 
				
			||||||
	g_free (_client_path);
 | 
					 | 
				
			||||||
	_client_path = NULL;
 | 
					 | 
				
			||||||
	return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    g_signal_connect (_client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), NULL);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
@ -392,7 +307,8 @@ main (int argc, char **argv)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    session_register();
 | 
					    session_register();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop = g_main_loop_new (NULL, FALSE);
 | 
					    GMainLoop *loop = g_main_loop_new (NULL, FALSE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    g_main_loop_run (loop);
 | 
					    g_main_loop_run (loop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (connection) {
 | 
					    if (connection) {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,6 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Defined in Rust
 | 
					// Defined in Rust
 | 
				
			||||||
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state, struct vis_manager *vis_manager);
 | 
					struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state, struct vis_manager *vis_manager);
 | 
				
			||||||
uint8_t submission_hint_available(struct submission *self);
 | 
					 | 
				
			||||||
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
 | 
					void submission_set_ui(struct submission *self, ServerContextService *ui_context);
 | 
				
			||||||
void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time);
 | 
					void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -93,18 +93,6 @@ pub mod c {
 | 
				
			|||||||
        let layout = unsafe { &*layout };
 | 
					        let layout = unsafe { &*layout };
 | 
				
			||||||
        submission.use_layout(layout, Timestamp(time));
 | 
					        submission.use_layout(layout, Timestamp(time));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[no_mangle]
 | 
					 | 
				
			||||||
    pub extern "C"
 | 
					 | 
				
			||||||
    fn submission_hint_available(submission: *mut Submission) -> u8 {
 | 
					 | 
				
			||||||
        if submission.is_null() {
 | 
					 | 
				
			||||||
            panic!("Null submission pointer");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        let submission: &mut Submission = unsafe { &mut *submission };
 | 
					 | 
				
			||||||
        let active = submission.imservice.as_ref()
 | 
					 | 
				
			||||||
            .map(|imservice| imservice.is_active());
 | 
					 | 
				
			||||||
        (Some(true) == active) as u8
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					#[derive(Clone, Copy)]
 | 
				
			||||||
@ -149,7 +137,7 @@ impl Submission {
 | 
				
			|||||||
                enum Outcome {
 | 
					                enum Outcome {
 | 
				
			||||||
                    Submitted(Result<(), imservice::SubmitError>),
 | 
					                    Submitted(Result<(), imservice::SubmitError>),
 | 
				
			||||||
                    NotSubmitted,
 | 
					                    NotSubmitted,
 | 
				
			||||||
                }
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let submit_outcome = match data {
 | 
					                let submit_outcome = match data {
 | 
				
			||||||
                    SubmitData::Text(text) => {
 | 
					                    SubmitData::Text(text) => {
 | 
				
			||||||
@ -270,7 +258,6 @@ impl Submission {
 | 
				
			|||||||
            .map(|(_id, m)| match m {
 | 
					            .map(|(_id, m)| match m {
 | 
				
			||||||
                Modifier::Control => Modifiers::CONTROL,
 | 
					                Modifier::Control => Modifiers::CONTROL,
 | 
				
			||||||
                Modifier::Alt => Modifiers::MOD1,
 | 
					                Modifier::Alt => Modifiers::MOD1,
 | 
				
			||||||
                Modifier::Mod4 => Modifiers::MOD4,
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .fold(Modifiers::empty(), |m, n| m | n);
 | 
					            .fold(Modifiers::empty(), |m, n| m | n);
 | 
				
			||||||
        self.virtual_keyboard.set_modifiers_state(raw_modifiers);
 | 
					        self.virtual_keyboard.set_modifiers_state(raw_modifiers);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
/*! Testing functionality */
 | 
					/*! Testing functionality */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use ::data::parsing::Layout;
 | 
					use ::data::Layout;
 | 
				
			||||||
use ::logging;
 | 
					use ::logging;
 | 
				
			||||||
use xkbcommon::xkb;
 | 
					use xkbcommon::xkb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,7 +43,7 @@ pub fn check_layout_file(path: &str) {
 | 
				
			|||||||
fn check_sym_in_keymap(state: &xkb::State, sym_name: &str) -> bool {
 | 
					fn check_sym_in_keymap(state: &xkb::State, sym_name: &str) -> bool {
 | 
				
			||||||
    let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
 | 
					    let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
 | 
				
			||||||
    if sym == xkb::KEY_NoSymbol {
 | 
					    if sym == xkb::KEY_NoSymbol {
 | 
				
			||||||
        panic!("Entered invalid keysym: {}", sym_name);
 | 
					        panic!(format!("Entered invalid keysym: {}", sym_name));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let map = state.get_keymap();
 | 
					    let map = state.get_keymap();
 | 
				
			||||||
    let range = map.min_keycode()..=map.max_keycode();
 | 
					    let range = map.min_keycode()..=map.max_keycode();
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ pub mod c {
 | 
				
			|||||||
    #[repr(transparent)]
 | 
					    #[repr(transparent)]
 | 
				
			||||||
    pub struct UIManager(*const c_void);
 | 
					    pub struct UIManager(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
        pub fn server_context_service_update_visible(imservice: *const UIManager, active: u32);
 | 
					        pub fn server_context_service_update_visible(imservice: *const UIManager, active: u32);
 | 
				
			||||||
        pub fn server_context_service_release_visibility(imservice: *const UIManager);
 | 
					        pub fn server_context_service_release_visibility(imservice: *const UIManager);
 | 
				
			||||||
 | 
				
			|||||||
@ -37,6 +37,7 @@ pub mod c {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[no_mangle]
 | 
				
			||||||
    extern "C" {
 | 
					    extern "C" {
 | 
				
			||||||
        // From libc, to let KeyMap get deallocated.
 | 
					        // From libc, to let KeyMap get deallocated.
 | 
				
			||||||
        fn close(fd: u32);
 | 
					        fn close(fd: u32);
 | 
				
			||||||
 | 
				
			|||||||
@ -46,56 +46,37 @@ endforeach
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# The layout test is in the examples directory
 | 
					# The layout test is in the examples directory
 | 
				
			||||||
# due to the way Cargo builds executables
 | 
					# due to the way Cargo builds executables
 | 
				
			||||||
# and the need to call it manually.
 | 
					# and the need to call it manually
 | 
				
			||||||
 | 
					 | 
				
			||||||
# This is the list of tested builtin layouts.
 | 
					 | 
				
			||||||
# Please keep each block alphabetical!
 | 
					 | 
				
			||||||
# Please keep shapes (with _) on the same line,
 | 
					 | 
				
			||||||
# variants (with +) on separate lines.
 | 
					 | 
				
			||||||
foreach layout : [
 | 
					foreach layout : [
 | 
				
			||||||
    # This is the fallback layout,
 | 
					    'us', 'us+colemak', 'us_wide',
 | 
				
			||||||
    # so stays first to make sure it never goes missing.
 | 
					    'br',
 | 
				
			||||||
    'us', 'us_wide',
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Block: Languages
 | 
					 | 
				
			||||||
    'be', 'be_wide',
 | 
					    'be', 'be_wide',
 | 
				
			||||||
    'bg',
 | 
					    'bg',
 | 
				
			||||||
    'br',
 | 
					 | 
				
			||||||
    'cz', 'cz_wide',
 | 
					    'cz', 'cz_wide',
 | 
				
			||||||
    'cz+qwerty', 'cz+qwerty_wide',
 | 
					    'cz+qwerty', 'cz+qwerty_wide',
 | 
				
			||||||
    'de', 'de_wide',
 | 
					    'de', 'de_wide',
 | 
				
			||||||
    'dk',
 | 
					    'dk',
 | 
				
			||||||
    'epo',
 | 
					    'epo',
 | 
				
			||||||
    'es',
 | 
					    'es',
 | 
				
			||||||
    'es+cat',
 | 
					 | 
				
			||||||
    'fi',
 | 
					    'fi',
 | 
				
			||||||
    'fr', 'fr_wide',
 | 
					    'fr', 'fr_wide',
 | 
				
			||||||
    'gr',
 | 
					 | 
				
			||||||
    'il',
 | 
					 | 
				
			||||||
    'ir',
 | 
					 | 
				
			||||||
    'it',
 | 
					 | 
				
			||||||
    'it+fur',
 | 
					    'it+fur',
 | 
				
			||||||
 | 
					    'gr',
 | 
				
			||||||
 | 
					    'it',
 | 
				
			||||||
    'jp+kana','jp+kana_wide',
 | 
					    'jp+kana','jp+kana_wide',
 | 
				
			||||||
    'no',
 | 
					    'no',
 | 
				
			||||||
 | 
					    'number',
 | 
				
			||||||
    'pl', 'pl_wide',
 | 
					    'pl', 'pl_wide',
 | 
				
			||||||
    'ru',
 | 
					    'ru',
 | 
				
			||||||
    'se',
 | 
					    'se',
 | 
				
			||||||
    'th', 'th_wide',
 | 
					 | 
				
			||||||
    'ua',
 | 
					    'ua',
 | 
				
			||||||
    'us+colemak', 'us+colemak_wide',
 | 
					    'th',
 | 
				
			||||||
    'us+dvorak', 'us+dvorak_wide',
 | 
					    'terminal', 'terminal_wide',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Terminal keyboards
 | 
					    'emoji',
 | 
				
			||||||
    'terminal/fr', 
 | 
					 | 
				
			||||||
    'terminal/us',
 | 
					 | 
				
			||||||
    'terminal/us_wide',
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    # Block: Not languages.
 | 
					 | 
				
			||||||
    'emoji/us',
 | 
					 | 
				
			||||||
    'number/us',
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
    extra = []
 | 
					    extra = []
 | 
				
			||||||
    if layout.startswith('emoji/')
 | 
					    if layout == 'emoji'
 | 
				
			||||||
        extra += ['allow_missing_return']
 | 
					        extra += ['allow_missing_return']
 | 
				
			||||||
    endif
 | 
					    endif
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user