Compare commits
	
		
			66 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2641a3b3d8 | |||
| b45039a813 | |||
| 04b105ab38 | |||
| 80388c0042 | |||
| 37820bf169 | |||
| d8e58fd774 | |||
| fcb57c9093 | |||
| b578414655 | |||
| 89b1f51ed5 | |||
| 7b1755a489 | |||
| 676a2b60ac | |||
| 32dc25dfbf | |||
| 484d64cfb9 | |||
| 637da2c177 | |||
| 3210a363ab | |||
| 8da8d55b98 | |||
| d8ca9f47ca | |||
| a3638f4bfb | |||
| f45f2db948 | |||
| 40bf3ca5de | |||
| e800a88893 | |||
| 24c3fac505 | |||
| 46f8790fc0 | |||
| 3cdced0c0c | |||
| bffd212e10 | |||
| c2c379b870 | |||
| 1ae29ff7bc | |||
| d3cd7dc11f | |||
| d3695d3bc9 | |||
| 11952ed29a | |||
| 842e616cd3 | |||
| a265427e8e | |||
| e6c45a63fb | |||
| e82e256581 | |||
| bedabb6188 | |||
| a030f55a7c | |||
| 49aa4256a9 | |||
| eb7d0d5db9 | |||
| 575619e812 | |||
| 630cfc8e59 | |||
| 2a11bce945 | |||
| a332efca45 | |||
| 5b3c185a16 | |||
| fefebf7f6e | |||
| 21c3a74019 | |||
| 47a483da2a | |||
| 0c179560b3 | |||
| 38842f9743 | |||
| 3cbfd8351c | |||
| 6e7c0e6f67 | |||
| 0e83697b61 | |||
| 66c3926eb2 | |||
| 1856e7023d | |||
| 976f0a6e37 | |||
| 4d24af4e1a | |||
| 422d06d582 | |||
| 4890c86b4e | |||
| 658df98e18 | |||
| 6f7252ec7c | |||
| c6cc58fd8e | |||
| 9522d4e302 | |||
| 8f62520648 | |||
| e36c4e597f | |||
| 8ab6997b21 | |||
| 3b06eadef5 | |||
| 287e851770 | 
							
								
								
									
										44
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										44
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -59,9 +59,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cc"
 | 
			
		||||
version = "1.0.65"
 | 
			
		||||
version = "1.0.66"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
 | 
			
		||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap"
 | 
			
		||||
@ -76,9 +76,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "dtoa"
 | 
			
		||||
version = "0.4.6"
 | 
			
		||||
version = "0.4.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
 | 
			
		||||
checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fragile"
 | 
			
		||||
@ -265,15 +265,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libc"
 | 
			
		||||
version = "0.2.80"
 | 
			
		||||
version = "0.2.82"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
 | 
			
		||||
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "linked-hash-map"
 | 
			
		||||
version = "0.5.3"
 | 
			
		||||
version = "0.5.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
 | 
			
		||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "maplit"
 | 
			
		||||
@ -335,9 +335,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "quote"
 | 
			
		||||
version = "1.0.7"
 | 
			
		||||
version = "1.0.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
 | 
			
		||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
]
 | 
			
		||||
@ -353,9 +353,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "regex-syntax"
 | 
			
		||||
version = "0.6.21"
 | 
			
		||||
version = "0.6.22"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
 | 
			
		||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rs"
 | 
			
		||||
@ -380,18 +380,18 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde"
 | 
			
		||||
version = "1.0.117"
 | 
			
		||||
version = "1.0.118"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
 | 
			
		||||
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "serde_derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_derive"
 | 
			
		||||
version = "1.0.117"
 | 
			
		||||
version = "1.0.118"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
 | 
			
		||||
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
@ -400,9 +400,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_yaml"
 | 
			
		||||
version = "0.8.14"
 | 
			
		||||
version = "0.8.15"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7"
 | 
			
		||||
checksum = "971be8f6e4d4a47163b405a3df70d14359186f9ab0f3a3ec37df144ca1ce089f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "dtoa",
 | 
			
		||||
 "linked-hash-map",
 | 
			
		||||
@ -412,9 +412,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "syn"
 | 
			
		||||
version = "1.0.48"
 | 
			
		||||
version = "1.0.58"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
 | 
			
		||||
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
@ -476,9 +476,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "yaml-rust"
 | 
			
		||||
version = "0.4.4"
 | 
			
		||||
version = "0.4.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
 | 
			
		||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "linked-hash-map",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										87
									
								
								data/keyboards/es+cat.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								data/keyboards/es+cat.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
			
		||||
---
 | 
			
		||||
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: ":"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								data/keyboards/il.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								data/keyboards/il.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
---
 | 
			
		||||
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: ":"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										78
									
								
								data/keyboards/ir.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								data/keyboards/ir.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
---
 | 
			
		||||
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: ":"
 | 
			
		||||
							
								
								
									
										78
									
								
								data/keyboards/ir_wide.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								data/keyboards/ir_wide.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
---
 | 
			
		||||
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:
 | 
			
		||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
			
		||||
        - "@ # € % & - _ + ( )"
 | 
			
		||||
        - "show_symbols   , \" ' colon ; ! =  BackSpace"
 | 
			
		||||
        - "show_symbols   , \" ' : ; ! =  BackSpace"
 | 
			
		||||
        - "show_letters show_eschars preferences         space        ? . Return"
 | 
			
		||||
    symbols:
 | 
			
		||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
			
		||||
@ -86,7 +86,4 @@ buttons:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "key-enter"
 | 
			
		||||
        keysym: "Return"
 | 
			
		||||
    colon:
 | 
			
		||||
        label: ":"
 | 
			
		||||
    "\"":
 | 
			
		||||
        keysym: "quotedbl"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ views:
 | 
			
		||||
    numbers:
 | 
			
		||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
			
		||||
        - "@ # € % & - _ + ( )"
 | 
			
		||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
			
		||||
        - "show_symbols   , \" ' : ; ! ?  BackSpace"
 | 
			
		||||
        - "show_letters show_eschars preferences         space        ? . Return"
 | 
			
		||||
    symbols:
 | 
			
		||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
			
		||||
@ -84,7 +84,4 @@ buttons:
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "key-enter"
 | 
			
		||||
        keysym: "Return"
 | 
			
		||||
    colon:
 | 
			
		||||
        label: ":"
 | 
			
		||||
    "\"":
 | 
			
		||||
        keysym: "quotedbl"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,8 @@ buttons:
 | 
			
		||||
            locking:
 | 
			
		||||
                lock_view: "upper_accents"
 | 
			
		||||
                unlock_view: "accents"
 | 
			
		||||
                looks_locked_from:
 | 
			
		||||
                    - "upper"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        icon: "key-shift"
 | 
			
		||||
    BackSpace:
 | 
			
		||||
@ -94,6 +96,8 @@ buttons:
 | 
			
		||||
            locking:
 | 
			
		||||
                lock_view: "upper_accents"
 | 
			
		||||
                unlock_view: "upper"
 | 
			
		||||
                looks_locked_from:
 | 
			
		||||
                    - "accents"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "ĄĘ"
 | 
			
		||||
    period:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										84
									
								
								data/keyboards/th_wide.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								data/keyboards/th_wide.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
---
 | 
			
		||||
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: ":"
 | 
			
		||||
							
								
								
									
										78
									
								
								data/keyboards/us+colemak_wide.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								data/keyboards/us+colemak_wide.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
---
 | 
			
		||||
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: ":"
 | 
			
		||||
							
								
								
									
										89
									
								
								data/keyboards/us+dvorak.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								data/keyboards/us+dvorak.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
---
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										89
									
								
								data/keyboards/us+dvorak_wide.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								data/keyboards/us+dvorak_wide.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
---
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										2
									
								
								data/langs/fa-IR.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								data/langs/fa-IR.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
emoji ایموجی
 | 
			
		||||
terminal ترمینال
 | 
			
		||||
							
								
								
									
										19
									
								
								data/langs/he-IL.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								data/langs/he-IL.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
be בלגית
 | 
			
		||||
br פורטוגזית (ברזיל)
 | 
			
		||||
cz צ'כית
 | 
			
		||||
de גרמנית
 | 
			
		||||
dk דנית
 | 
			
		||||
es ספרדית
 | 
			
		||||
emoji אימוג'י
 | 
			
		||||
fi פינית
 | 
			
		||||
fr צרפתית
 | 
			
		||||
gr יוונית
 | 
			
		||||
il עברית
 | 
			
		||||
it איטלקית
 | 
			
		||||
no נורווגית
 | 
			
		||||
pl פולנית
 | 
			
		||||
ru רוסית
 | 
			
		||||
se שוודית
 | 
			
		||||
terminal טרמינל
 | 
			
		||||
ua אוקראינית
 | 
			
		||||
us אנגלית (ארה"ב)
 | 
			
		||||
@ -31,11 +31,16 @@ sq_button.wide {
 | 
			
		||||
    border-color: #3e3a44;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.locked {
 | 
			
		||||
sq_button.latched {
 | 
			
		||||
    background: #ffffff;
 | 
			
		||||
    color: #2b292f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.locked {
 | 
			
		||||
    background: #ffffff;
 | 
			
		||||
    color: #1c71d8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.action {
 | 
			
		||||
    font-size: 0.75em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,11 +34,16 @@ sq_button.wide {
 | 
			
		||||
    border-color: @borders; /* #3e3a44; */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.locked {
 | 
			
		||||
sq_button.latched {
 | 
			
		||||
    background: @theme_fg_color; /*#ffffff;*/
 | 
			
		||||
    color: @theme_bg_color; /*#2b292f;*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.locked {
 | 
			
		||||
    background: @theme_fg_color; /*#ffffff;*/
 | 
			
		||||
    color: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#2b292f;*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.action {
 | 
			
		||||
    font-size: 0.75em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										60
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,63 @@
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
  [ Mark Müller ]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								debian/check_release.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								debian/check_release.py
									
									
									
									
										vendored
									
									
								
							@ -5,6 +5,7 @@ Feed it the first changelog line, and then all available tags.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import re, sys
 | 
			
		||||
tag = "v" + re.findall("\\((.*)\\)", input())[0]
 | 
			
		||||
version = 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()):
 | 
			
		||||
    raise Exception("Changelog's current version doesn't have a tag. Push the tag!")
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							@ -1 +1 @@
 | 
			
		||||
10
 | 
			
		||||
12
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@ -41,6 +41,7 @@ Depends:
 | 
			
		||||
 ${misc:Depends},
 | 
			
		||||
Breaks:
 | 
			
		||||
 librem5-base (<< 24),
 | 
			
		||||
 phosh (<= 0.10),
 | 
			
		||||
Description: On-screen keyboard for Wayland
 | 
			
		||||
 Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/squeekboard.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/squeekboard.install
									
									
									
									
										vendored
									
									
								
							@ -1,2 +1,2 @@
 | 
			
		||||
tools/squeekboard-restyled usr/bin
 | 
			
		||||
usr/bin/squeekboard /usr/bin
 | 
			
		||||
usr/share/applications/sm.puri.Squeekboard.desktop
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								debian/squeekboard.user.service
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								debian/squeekboard.user.service
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Squeekboard - a Wayland virtual keyboard
 | 
			
		||||
PartOf=graphical-session.target
 | 
			
		||||
# Don't stop restarting the program
 | 
			
		||||
StartLimitIntervalSec=0
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Environment="GTK_THEME=Adwaita:dark"
 | 
			
		||||
ExecStart=/usr/bin/squeekboard
 | 
			
		||||
 | 
			
		||||
Restart=on-failure
 | 
			
		||||
RestartSec=3s
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=graphical-session.target
 | 
			
		||||
 | 
			
		||||
@ -45,6 +45,8 @@
 | 
			
		||||
typedef struct _EekGtkKeyboardPrivate
 | 
			
		||||
{
 | 
			
		||||
    EekRenderer *renderer; // owned, nullable
 | 
			
		||||
    struct render_geometry render_geometry; // mutable
 | 
			
		||||
 | 
			
		||||
    EekboardContextService *eekboard_context; // unowned reference
 | 
			
		||||
    struct submission *submission; // unowned reference
 | 
			
		||||
 | 
			
		||||
@ -72,12 +74,23 @@ eek_gtk_keyboard_real_realize (GtkWidget      *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
 | 
			
		||||
eek_gtk_keyboard_real_draw (GtkWidget *self,
 | 
			
		||||
                            cairo_t   *cr)
 | 
			
		||||
{
 | 
			
		||||
    EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
 | 
			
		||||
    EekGtkKeyboardPrivate *priv =
 | 
			
		||||
        eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
 | 
			
		||||
        eek_gtk_keyboard_get_instance_private (keyboard);
 | 
			
		||||
    GtkAllocation allocation;
 | 
			
		||||
    gtk_widget_get_allocation (self, &allocation);
 | 
			
		||||
 | 
			
		||||
@ -92,15 +105,14 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
 | 
			
		||||
                    priv->keyboard,
 | 
			
		||||
                    pcontext);
 | 
			
		||||
 | 
			
		||||
        eek_renderer_set_allocation_size (priv->renderer,
 | 
			
		||||
                                          priv->keyboard->layout,
 | 
			
		||||
                                          allocation.width,
 | 
			
		||||
                                          allocation.height);
 | 
			
		||||
        set_allocation_size (keyboard, priv->keyboard->layout,
 | 
			
		||||
            allocation.width, allocation.height);
 | 
			
		||||
        eek_renderer_set_scale_factor (priv->renderer,
 | 
			
		||||
                                       gtk_widget_get_scale_factor (self));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    eek_renderer_render_keyboard (priv->renderer, priv->submission, cr, priv->keyboard);
 | 
			
		||||
    eek_renderer_render_keyboard (priv->renderer, priv->render_geometry,
 | 
			
		||||
        priv->submission, cr, priv->keyboard);
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -117,8 +129,9 @@ static void
 | 
			
		||||
eek_gtk_keyboard_real_size_allocate (GtkWidget     *self,
 | 
			
		||||
                                     GtkAllocation *allocation)
 | 
			
		||||
{
 | 
			
		||||
    EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
 | 
			
		||||
    EekGtkKeyboardPrivate *priv =
 | 
			
		||||
        eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
 | 
			
		||||
        eek_gtk_keyboard_get_instance_private (keyboard);
 | 
			
		||||
    // check if the change would switch types
 | 
			
		||||
    enum squeek_arrangement_kind new_type = get_type(
 | 
			
		||||
                (uint32_t)(allocation->width - allocation->x),
 | 
			
		||||
@ -130,10 +143,8 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget     *self,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (priv->renderer) {
 | 
			
		||||
        eek_renderer_set_allocation_size (priv->renderer,
 | 
			
		||||
                                          priv->keyboard->layout,
 | 
			
		||||
                                          allocation->width,
 | 
			
		||||
                                          allocation->height);
 | 
			
		||||
        set_allocation_size (keyboard, priv->keyboard->layout,
 | 
			
		||||
            allocation->width, allocation->height);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->
 | 
			
		||||
@ -162,7 +173,7 @@ static void depress(EekGtkKeyboard *self,
 | 
			
		||||
    }
 | 
			
		||||
    squeek_layout_depress(priv->keyboard->layout,
 | 
			
		||||
                          priv->submission,
 | 
			
		||||
                          x, y, eek_renderer_get_transformation(priv->renderer), time, self);
 | 
			
		||||
                          x, y, priv->render_geometry.widget_to_layout, time, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void drag(EekGtkKeyboard *self,
 | 
			
		||||
@ -174,7 +185,7 @@ static void drag(EekGtkKeyboard *self,
 | 
			
		||||
    }
 | 
			
		||||
    squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
			
		||||
                       priv->submission,
 | 
			
		||||
                       x, y, eek_renderer_get_transformation(priv->renderer), time,
 | 
			
		||||
                       x, y, priv->render_geometry.widget_to_layout, time,
 | 
			
		||||
                       priv->eekboard_context, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -185,8 +196,7 @@ static void release(EekGtkKeyboard *self, guint32 time)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
 | 
			
		||||
                          priv->submission,
 | 
			
		||||
                          eek_renderer_get_transformation(priv->renderer), time,
 | 
			
		||||
                          priv->submission, priv->render_geometry.widget_to_layout, time,
 | 
			
		||||
                          priv->eekboard_context, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -396,6 +406,24 @@ eek_gtk_keyboard_new (EekboardContextService *eekservice,
 | 
			
		||||
    priv->submission = submission;
 | 
			
		||||
    priv->layout = layout;
 | 
			
		||||
    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,
 | 
			
		||||
                      "notify::keyboard",
 | 
			
		||||
                      G_CALLBACK(on_notify_keyboard),
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
#include <gtk/gtk.h>
 | 
			
		||||
 | 
			
		||||
#include "eek/eek-renderer.h"
 | 
			
		||||
#include "eek/eek-types.h"
 | 
			
		||||
 | 
			
		||||
struct submission;
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,6 @@
 | 
			
		||||
 * 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <gdk-pixbuf/gdk-pixbuf.h>
 | 
			
		||||
@ -33,10 +31,6 @@
 | 
			
		||||
static void render_button_label (cairo_t *cr, GtkStyleContext *ctx,
 | 
			
		||||
                                                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
 | 
			
		||||
render_outline (cairo_t     *cr,
 | 
			
		||||
                GtkStyleContext *ctx,
 | 
			
		||||
@ -60,21 +54,21 @@ render_outline (cairo_t     *cr,
 | 
			
		||||
        position.x, position.y, position.width, position.height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void render_button_in_context(gint scale_factor,
 | 
			
		||||
/// Rust interface
 | 
			
		||||
void eek_render_button_in_context(uint32_t scale_factor,
 | 
			
		||||
                                     cairo_t     *cr,
 | 
			
		||||
                                     GtkStyleContext *ctx,
 | 
			
		||||
                                     const struct squeek_button *button) {
 | 
			
		||||
                                     EekBounds bounds,
 | 
			
		||||
                                     const char *icon_name,
 | 
			
		||||
                                     const gchar *label) {
 | 
			
		||||
    /* blank background */
 | 
			
		||||
    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
 | 
			
		||||
    cairo_paint (cr);
 | 
			
		||||
 | 
			
		||||
    EekBounds bounds = squeek_button_get_bounds(button);
 | 
			
		||||
    render_outline (cr, ctx, bounds);
 | 
			
		||||
    cairo_paint (cr);
 | 
			
		||||
 | 
			
		||||
    /* render icon (if any) */
 | 
			
		||||
    const char *icon_name = squeek_button_get_icon_name(button);
 | 
			
		||||
 | 
			
		||||
    if (icon_name) {
 | 
			
		||||
        cairo_surface_t *icon_surface =
 | 
			
		||||
            eek_renderer_get_icon_surface (icon_name, 16, scale_factor);
 | 
			
		||||
@ -104,25 +98,27 @@ static void render_button_in_context(gint scale_factor,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const gchar *label = squeek_button_get_label(button);
 | 
			
		||||
    if (label) {
 | 
			
		||||
        render_button_label (cr, ctx, label, squeek_button_get_bounds(button));
 | 
			
		||||
        render_button_label (cr, ctx, label, bounds);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
eek_render_button (EekRenderer *self,
 | 
			
		||||
            cairo_t     *cr,
 | 
			
		||||
            const struct squeek_button *button,
 | 
			
		||||
               gboolean     pressed,
 | 
			
		||||
               gboolean     locked)
 | 
			
		||||
/// Prepare context for drawing the button.
 | 
			
		||||
/// The context MUST be released using the corresponing "put" procedure
 | 
			
		||||
/// before drawing the next button.
 | 
			
		||||
/// Interface for Rust.
 | 
			
		||||
GtkStyleContext *
 | 
			
		||||
eek_get_style_context_for_button (EekRenderer *self,
 | 
			
		||||
                                  const char *name,
 | 
			
		||||
                                  const char *outline_name,
 | 
			
		||||
                                  const char *locked_class,
 | 
			
		||||
               uint64_t     pressed)
 | 
			
		||||
{
 | 
			
		||||
    GtkStyleContext *ctx = self->button_context;
 | 
			
		||||
    /* Set the name of the button on the widget path, using the name obtained
 | 
			
		||||
       from the button's symbol. */
 | 
			
		||||
    g_autoptr (GtkWidgetPath) path = NULL;
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    /* Update the style context with the updated widget path. */
 | 
			
		||||
@ -131,19 +127,22 @@ eek_render_button (EekRenderer *self,
 | 
			
		||||
       (pressed) or normal. */
 | 
			
		||||
    gtk_style_context_set_state(ctx,
 | 
			
		||||
        pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
 | 
			
		||||
    const char *outline_name = squeek_button_get_outline_name(button);
 | 
			
		||||
    if (locked) {
 | 
			
		||||
        gtk_style_context_add_class(ctx, "locked");
 | 
			
		||||
    if (locked_class) {
 | 
			
		||||
        gtk_style_context_add_class(ctx, locked_class);
 | 
			
		||||
    }
 | 
			
		||||
    gtk_style_context_add_class(ctx, outline_name);
 | 
			
		||||
    return ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    render_button_in_context(self->scale_factor, cr, ctx, button);
 | 
			
		||||
 | 
			
		||||
/// Interface for Rust.
 | 
			
		||||
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
 | 
			
		||||
    gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
 | 
			
		||||
    gtk_style_context_remove_class(ctx, outline_name);
 | 
			
		||||
    if (locked) {
 | 
			
		||||
        gtk_style_context_remove_class(ctx, "locked");
 | 
			
		||||
    if (locked_class) {
 | 
			
		||||
        gtk_style_context_remove_class(ctx, locked_class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -194,22 +193,23 @@ render_button_label (cairo_t     *cr,
 | 
			
		||||
// FIXME: Pass just the active modifiers instead of entire submission
 | 
			
		||||
void
 | 
			
		||||
eek_renderer_render_keyboard (EekRenderer *self,
 | 
			
		||||
                              struct render_geometry geometry,
 | 
			
		||||
                              struct submission *submission,
 | 
			
		||||
                                   cairo_t     *cr,
 | 
			
		||||
                              LevelKeyboard *keyboard)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (self->allocation_width > 0.0);
 | 
			
		||||
    g_return_if_fail (self->allocation_height > 0.0);
 | 
			
		||||
    g_return_if_fail (geometry.allocation_width > 0.0);
 | 
			
		||||
    g_return_if_fail (geometry.allocation_height > 0.0);
 | 
			
		||||
 | 
			
		||||
    /* Paint the background covering the entire widget area */
 | 
			
		||||
    gtk_render_background (self->view_context,
 | 
			
		||||
                           cr,
 | 
			
		||||
                           0, 0,
 | 
			
		||||
                           self->allocation_width, self->allocation_height);
 | 
			
		||||
                           geometry.allocation_width, geometry.allocation_height);
 | 
			
		||||
 | 
			
		||||
    cairo_save(cr);
 | 
			
		||||
    cairo_translate (cr, self->widget_to_layout.origin_x, self->widget_to_layout.origin_y);
 | 
			
		||||
    cairo_scale (cr, self->widget_to_layout.scale, self->widget_to_layout.scale);
 | 
			
		||||
    cairo_translate (cr, geometry.widget_to_layout.origin_x, geometry.widget_to_layout.origin_y);
 | 
			
		||||
    cairo_scale (cr, geometry.widget_to_layout.scale, geometry.widget_to_layout.scale);
 | 
			
		||||
 | 
			
		||||
    squeek_draw_layout_base_view(keyboard->layout, self, cr);
 | 
			
		||||
    squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);
 | 
			
		||||
@ -261,8 +261,6 @@ static void
 | 
			
		||||
renderer_init (EekRenderer *self)
 | 
			
		||||
{
 | 
			
		||||
    self->pcontext = NULL;
 | 
			
		||||
    self->allocation_width = 0.0;
 | 
			
		||||
    self->allocation_height = 0.0;
 | 
			
		||||
    self->scale_factor = 1;
 | 
			
		||||
 | 
			
		||||
    self->css_provider = squeek_load_style();
 | 
			
		||||
@ -309,22 +307,18 @@ eek_renderer_new (LevelKeyboard  *keyboard,
 | 
			
		||||
    return renderer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
eek_renderer_set_allocation_size (EekRenderer *renderer,
 | 
			
		||||
                                  struct squeek_layout *layout,
 | 
			
		||||
struct render_geometry
 | 
			
		||||
eek_render_geometry_from_allocation_size (struct squeek_layout *layout,
 | 
			
		||||
                                  gdouble      width,
 | 
			
		||||
                                  gdouble      height)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (width > 0.0 && height > 0.0);
 | 
			
		||||
 | 
			
		||||
    renderer->allocation_width = width;
 | 
			
		||||
    renderer->allocation_height = height;
 | 
			
		||||
 | 
			
		||||
    renderer->widget_to_layout = squeek_layout_calculate_transformation(
 | 
			
		||||
                layout,
 | 
			
		||||
                renderer->allocation_width, renderer->allocation_height);
 | 
			
		||||
 | 
			
		||||
    // This is where size-dependent surfaces would be released
 | 
			
		||||
    struct render_geometry ret = {
 | 
			
		||||
        .allocation_width = width,
 | 
			
		||||
        .allocation_height = height,
 | 
			
		||||
        .widget_to_layout = squeek_layout_calculate_transformation(
 | 
			
		||||
            layout, width, height),
 | 
			
		||||
    };
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@ -333,6 +327,11 @@ eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale)
 | 
			
		||||
    renderer->scale_factor = scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Rust interface.
 | 
			
		||||
uint32_t eek_renderer_get_scale_factor(EekRenderer *renderer) {
 | 
			
		||||
    return renderer->scale_factor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cairo_surface_t *
 | 
			
		||||
eek_renderer_get_icon_surface (const gchar *icon_name,
 | 
			
		||||
                               gint size,
 | 
			
		||||
@ -356,8 +355,3 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
 | 
			
		||||
    }
 | 
			
		||||
    return surface;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct transformation
 | 
			
		||||
eek_renderer_get_transformation (EekRenderer *renderer) {
 | 
			
		||||
    return renderer->widget_to_layout;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -41,22 +41,23 @@ typedef struct EekRenderer
 | 
			
		||||
    gchar *extra_style; // owned
 | 
			
		||||
 | 
			
		||||
    // 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
 | 
			
		||||
    gdouble allocation_width;
 | 
			
		||||
    gdouble allocation_height;
 | 
			
		||||
    gint scale_factor; /* the outputs scale factor */
 | 
			
		||||
    /// Coords transformation
 | 
			
		||||
    struct transformation widget_to_layout;
 | 
			
		||||
} EekRenderer;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GType            eek_renderer_get_type         (void) G_GNUC_CONST;
 | 
			
		||||
EekRenderer     *eek_renderer_new              (LevelKeyboard     *keyboard,
 | 
			
		||||
                                                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,
 | 
			
		||||
                                                gint             scale);
 | 
			
		||||
 | 
			
		||||
@ -64,13 +65,14 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar     *icon_name,
 | 
			
		||||
                                                gint             size,
 | 
			
		||||
                                                gint             scale);
 | 
			
		||||
 | 
			
		||||
void             eek_renderer_render_keyboard  (EekRenderer     *renderer, struct submission *submission,
 | 
			
		||||
void             eek_renderer_render_keyboard  (EekRenderer     *renderer, struct render_geometry geometry, struct submission *submission,
 | 
			
		||||
                                                cairo_t         *cr, LevelKeyboard *keyboard);
 | 
			
		||||
void
 | 
			
		||||
eek_renderer_free (EekRenderer        *self);
 | 
			
		||||
 | 
			
		||||
struct transformation
 | 
			
		||||
eek_renderer_get_transformation (EekRenderer *renderer);
 | 
			
		||||
struct render_geometry
 | 
			
		||||
eek_render_geometry_from_allocation_size (struct squeek_layout *layout,
 | 
			
		||||
    gdouble      width, gdouble      height);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif  /* EEK_RENDERER_H */
 | 
			
		||||
 | 
			
		||||
@ -90,13 +90,5 @@ struct transformation {
 | 
			
		||||
    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
 | 
			
		||||
#endif  /* EEK_TYPES_H */
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
project(
 | 
			
		||||
    'squeekboard',
 | 
			
		||||
    'c', 'rust',
 | 
			
		||||
    version: '1.10.0',
 | 
			
		||||
    version: '1.12.0',
 | 
			
		||||
    license: 'GPLv3',
 | 
			
		||||
    meson_version: '>=0.51.0',
 | 
			
		||||
    default_options: [
 | 
			
		||||
 | 
			
		||||
@ -294,8 +294,8 @@
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        equal to the number of commit requests already issued on that object.
 | 
			
		||||
        When the compositor receives a done event with a serial different than
 | 
			
		||||
        equal to the number of done events already issued on that object.
 | 
			
		||||
        When the compositor receives a commit request with a serial different than
 | 
			
		||||
        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
 | 
			
		||||
        object.
 | 
			
		||||
 | 
			
		||||
@ -10,13 +10,14 @@ pub struct KeySym(pub String);
 | 
			
		||||
type View = String;
 | 
			
		||||
 | 
			
		||||
/// Use to send modified keypresses
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
pub enum Modifier {
 | 
			
		||||
    /// Control and Alt are the only modifiers
 | 
			
		||||
    /// which doesn't interfere with levels,
 | 
			
		||||
    /// so it's simple to implement as levels are deprecated in squeekboard.
 | 
			
		||||
    Control,
 | 
			
		||||
    Alt,
 | 
			
		||||
    Mod4,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Action to perform on the keypress and, in reverse, on keyrelease
 | 
			
		||||
@ -29,6 +30,11 @@ pub enum Action {
 | 
			
		||||
        lock: View,
 | 
			
		||||
        /// When unlocked by pressing it or emitting a key
 | 
			
		||||
        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
 | 
			
		||||
    ApplyModifier(Modifier),
 | 
			
		||||
@ -48,14 +54,24 @@ pub enum Action {
 | 
			
		||||
impl Action {
 | 
			
		||||
    pub fn is_locked(&self, view_name: &str) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            Action::LockView { lock, unlock: _ } => lock == view_name,
 | 
			
		||||
            Action::LockView { lock, unlock: _, latches: _, looks_locked_from: _ } => 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,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_active(&self, view_name: &str) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            Action::SetView(view) => view == view_name,
 | 
			
		||||
            Action::LockView { lock, unlock: _ } => lock == view_name,
 | 
			
		||||
            Action::LockView { lock, unlock: _, latches: _, looks_locked_from: _ } => lock == view_name,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/data.rs
									
									
									
									
									
								
							@ -289,7 +289,13 @@ struct ButtonMeta {
 | 
			
		||||
#[serde(deny_unknown_fields)]
 | 
			
		||||
enum Action {
 | 
			
		||||
    #[serde(rename="locking")]
 | 
			
		||||
    Locking { lock_view: String, unlock_view: String },
 | 
			
		||||
    Locking {
 | 
			
		||||
        lock_view: String,
 | 
			
		||||
        unlock_view: String,
 | 
			
		||||
        pops: Option<bool>,
 | 
			
		||||
        #[serde(default)]
 | 
			
		||||
        looks_locked_from: Vec<String>,
 | 
			
		||||
    },
 | 
			
		||||
    #[serde(rename="set_view")]
 | 
			
		||||
    SetView(String),
 | 
			
		||||
    #[serde(rename="show_prefs")]
 | 
			
		||||
@ -600,7 +606,9 @@ fn create_action<H: logging::Handler>(
 | 
			
		||||
            )
 | 
			
		||||
        ),
 | 
			
		||||
        SubmitData::Action(Action::Locking {
 | 
			
		||||
            lock_view, unlock_view
 | 
			
		||||
            lock_view, unlock_view,
 | 
			
		||||
            pops,
 | 
			
		||||
            looks_locked_from,
 | 
			
		||||
        }) => ::action::Action::LockView {
 | 
			
		||||
            lock: filter_view_name(
 | 
			
		||||
                name,
 | 
			
		||||
@ -614,6 +622,8 @@ fn create_action<H: logging::Handler>(
 | 
			
		||||
                &view_names,
 | 
			
		||||
                warning_handler,
 | 
			
		||||
            ),
 | 
			
		||||
            latches: pops.unwrap_or(true),
 | 
			
		||||
            looks_locked_from,
 | 
			
		||||
        },
 | 
			
		||||
        SubmitData::Action(
 | 
			
		||||
            Action::ShowPrefs
 | 
			
		||||
@ -658,6 +668,9 @@ fn create_action<H: logging::Handler>(
 | 
			
		||||
            Modifier::Alt => action::Action::ApplyModifier(
 | 
			
		||||
                action::Modifier::Alt,
 | 
			
		||||
            ),
 | 
			
		||||
            Modifier::Mod4 => action::Action::ApplyModifier(
 | 
			
		||||
                action::Modifier::Mod4,
 | 
			
		||||
            ),
 | 
			
		||||
            unsupported_modifier => {
 | 
			
		||||
                warning_handler.handle(
 | 
			
		||||
                    logging::Level::Bug,
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@ handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
 | 
			
		||||
 | 
			
		||||
    if (service->context) {
 | 
			
		||||
        if (arg_visible) {
 | 
			
		||||
            server_context_service_show_keyboard (service->context);
 | 
			
		||||
            server_context_service_force_show_keyboard (service->context);
 | 
			
		||||
        } else {
 | 
			
		||||
            server_context_service_hide_keyboard (service->context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										213
									
								
								src/drawing.rs
									
									
									
									
									
								
							
							
						
						
									
										213
									
								
								src/drawing.rs
									
									
									
									
									
								
							@ -3,20 +3,24 @@
 | 
			
		||||
use cairo;
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
 | 
			
		||||
use ::action::Action;
 | 
			
		||||
use ::action::{ Action, Modifier };
 | 
			
		||||
use ::keyboard;
 | 
			
		||||
use ::layout::{ Button, Layout };
 | 
			
		||||
use ::layout::c::{ EekGtkKeyboard, Point };
 | 
			
		||||
use ::layout::{ Button, Label, LatchedState, Layout };
 | 
			
		||||
use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
 | 
			
		||||
use ::submission::Submission;
 | 
			
		||||
 | 
			
		||||
use glib::translate::FromGlibPtrNone;
 | 
			
		||||
use gtk::WidgetExt;
 | 
			
		||||
 | 
			
		||||
use std::collections::HashSet;
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::ptr;
 | 
			
		||||
 | 
			
		||||
mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    use cairo_sys;
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    
 | 
			
		||||
    // This is constructed only in C, no need for warnings
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
@ -24,18 +28,44 @@ mod c {
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct EekRenderer(*const c_void);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    // This is constructed only in C, no need for warnings
 | 
			
		||||
    /// Just don't clone this for no reason.
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct GtkStyleContext(*const c_void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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)]
 | 
			
		||||
        pub fn eek_render_button(
 | 
			
		||||
        pub fn eek_renderer_get_scale_factor(
 | 
			
		||||
            renderer: EekRenderer,
 | 
			
		||||
        ) -> u32;
 | 
			
		||||
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_render_button_in_context(
 | 
			
		||||
            scale_factor: u32,
 | 
			
		||||
            cr: *mut cairo_sys::cairo_t,
 | 
			
		||||
            button: *const Button,
 | 
			
		||||
            ctx: GtkStyleContext,
 | 
			
		||||
            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,
 | 
			
		||||
            locked: u64,
 | 
			
		||||
        ) -> GtkStyleContext;
 | 
			
		||||
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_put_style_context_for_button(
 | 
			
		||||
            ctx: GtkStyleContext,
 | 
			
		||||
            outline_name: *const c_char,
 | 
			
		||||
            locked_class: *const c_char,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -55,13 +85,16 @@ mod c {
 | 
			
		||||
 | 
			
		||||
        layout.foreach_visible_button(|offset, button| {
 | 
			
		||||
            let state = RefCell::borrow(&button.state).clone();
 | 
			
		||||
            let active_mod = match &state.action {
 | 
			
		||||
                Action::ApplyModifier(m) => active_modifiers.contains(m),
 | 
			
		||||
                _ => false,
 | 
			
		||||
            };
 | 
			
		||||
            let locked = state.action.is_active(&layout.current_view)
 | 
			
		||||
                | active_mod;
 | 
			
		||||
            if state.pressed == keyboard::PressType::Pressed || locked {
 | 
			
		||||
 | 
			
		||||
            let locked = LockedStyle::from_action(
 | 
			
		||||
                &state.action,
 | 
			
		||||
                &active_modifiers,
 | 
			
		||||
                layout.get_view_latched(),
 | 
			
		||||
                &layout.current_view,
 | 
			
		||||
            );
 | 
			
		||||
            if state.pressed == keyboard::PressType::Pressed
 | 
			
		||||
                || locked != LockedStyle::Free
 | 
			
		||||
            {
 | 
			
		||||
                render_button_at_position(
 | 
			
		||||
                    renderer, &cr,
 | 
			
		||||
                    offset,
 | 
			
		||||
@ -87,20 +120,55 @@ mod c {
 | 
			
		||||
                renderer, &cr,
 | 
			
		||||
                offset,
 | 
			
		||||
                button.as_ref(),
 | 
			
		||||
                keyboard::PressType::Released, false,
 | 
			
		||||
                keyboard::PressType::Released,
 | 
			
		||||
                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)
 | 
			
		||||
pub fn render_button_at_position(
 | 
			
		||||
fn render_button_at_position(
 | 
			
		||||
    renderer: c::EekRenderer,
 | 
			
		||||
    cr: &cairo::Context,
 | 
			
		||||
    position: Point,
 | 
			
		||||
    button: &Button,
 | 
			
		||||
    pressed: keyboard::PressType,
 | 
			
		||||
    locked: bool,
 | 
			
		||||
    locked: LockedStyle,
 | 
			
		||||
) {
 | 
			
		||||
    cr.save();
 | 
			
		||||
    cr.translate(position.x, position.y);
 | 
			
		||||
@ -109,19 +177,110 @@ pub fn render_button_at_position(
 | 
			
		||||
        button.size.width, button.size.height
 | 
			
		||||
    );
 | 
			
		||||
    cr.clip();
 | 
			
		||||
    unsafe {
 | 
			
		||||
        c::eek_render_button(
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
            cairo::Context::to_raw_none(&cr),
 | 
			
		||||
            button as *const Button,
 | 
			
		||||
            button.name.as_ptr(),
 | 
			
		||||
            outline_name_c,
 | 
			
		||||
            locked_class_c,
 | 
			
		||||
            pressed as u64,
 | 
			
		||||
            locked as u64,
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
    cr.restore();
 | 
			
		||||
    
 | 
			
		||||
    let r = operation(&ctx);
 | 
			
		||||
 | 
			
		||||
    unsafe {
 | 
			
		||||
        c::eek_put_style_context_for_button(
 | 
			
		||||
            ctx,
 | 
			
		||||
            outline_name_c,
 | 
			
		||||
            locked_class_c,
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn queue_redraw(keyboard: EekGtkKeyboard) {
 | 
			
		||||
    let widget = unsafe { gtk::Widget::from_glib_none(keyboard.0) };
 | 
			
		||||
    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)]
 | 
			
		||||
    pub struct InputMethod(*const c_void);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        fn imservice_destroy_im(im: *mut c::InputMethod);
 | 
			
		||||
 | 
			
		||||
        #[allow(improper_ctypes)] // IMService will never be dereferenced in C
 | 
			
		||||
        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);
 | 
			
		||||
@ -149,21 +149,18 @@ pub mod c {
 | 
			
		||||
            ..IMProtocolState::default()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        imservice.serial += Wrapping(1u32);
 | 
			
		||||
 | 
			
		||||
        if active_changed {
 | 
			
		||||
            (imservice.active_callback)(imservice.current.active);
 | 
			
		||||
            let (hint, purpose) = if imservice.current.active {(
 | 
			
		||||
                imservice.current.content_hint,
 | 
			
		||||
                imservice.current.content_purpose.clone(),
 | 
			
		||||
            )} else {(
 | 
			
		||||
                ContentHint::NONE,
 | 
			
		||||
                ContentPurpose::Normal,
 | 
			
		||||
            )};
 | 
			
		||||
            unsafe {
 | 
			
		||||
                eekboard_context_service_set_hint_purpose(
 | 
			
		||||
                    imservice.state_manager,
 | 
			
		||||
                    hint.bits(),
 | 
			
		||||
                    purpose as u32,
 | 
			
		||||
                );
 | 
			
		||||
            if imservice.current.active {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    eekboard_context_service_set_hint_purpose(
 | 
			
		||||
                        imservice.state_manager,
 | 
			
		||||
                        imservice.current.content_hint.bits(),
 | 
			
		||||
                        imservice.current.content_purpose.clone() as u32,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -409,7 +406,6 @@ impl IMService {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    c::eek_input_method_commit(self.im, self.serial.0)
 | 
			
		||||
                }
 | 
			
		||||
                self.serial += Wrapping(1u32);
 | 
			
		||||
                Ok(())
 | 
			
		||||
            },
 | 
			
		||||
            false => Err(SubmitError::NotActive),
 | 
			
		||||
 | 
			
		||||
@ -26,13 +26,6 @@ struct squeek_layout_state {
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
        const struct squeek_layout *layout,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										541
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										541
									
								
								src/layout.rs
									
									
									
									
									
								
							@ -41,9 +41,7 @@ pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    use gtk_sys;
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
 | 
			
		||||
    use std::ops::{ Add, Sub };
 | 
			
		||||
 | 
			
		||||
@ -52,7 +50,6 @@ pub mod c {
 | 
			
		||||
    #[derive(Copy, Clone)]
 | 
			
		||||
    pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_gtk_keyboard_emit_feedback(
 | 
			
		||||
@ -162,64 +159,6 @@ pub mod c {
 | 
			
		||||
    pub struct LevelKeyboard(*const c_void);
 | 
			
		||||
 | 
			
		||||
    // 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.
 | 
			
		||||
    /// The origin of the transformation is the point inside the margins.
 | 
			
		||||
@ -485,6 +424,15 @@ pub struct Button {
 | 
			
		||||
    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
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Row {
 | 
			
		||||
@ -552,6 +500,7 @@ pub struct Spacing {
 | 
			
		||||
    pub button: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct View {
 | 
			
		||||
    /// Rows together with their offsets from the top left
 | 
			
		||||
    rows: Vec<(c::Point, Row)>,
 | 
			
		||||
@ -665,6 +614,19 @@ pub struct Margins {
 | 
			
		||||
    pub right: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum LatchedState {
 | 
			
		||||
    /// Holds view to return to.
 | 
			
		||||
    FromView(String),
 | 
			
		||||
    Not,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LatchedState {
 | 
			
		||||
    pub fn is_latched(&self) -> bool {
 | 
			
		||||
        self != &LatchedState::Not
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: split into sth like
 | 
			
		||||
// Arrangement (views) + details (keymap) + State (keys)
 | 
			
		||||
/// State of the UI, contains the backend as well
 | 
			
		||||
@ -672,6 +634,12 @@ pub struct Layout {
 | 
			
		||||
    pub margins: Margins,
 | 
			
		||||
    pub kind: ArrangementKind,
 | 
			
		||||
    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
 | 
			
		||||
    // Maybe they should own UI only,
 | 
			
		||||
    // and keys should be owned by a dedicated non-UI-State?
 | 
			
		||||
@ -718,6 +686,7 @@ impl Layout {
 | 
			
		||||
        Layout {
 | 
			
		||||
            kind,
 | 
			
		||||
            current_view: "base".to_owned(),
 | 
			
		||||
            view_latched: LatchedState::Not,
 | 
			
		||||
            views: data.views,
 | 
			
		||||
            keymaps: data.keymaps,
 | 
			
		||||
            pressed_keys: HashSet::new(),
 | 
			
		||||
@ -743,6 +712,12 @@ 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
 | 
			
		||||
    fn calculate_inner_size(&self) -> Size {
 | 
			
		||||
        View::calculate_super_size(
 | 
			
		||||
@ -818,8 +793,117 @@ impl Layout {
 | 
			
		||||
        }
 | 
			
		||||
        out
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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 {
 | 
			
		||||
            ViewTransition::UnlatchAll => self.unstick_locks(),
 | 
			
		||||
            ViewTransition::ChangeTo(view) => try_set_view(self, view.into()),
 | 
			
		||||
            ViewTransition::NoChange => {},
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.view_latched = new_latched;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Unlatch all latched keys,
 | 
			
		||||
    /// so that the new view is the one before first press.
 | 
			
		||||
    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,
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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 {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
@ -898,67 +982,6 @@ pub struct UIBackend {
 | 
			
		||||
mod seat {
 | 
			
		||||
    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(
 | 
			
		||||
        layout: &mut Layout,
 | 
			
		||||
        submission: &mut Submission,
 | 
			
		||||
@ -1018,37 +1041,22 @@ mod seat {
 | 
			
		||||
        };
 | 
			
		||||
        let action = key.action.clone();
 | 
			
		||||
 | 
			
		||||
        layout.apply_view_transition(&action);
 | 
			
		||||
 | 
			
		||||
        // update
 | 
			
		||||
        let key = key.into_released();
 | 
			
		||||
 | 
			
		||||
        // process changes
 | 
			
		||||
        // process non-view switching
 | 
			
		||||
        match action {
 | 
			
		||||
            Action::Submit { text: _, keys: _ }
 | 
			
		||||
                | Action::Erase
 | 
			
		||||
            => {
 | 
			
		||||
                unstick_locks(layout).apply();
 | 
			
		||||
                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) => {
 | 
			
		||||
                // FIXME: key id is unneeded with stateless locks
 | 
			
		||||
                let key_id = KeyState::get_id(rckey);
 | 
			
		||||
                let gets_locked = !submission.is_modifier_active(modifier.clone());
 | 
			
		||||
                let gets_locked = !submission.is_modifier_active(modifier);
 | 
			
		||||
                match gets_locked {
 | 
			
		||||
                    true => submission.handle_add_modifier(
 | 
			
		||||
                        key_id,
 | 
			
		||||
@ -1083,6 +1091,8 @@ mod seat {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            // Other keys are handled in view switcher before.
 | 
			
		||||
            _ => {}
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let pointer = ::util::Pointer(rckey.clone());
 | 
			
		||||
@ -1100,14 +1110,20 @@ mod test {
 | 
			
		||||
    use std::ffi::CString;
 | 
			
		||||
    use ::keyboard::PressType;
 | 
			
		||||
 | 
			
		||||
    pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
			
		||||
    pub fn make_state_with_action(action: Action)
 | 
			
		||||
        -> Rc<RefCell<::keyboard::KeyState>>
 | 
			
		||||
    {
 | 
			
		||||
        Rc::new(RefCell::new(::keyboard::KeyState {
 | 
			
		||||
            pressed: PressType::Released,
 | 
			
		||||
            keycodes: Vec::new(),
 | 
			
		||||
            action: Action::SetView("default".into()),
 | 
			
		||||
            action,
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
			
		||||
        make_state_with_action(Action::SetView("default".into()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn make_button_with_state(
 | 
			
		||||
        name: String,
 | 
			
		||||
        state: Rc<RefCell<::keyboard::KeyState>>,
 | 
			
		||||
@ -1121,6 +1137,242 @@ 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]
 | 
			
		||||
    fn check_centering() {
 | 
			
		||||
        //    A B
 | 
			
		||||
@ -1193,6 +1445,7 @@ mod test {
 | 
			
		||||
        ]);
 | 
			
		||||
        let layout = Layout {
 | 
			
		||||
            current_view: String::new(),
 | 
			
		||||
            view_latched: LatchedState::Not,
 | 
			
		||||
            keymaps: Vec::new(),
 | 
			
		||||
            kind: ArrangementKind::Base,
 | 
			
		||||
            pressed_keys: HashSet::new(),
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ mod c {
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct GnomeXkbInfo(*const c_void);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        // from libc
 | 
			
		||||
        pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ pub mod c {
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct Manager(*const c_void);
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn eekboard_context_service_set_overlay(
 | 
			
		||||
            manager: Manager,
 | 
			
		||||
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
#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,7 +29,6 @@ use ::logging::Warn;
 | 
			
		||||
mod c {
 | 
			
		||||
    use std::os::raw::c_char;
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn popover_open_settings_panel(panel: *const c_char);
 | 
			
		||||
    }
 | 
			
		||||
@ -437,7 +436,8 @@ pub fn show(
 | 
			
		||||
 | 
			
		||||
        let settings_action = gio::SimpleAction::new("settings", None);
 | 
			
		||||
        settings_action.connect_activate(move |_, _| {
 | 
			
		||||
            unsafe { c::popover_open_settings_panel(CString::new("region").unwrap().as_ptr()) };
 | 
			
		||||
            let s = CString::new("region").unwrap();
 | 
			
		||||
            unsafe { c::popover_open_settings_panel(s.as_ptr()) };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let action_group = gio::SimpleActionGroup::new();
 | 
			
		||||
 | 
			
		||||
@ -10,41 +10,78 @@ use std::iter::FromIterator;
 | 
			
		||||
// TODO: keep a list of what is a language layout,
 | 
			
		||||
// and what a convenience layout. "_wide" is not a layout,
 | 
			
		||||
// neither is "number"
 | 
			
		||||
/// List of builtin layouts
 | 
			
		||||
const KEYBOARDS: &[(*const str, *const str)] = &[
 | 
			
		||||
    // layouts: us must be left as first, as it is the,
 | 
			
		||||
    // fallback layout. The others should be alphabetical.
 | 
			
		||||
    // fallback layout.
 | 
			
		||||
    ("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")),
 | 
			
		||||
    ("br", include_str!("../data/keyboards/br.yaml")),
 | 
			
		||||
    ("de", include_str!("../data/keyboards/de.yaml")),
 | 
			
		||||
 | 
			
		||||
    // Language layouts: keep alphabetical.
 | 
			
		||||
    ("be", include_str!("../data/keyboards/be.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")),
 | 
			
		||||
 | 
			
		||||
    ("cz", include_str!("../data/keyboards/cz.yaml")),
 | 
			
		||||
    ("cz_wide", include_str!("../data/keyboards/cz_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("cz+qwerty", include_str!("../data/keyboards/cz+qwerty.yaml")),
 | 
			
		||||
    ("cz+qwerty_wide", include_str!("../data/keyboards/cz+qwerty_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("dk", include_str!("../data/keyboards/dk.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("epo", include_str!("../data/keyboards/epo.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("es", include_str!("../data/keyboards/es.yaml")),
 | 
			
		||||
    ("es+cat", include_str!("../data/keyboards/es+cat.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("fi", include_str!("../data/keyboards/fi.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("fr", include_str!("../data/keyboards/fr.yaml")),
 | 
			
		||||
    ("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
 | 
			
		||||
    ("it+fur", include_str!("../data/keyboards/it+fur.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")),
 | 
			
		||||
 | 
			
		||||
    ("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
 | 
			
		||||
    ("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("no", include_str!("../data/keyboards/no.yaml")),
 | 
			
		||||
    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("pl", include_str!("../data/keyboards/pl.yaml")),
 | 
			
		||||
    ("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("ru", include_str!("../data/keyboards/ru.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("se", include_str!("../data/keyboards/se.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("th", include_str!("../data/keyboards/th.yaml")),
 | 
			
		||||
    ("th_wide", include_str!("../data/keyboards/th_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("ua", include_str!("../data/keyboards/ua.yaml")),
 | 
			
		||||
    ("bg", include_str!("../data/keyboards/bg.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("us+colemak", include_str!("../data/keyboards/us+colemak.yaml")),
 | 
			
		||||
    ("us+colemak_wide", include_str!("../data/keyboards/us+colemak_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("us+dvorak", include_str!("../data/keyboards/us+dvorak.yaml")),
 | 
			
		||||
    ("us+dvorak_wide", include_str!("../data/keyboards/us+dvorak_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    // Others
 | 
			
		||||
    ("number", include_str!("../data/keyboards/number.yaml")),
 | 
			
		||||
 | 
			
		||||
    // layout+overlay
 | 
			
		||||
    ("terminal", include_str!("../data/keyboards/terminal.yaml")),
 | 
			
		||||
    ("terminal_wide", include_str!("../data/keyboards/terminal_wide.yaml")),
 | 
			
		||||
@ -85,6 +122,7 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
 | 
			
		||||
    ("en-US", include_str!("../data/langs/en-US.txt")),
 | 
			
		||||
    ("es-ES", include_str!("../data/langs/es-ES.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")),
 | 
			
		||||
    ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
 | 
			
		||||
    ("ru-RU", include_str!("../data/langs/ru-RU.txt")),
 | 
			
		||||
 | 
			
		||||
@ -239,6 +239,13 @@ server_context_service_real_show_keyboard (ServerContextService *self)
 | 
			
		||||
    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
 | 
			
		||||
server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
@ -246,6 +253,13 @@ server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
			
		||||
    self->visible = FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
hide_keyboard_source_func(ServerContextService *context)
 | 
			
		||||
{
 | 
			
		||||
    server_context_service_real_hide_keyboard(context);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
on_hide (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
@ -255,7 +269,7 @@ on_hide (ServerContextService *self)
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
static void
 | 
			
		||||
server_context_service_show_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
			
		||||
@ -266,17 +280,30 @@ server_context_service_show_keyboard (ServerContextService *self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!self->visible) {
 | 
			
		||||
        server_context_service_real_show_keyboard (self);
 | 
			
		||||
        g_idle_add((GSourceFunc)show_keyboard_source_func, 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
 | 
			
		||||
server_context_service_hide_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
 | 
			
		||||
 | 
			
		||||
    if (self->visible) {
 | 
			
		||||
        server_context_service_real_hide_keyboard (self);
 | 
			
		||||
        g_idle_add((GSourceFunc)hide_keyboard_source_func, 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);
 | 
			
		||||
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
 | 
			
		||||
void server_context_service_show_keyboard (ServerContextService *self);
 | 
			
		||||
void server_context_service_force_show_keyboard (ServerContextService *self);
 | 
			
		||||
void server_context_service_hide_keyboard (ServerContextService *self);
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif  /* SERVER_CONTEXT_SERVICE_H */
 | 
			
		||||
 | 
			
		||||
@ -50,9 +50,16 @@ struct squeekboard {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GMainLoop *loop;
 | 
			
		||||
static gboolean opt_system = FALSE;
 | 
			
		||||
static gchar *opt_address = NULL;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
quit (void)
 | 
			
		||||
{
 | 
			
		||||
  g_main_loop_quit (loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// D-Bus
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -131,6 +138,67 @@ static const struct wl_registry_listener registry_listener = {
 | 
			
		||||
#define SESSION_NAME "sm.puri.OSK0"
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
session_register(void) {
 | 
			
		||||
@ -151,7 +219,8 @@ session_register(void) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_dbus_proxy_call_sync(_proxy, "RegisterClient",
 | 
			
		||||
    g_autoptr (GVariant) res = NULL;
 | 
			
		||||
    res = g_dbus_proxy_call_sync(_proxy, "RegisterClient",
 | 
			
		||||
        g_variant_new("(ss)", SESSION_NAME, autostart_id),
 | 
			
		||||
        G_DBUS_CALL_FLAGS_NONE, 1000, NULL, &error);
 | 
			
		||||
    if (error) {
 | 
			
		||||
@ -160,6 +229,22 @@ session_register(void) {
 | 
			
		||||
        g_clear_error(&error);
 | 
			
		||||
        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
 | 
			
		||||
@ -307,8 +392,7 @@ main (int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
    session_register();
 | 
			
		||||
 | 
			
		||||
    GMainLoop *loop = g_main_loop_new (NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    loop = g_main_loop_new (NULL, FALSE);
 | 
			
		||||
    g_main_loop_run (loop);
 | 
			
		||||
 | 
			
		||||
    if (connection) {
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
 | 
			
		||||
 | 
			
		||||
// 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);
 | 
			
		||||
uint8_t submission_hint_available(struct submission *self);
 | 
			
		||||
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
 | 
			
		||||
void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -93,6 +93,18 @@ pub mod c {
 | 
			
		||||
        let layout = unsafe { &*layout };
 | 
			
		||||
        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)]
 | 
			
		||||
@ -258,6 +270,7 @@ impl Submission {
 | 
			
		||||
            .map(|(_id, m)| match m {
 | 
			
		||||
                Modifier::Control => Modifiers::CONTROL,
 | 
			
		||||
                Modifier::Alt => Modifiers::MOD1,
 | 
			
		||||
                Modifier::Mod4 => Modifiers::MOD4,
 | 
			
		||||
            })
 | 
			
		||||
            .fold(Modifiers::empty(), |m, n| m | n);
 | 
			
		||||
        self.virtual_keyboard.set_modifiers_state(raw_modifiers);
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,6 @@ pub mod c {
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    pub struct UIManager(*const c_void);
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn server_context_service_update_visible(imservice: *const UIManager, active: u32);
 | 
			
		||||
        pub fn server_context_service_release_visibility(imservice: *const UIManager);
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,6 @@ pub mod c {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        // From libc, to let KeyMap get deallocated.
 | 
			
		||||
        fn close(fd: u32);
 | 
			
		||||
 | 
			
		||||
@ -46,34 +46,49 @@ endforeach
 | 
			
		||||
 | 
			
		||||
# The layout test is in the examples directory
 | 
			
		||||
# 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 : [
 | 
			
		||||
    'us', 'us+colemak', 'us_wide',
 | 
			
		||||
    'br',
 | 
			
		||||
    # This is the fallback layout,
 | 
			
		||||
    # so stays first to make sure it never goes missing.
 | 
			
		||||
    'us', 'us_wide',
 | 
			
		||||
 | 
			
		||||
    # Block: Languages
 | 
			
		||||
    'be', 'be_wide',
 | 
			
		||||
    'bg',
 | 
			
		||||
    'br',
 | 
			
		||||
    'cz', 'cz_wide',
 | 
			
		||||
    'cz+qwerty', 'cz+qwerty_wide',
 | 
			
		||||
    'de', 'de_wide',
 | 
			
		||||
    'dk',
 | 
			
		||||
    'epo',
 | 
			
		||||
    'es',
 | 
			
		||||
    'es+cat',
 | 
			
		||||
    'fi',
 | 
			
		||||
    'fr', 'fr_wide',
 | 
			
		||||
    'it+fur',
 | 
			
		||||
    'gr',
 | 
			
		||||
    'il',
 | 
			
		||||
    'ir',
 | 
			
		||||
    'it',
 | 
			
		||||
    'it+fur',
 | 
			
		||||
    'jp+kana','jp+kana_wide',
 | 
			
		||||
    'no',
 | 
			
		||||
    'number',
 | 
			
		||||
    'pl', 'pl_wide',
 | 
			
		||||
    'ru',
 | 
			
		||||
    'se',
 | 
			
		||||
    'th', 'th_wide',
 | 
			
		||||
    'ua',
 | 
			
		||||
    'th',
 | 
			
		||||
    'terminal', 'terminal_wide',
 | 
			
		||||
    'us+colemak', 'us+colemak_wide',
 | 
			
		||||
    'us+dvorak', 'us+dvorak_wide',
 | 
			
		||||
 | 
			
		||||
    # Block: Not languages.
 | 
			
		||||
    'emoji',
 | 
			
		||||
    'number',
 | 
			
		||||
    'terminal', 'terminal_wide',
 | 
			
		||||
]
 | 
			
		||||
    extra = []
 | 
			
		||||
    if layout == 'emoji'
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user