First implementation of x11 source
This commit is contained in:
		
							parent
							
								
									e77e013ae7
								
							
						
					
					
						commit
						a450ee18fa
					
				
							
								
								
									
										184
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										184
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -4,317 +4,315 @@
 | 
			
		|||
name = "anyhow"
 | 
			
		||||
version = "1.0.38"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "autocfg"
 | 
			
		||||
version = "1.0.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cc"
 | 
			
		||||
version = "1.0.66"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cfg-if"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "chrono"
 | 
			
		||||
version = "0.4.19"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc 0.2.84 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "time 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "num-integer",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
 "time",
 | 
			
		||||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "enum-as-inner"
 | 
			
		||||
version = "0.3.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "heck 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "quote 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "syn 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "heck",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "espanso"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "espanso-detect 0.1.0",
 | 
			
		||||
 "espanso-ui 0.1.0",
 | 
			
		||||
 "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "simplelog 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "espanso-detect",
 | 
			
		||||
 "espanso-ui",
 | 
			
		||||
 "maplit",
 | 
			
		||||
 "simplelog",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "espanso-detect"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cc 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "enum-as-inner 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "lazycell 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "widestring 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "cc",
 | 
			
		||||
 "enum-as-inner",
 | 
			
		||||
 "lazycell",
 | 
			
		||||
 "log",
 | 
			
		||||
 "widestring",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "espanso-ui"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anyhow 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "cc 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "lazycell 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "serde 1.0.123 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "serde_json 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "thiserror 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "widestring 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "cc",
 | 
			
		||||
 "lazycell",
 | 
			
		||||
 "log",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "thiserror",
 | 
			
		||||
 "widestring",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "heck"
 | 
			
		||||
version = "0.3.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "unicode-segmentation 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "unicode-segmentation",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "itoa"
 | 
			
		||||
version = "0.4.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "lazycell"
 | 
			
		||||
version = "1.3.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libc"
 | 
			
		||||
version = "0.2.84"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "log"
 | 
			
		||||
version = "0.4.14"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "maplit"
 | 
			
		||||
version = "1.0.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-integer"
 | 
			
		||||
version = "0.1.44"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "autocfg",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-traits"
 | 
			
		||||
version = "0.2.14"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "autocfg",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "proc-macro2"
 | 
			
		||||
version = "1.0.24"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "unicode-xid",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "quote"
 | 
			
		||||
version = "1.0.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ryu"
 | 
			
		||||
version = "1.0.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde"
 | 
			
		||||
version = "1.0.123"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "serde_derive 1.0.123 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "serde_derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_derive"
 | 
			
		||||
version = "1.0.123"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "quote 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "syn 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_json"
 | 
			
		||||
version = "1.0.61"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "itoa 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "serde 1.0.123 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "itoa",
 | 
			
		||||
 "ryu",
 | 
			
		||||
 "serde",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "simplelog"
 | 
			
		||||
version = "0.9.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "termcolor 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "log",
 | 
			
		||||
 "termcolor",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "syn"
 | 
			
		||||
version = "1.0.60"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "quote 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "unicode-xid",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "termcolor"
 | 
			
		||||
version = "1.1.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "winapi-util",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror"
 | 
			
		||||
version = "1.0.23"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "thiserror-impl 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "thiserror-impl",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror-impl"
 | 
			
		||||
version = "1.0.23"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "quote 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "syn 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "time"
 | 
			
		||||
version = "0.1.44"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc 0.2.84 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "wasi 0.10.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "wasi",
 | 
			
		||||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicode-segmentation"
 | 
			
		||||
version = "1.7.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicode-xid"
 | 
			
		||||
version = "0.2.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wasi"
 | 
			
		||||
version = "0.10.0+wasi-snapshot-preview1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "widestring"
 | 
			
		||||
version = "0.4.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi"
 | 
			
		||||
version = "0.3.9"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "winapi-i686-pc-windows-gnu",
 | 
			
		||||
 "winapi-x86_64-pc-windows-gnu",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi-i686-pc-windows-gnu"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi-util"
 | 
			
		||||
version = "0.1.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi-x86_64-pc-windows-gnu"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
 | 
			
		||||
[metadata]
 | 
			
		||||
"checksum anyhow 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
 | 
			
		||||
"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 | 
			
		||||
"checksum cc 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
 | 
			
		||||
"checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
"checksum chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
 | 
			
		||||
"checksum enum-as-inner 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
 | 
			
		||||
"checksum heck 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
 | 
			
		||||
"checksum itoa 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
 | 
			
		||||
"checksum lazycell 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 | 
			
		||||
"checksum libc 0.2.84 (registry+https://github.com/rust-lang/crates.io-index)" = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff"
 | 
			
		||||
"checksum log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 | 
			
		||||
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 | 
			
		||||
"checksum num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
 | 
			
		||||
"checksum num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
 | 
			
		||||
"checksum proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
 | 
			
		||||
"checksum quote 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
 | 
			
		||||
"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 | 
			
		||||
"checksum serde 1.0.123 (registry+https://github.com/rust-lang/crates.io-index)" = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
 | 
			
		||||
"checksum serde_derive 1.0.123 (registry+https://github.com/rust-lang/crates.io-index)" = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
 | 
			
		||||
"checksum serde_json 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)" = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
 | 
			
		||||
"checksum simplelog 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720"
 | 
			
		||||
"checksum syn 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)" = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
 | 
			
		||||
"checksum termcolor 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 | 
			
		||||
"checksum thiserror 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
 | 
			
		||||
"checksum thiserror-impl 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
 | 
			
		||||
"checksum time 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
 | 
			
		||||
"checksum unicode-segmentation 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
 | 
			
		||||
"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 | 
			
		||||
"checksum wasi 0.10.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
 | 
			
		||||
"checksum widestring 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
 | 
			
		||||
"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 | 
			
		||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 | 
			
		||||
"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
 | 
			
		||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 | 
			
		||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,10 +7,10 @@ build="build.rs"
 | 
			
		|||
 | 
			
		||||
[dependencies]
 | 
			
		||||
log = "0.4.14"
 | 
			
		||||
lazycell = "1.3.0"
 | 
			
		||||
 | 
			
		||||
[target.'cfg(windows)'.dependencies]
 | 
			
		||||
widestring = "0.4.3"
 | 
			
		||||
lazycell = "1.3.0"
 | 
			
		||||
 | 
			
		||||
[build-dependencies]
 | 
			
		||||
cc = "1.0.66"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,11 +35,17 @@ fn cc_config() {
 | 
			
		|||
 | 
			
		||||
#[cfg(target_os = "linux")]
 | 
			
		||||
fn cc_config() {
 | 
			
		||||
  println!("cargo:rerun-if-changed=src/x11/native.cpp");
 | 
			
		||||
  println!("cargo:rerun-if-changed=src/x11/native.h");
 | 
			
		||||
  cc::Build::new()
 | 
			
		||||
    .cpp(true)
 | 
			
		||||
    .include("src/x11/native.h")
 | 
			
		||||
    .file("src/x11/native.cpp")
 | 
			
		||||
    .compile("espansodetect");
 | 
			
		||||
  println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/");
 | 
			
		||||
  println!("cargo:rustc-link-lib=static=linuxbridge");
 | 
			
		||||
  println!("cargo:rustc-link-lib=static=espansodetect");
 | 
			
		||||
  println!("cargo:rustc-link-lib=dylib=X11");
 | 
			
		||||
  println!("cargo:rustc-link-lib=dylib=Xtst");
 | 
			
		||||
  println!("cargo:rustc-link-lib=dylib=xdo");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_os = "macos")]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								espanso-detect/src/evdev/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								espanso-detect/src/evdev/README.md
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
This is a great starting point for wayland support:
 | 
			
		||||
https://github.com/xkbcommon/libxkbcommon/blob/master/tools/interactive-evdev.c
 | 
			
		||||
| 
						 | 
				
			
			@ -21,3 +21,6 @@ pub mod event;
 | 
			
		|||
 | 
			
		||||
#[cfg(target_os = "windows")]
 | 
			
		||||
pub mod win32;
 | 
			
		||||
 | 
			
		||||
#[cfg(target_os = "linux")]
 | 
			
		||||
pub mod x11;
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +91,7 @@ impl Win32Source {
 | 
			
		|||
    let handle = unsafe { detect_initialize(self as *const Win32Source) };
 | 
			
		||||
 | 
			
		||||
    if handle.is_null() {
 | 
			
		||||
      panic!("Unable to initialize Win32EventLoop");
 | 
			
		||||
      panic!("Unable to initialize Win32Source");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self.handle = handle;
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +135,7 @@ impl Drop for Win32Source {
 | 
			
		|||
    let result = unsafe { detect_destroy(self.handle) };
 | 
			
		||||
 | 
			
		||||
    if result != 0 {
 | 
			
		||||
      error!("Win32EventLoop destruction returned non-zero code");
 | 
			
		||||
      error!("Win32Source destruction returned non-zero code");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										359
									
								
								espanso-detect/src/x11/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								espanso-detect/src/x11/mod.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,359 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::{
 | 
			
		||||
  ffi::{c_void, CStr},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use lazycell::LazyCell;
 | 
			
		||||
use log::{error, trace, warn};
 | 
			
		||||
 | 
			
		||||
use crate::event::Status::*;
 | 
			
		||||
use crate::event::Variant::*;
 | 
			
		||||
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
 | 
			
		||||
use crate::event::{Key::*, MouseButton, MouseEvent};
 | 
			
		||||
 | 
			
		||||
const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1;
 | 
			
		||||
const INPUT_EVENT_TYPE_MOUSE: i32 = 2;
 | 
			
		||||
 | 
			
		||||
const INPUT_STATUS_PRESSED: i32 = 1;
 | 
			
		||||
const INPUT_STATUS_RELEASED: i32 = 2;
 | 
			
		||||
 | 
			
		||||
const INPUT_MOUSE_LEFT_BUTTON: i32 = 1;
 | 
			
		||||
const INPUT_MOUSE_RIGHT_BUTTON: i32 = 3;
 | 
			
		||||
const INPUT_MOUSE_MIDDLE_BUTTON: i32 = 2;
 | 
			
		||||
const INPUT_MOUSE_BUTTON_1: i32 = 9;
 | 
			
		||||
const INPUT_MOUSE_BUTTON_2: i32 = 8;
 | 
			
		||||
 | 
			
		||||
// Take a look at the native.h header file for an explanation of the fields
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
pub struct RawInputEvent {
 | 
			
		||||
  pub event_type: i32,
 | 
			
		||||
 | 
			
		||||
  pub buffer: [u8; 24],
 | 
			
		||||
  pub buffer_len: i32,
 | 
			
		||||
 | 
			
		||||
  pub key_sym: i32,
 | 
			
		||||
  pub key_code: i32,
 | 
			
		||||
  pub status: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(improper_ctypes)]
 | 
			
		||||
#[link(name = "espansodetect", kind = "static")]
 | 
			
		||||
extern "C" {
 | 
			
		||||
  pub fn detect_check_x11() -> i32;
 | 
			
		||||
 | 
			
		||||
  pub fn detect_initialize(_self: *const X11Source, error_code: *mut i32) -> *mut c_void;
 | 
			
		||||
 | 
			
		||||
  pub fn detect_eventloop(
 | 
			
		||||
    window: *const c_void,
 | 
			
		||||
    event_callback: extern "C" fn(_self: *mut X11Source, event: RawInputEvent),
 | 
			
		||||
  ) -> i32;
 | 
			
		||||
 | 
			
		||||
  pub fn detect_destroy(window: *const c_void) -> i32;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type X11SourceCallback = Box<dyn Fn(InputEvent)>;
 | 
			
		||||
pub struct X11Source {
 | 
			
		||||
  handle: *mut c_void,
 | 
			
		||||
  callback: LazyCell<X11SourceCallback>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::new_without_default)]
 | 
			
		||||
impl X11Source {
 | 
			
		||||
  pub fn new() -> X11Source {
 | 
			
		||||
    Self {
 | 
			
		||||
      handle: std::ptr::null_mut(),
 | 
			
		||||
      callback: LazyCell::new(),
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn is_compatible() -> bool {
 | 
			
		||||
    unsafe { detect_check_x11() != 0 }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn initialize(&mut self) {
 | 
			
		||||
    let mut error_code = 0;
 | 
			
		||||
    let handle = unsafe { detect_initialize(self as *const X11Source, &mut error_code) };
 | 
			
		||||
 | 
			
		||||
    if handle.is_null() {
 | 
			
		||||
      match error_code {
 | 
			
		||||
        -1 => panic!("Unable to initialize X11Source, cannot open displays"),
 | 
			
		||||
        -2 => panic!("Unable to initialize X11Source, X Record Extension is not installed"),
 | 
			
		||||
        -3 => panic!("Unable to initialize X11Source, X Keyboard Extension is not installed"),
 | 
			
		||||
        -4 => panic!("Unable to initialize X11Source, cannot initialize record range"),
 | 
			
		||||
        -5 => panic!("Unable to initialize X11Source, cannot initialize XRecord context"),
 | 
			
		||||
        -6 => panic!("Unable to initialize X11Source, cannot enable XRecord context"),
 | 
			
		||||
        _ => panic!("Unable to initialize X11Source, unknown error"),
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self.handle = handle;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pub fn eventloop(&self, event_callback: X11SourceCallback) {
 | 
			
		||||
    if self.handle.is_null() {
 | 
			
		||||
      panic!("Attempt to start X11Source eventloop without initialization");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if self.callback.fill(event_callback).is_err() {
 | 
			
		||||
      panic!("Unable to set X11Source event callback");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extern "C" fn callback(_self: *mut X11Source, event: RawInputEvent) {
 | 
			
		||||
      let event: Option<InputEvent> = event.into();
 | 
			
		||||
      if let Some(callback) = unsafe { (*_self).callback.borrow() } {
 | 
			
		||||
        if let Some(event) = event {
 | 
			
		||||
          callback(event)
 | 
			
		||||
        } else {
 | 
			
		||||
          trace!("Unable to convert raw event to input event");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let error_code = unsafe { detect_eventloop(self.handle, callback) };
 | 
			
		||||
 | 
			
		||||
    if error_code <= 0 {
 | 
			
		||||
      panic!("X11Source eventloop returned a negative error code");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for X11Source {
 | 
			
		||||
  fn drop(&mut self) {
 | 
			
		||||
    if self.handle.is_null() {
 | 
			
		||||
      error!("X11Source destruction cannot be performed, handle is null");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let result = unsafe { detect_destroy(self.handle) };
 | 
			
		||||
 | 
			
		||||
    if result != 0 {
 | 
			
		||||
      error!("X11Source destruction returned non-zero code");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<RawInputEvent> for Option<InputEvent> {
 | 
			
		||||
  fn from(raw: RawInputEvent) -> Option<InputEvent> {
 | 
			
		||||
    let status = match raw.status {
 | 
			
		||||
      INPUT_STATUS_RELEASED => Released,
 | 
			
		||||
      INPUT_STATUS_PRESSED => Pressed,
 | 
			
		||||
      _ => Pressed,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    match raw.event_type {
 | 
			
		||||
      // Keyboard events
 | 
			
		||||
      INPUT_EVENT_TYPE_KEYBOARD => {
 | 
			
		||||
        let (key, variant) = key_sym_to_key(raw.key_sym);
 | 
			
		||||
        let value = if raw.buffer_len > 0 {
 | 
			
		||||
          let raw_string_result =
 | 
			
		||||
            CStr::from_bytes_with_nul(&raw.buffer[..((raw.buffer_len + 1) as usize)]);
 | 
			
		||||
          match raw_string_result {
 | 
			
		||||
            Ok(c_string) => {
 | 
			
		||||
              let string_result = c_string.to_str();
 | 
			
		||||
              match string_result {
 | 
			
		||||
                Ok(value) => Some(value.to_string()),
 | 
			
		||||
                Err(err) => {
 | 
			
		||||
                  warn!("char conversion error: {}", err);
 | 
			
		||||
                  None
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
              warn!("Received malformed char: {}", err);
 | 
			
		||||
              None
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          None
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return Some(InputEvent::Keyboard(KeyboardEvent {
 | 
			
		||||
          key,
 | 
			
		||||
          value,
 | 
			
		||||
          status,
 | 
			
		||||
          variant,
 | 
			
		||||
        }));
 | 
			
		||||
      }
 | 
			
		||||
      // Mouse events
 | 
			
		||||
      INPUT_EVENT_TYPE_MOUSE => {
 | 
			
		||||
        let button = raw_to_mouse_button(raw.key_code);
 | 
			
		||||
 | 
			
		||||
        if let Some(button) = button {
 | 
			
		||||
          return Some(InputEvent::Mouse(MouseEvent { button, status }));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      _ => {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    None
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mappings from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
 | 
			
		||||
// TODO: might need to add also the variants
 | 
			
		||||
fn key_sym_to_key(key_sym: i32) -> (Key, Option<Variant>) {
 | 
			
		||||
  match key_sym {
 | 
			
		||||
    // Modifiers
 | 
			
		||||
    0xFFE9 => (Alt, Some(Left)),
 | 
			
		||||
    0xFFEA => (Alt, Some(Right)),
 | 
			
		||||
    0xFFE5 => (CapsLock, None),
 | 
			
		||||
    0xFFE3 => (Control, Some(Left)),
 | 
			
		||||
    0xFFE4 => (Control, Some(Right)),
 | 
			
		||||
    0xFFE7 | 0xFFEB => (Meta, Some(Left)),
 | 
			
		||||
    0xFFE8 | 0xFFEC => (Meta, Some(Right)),
 | 
			
		||||
    0xFF7F => (NumLock, None),
 | 
			
		||||
    0xFFE1 => (Shift, Some(Left)),
 | 
			
		||||
    0xFFE2 => (Shift, Some(Right)),
 | 
			
		||||
 | 
			
		||||
    // Whitespace
 | 
			
		||||
    0xFF0D => (Enter, None),
 | 
			
		||||
    0xFF09 => (Tab, None),
 | 
			
		||||
    0x20 => (Space, None),
 | 
			
		||||
 | 
			
		||||
    // Navigation
 | 
			
		||||
    0xFF54 => (ArrowDown, None),
 | 
			
		||||
    0xFF51 => (ArrowLeft, None),
 | 
			
		||||
    0xFF53 => (ArrowRight, None),
 | 
			
		||||
    0xFF52 => (ArrowUp, None),
 | 
			
		||||
    0xFF57 => (End, None),
 | 
			
		||||
    0xFF50 => (Home, None),
 | 
			
		||||
    0xFF56 => (PageDown, None),
 | 
			
		||||
    0xFF55 => (PageUp, None),
 | 
			
		||||
 | 
			
		||||
    // Editing keys
 | 
			
		||||
    0xFF08 => (Backspace, None),
 | 
			
		||||
 | 
			
		||||
    // Function keys
 | 
			
		||||
    0xFFBE => (F1, None),
 | 
			
		||||
    0xFFBF => (F2, None),
 | 
			
		||||
    0xFFC0 => (F3, None),
 | 
			
		||||
    0xFFC1 => (F4, None),
 | 
			
		||||
    0xFFC2 => (F5, None),
 | 
			
		||||
    0xFFC3 => (F6, None),
 | 
			
		||||
    0xFFC4 => (F7, None),
 | 
			
		||||
    0xFFC5 => (F8, None),
 | 
			
		||||
    0xFFC6 => (F9, None),
 | 
			
		||||
    0xFFC7 => (F10, None),
 | 
			
		||||
    0xFFC8 => (F11, None),
 | 
			
		||||
    0xFFC9 => (F12, None),
 | 
			
		||||
    0xFFCA => (F13, None),
 | 
			
		||||
    0xFFCB => (F14, None),
 | 
			
		||||
    0xFFCC => (F15, None),
 | 
			
		||||
    0xFFCD => (F16, None),
 | 
			
		||||
    0xFFCE => (F17, None),
 | 
			
		||||
    0xFFCF => (F18, None),
 | 
			
		||||
    0xFFD0 => (F19, None),
 | 
			
		||||
    0xFFD1 => (F20, None),
 | 
			
		||||
 | 
			
		||||
    // Other keys, includes the raw code provided by the operating system
 | 
			
		||||
    _ => (Other(key_sym), None),
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn raw_to_mouse_button(raw: i32) -> Option<MouseButton> {
 | 
			
		||||
  match raw {
 | 
			
		||||
    INPUT_MOUSE_LEFT_BUTTON => Some(MouseButton::Left),
 | 
			
		||||
    INPUT_MOUSE_RIGHT_BUTTON => Some(MouseButton::Right),
 | 
			
		||||
    INPUT_MOUSE_MIDDLE_BUTTON => Some(MouseButton::Middle),
 | 
			
		||||
    INPUT_MOUSE_BUTTON_1 => Some(MouseButton::Button1),
 | 
			
		||||
    INPUT_MOUSE_BUTTON_2 => Some(MouseButton::Button2),
 | 
			
		||||
    _ => None,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
  use std::ffi::CString;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
  fn default_raw_input_event() -> RawInputEvent {
 | 
			
		||||
    RawInputEvent {
 | 
			
		||||
      event_type: INPUT_EVENT_TYPE_KEYBOARD,
 | 
			
		||||
      buffer: [0; 24],
 | 
			
		||||
      buffer_len: 0,
 | 
			
		||||
      key_code: 0,
 | 
			
		||||
      key_sym: 0,
 | 
			
		||||
      status: INPUT_STATUS_PRESSED,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn raw_to_input_event_keyboard_works_correctly() {
 | 
			
		||||
    let c_string = CString::new("k".to_string()).unwrap();
 | 
			
		||||
    let mut buffer: [u8; 24] = [0; 24];
 | 
			
		||||
    buffer[..1].copy_from_slice(c_string.as_bytes());
 | 
			
		||||
 | 
			
		||||
    let mut raw = default_raw_input_event();
 | 
			
		||||
    raw.buffer = buffer;
 | 
			
		||||
    raw.buffer_len = 1;
 | 
			
		||||
    raw.status = INPUT_STATUS_RELEASED;
 | 
			
		||||
    raw.key_sym = 0x4B;
 | 
			
		||||
 | 
			
		||||
    let result: Option<InputEvent> = raw.into();
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      result.unwrap(),
 | 
			
		||||
      InputEvent::Keyboard(KeyboardEvent {
 | 
			
		||||
        key: Other(0x4B),
 | 
			
		||||
        status: Released,
 | 
			
		||||
        value: Some("k".to_string()),
 | 
			
		||||
        variant: None,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn raw_to_input_event_mouse_works_correctly() {
 | 
			
		||||
    let mut raw = default_raw_input_event();
 | 
			
		||||
    raw.event_type = INPUT_EVENT_TYPE_MOUSE;
 | 
			
		||||
    raw.status = INPUT_STATUS_RELEASED;
 | 
			
		||||
    raw.key_code = INPUT_MOUSE_RIGHT_BUTTON;
 | 
			
		||||
 | 
			
		||||
    let result: Option<InputEvent> = raw.into();
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      result.unwrap(),
 | 
			
		||||
      InputEvent::Mouse(MouseEvent {
 | 
			
		||||
        status: Released,
 | 
			
		||||
        button: MouseButton::Right,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn raw_to_input_invalid_buffer() {
 | 
			
		||||
    let buffer: [u8; 24] = [123; 24];
 | 
			
		||||
 | 
			
		||||
    let mut raw = default_raw_input_event();
 | 
			
		||||
    raw.buffer = buffer;
 | 
			
		||||
    raw.buffer_len = 5;
 | 
			
		||||
 | 
			
		||||
    let result: Option<InputEvent> = raw.into();
 | 
			
		||||
    assert!(result.unwrap().into_keyboard().unwrap().value.is_none());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn raw_to_input_event_returns_none_when_missing_type() {
 | 
			
		||||
    let mut raw = default_raw_input_event();
 | 
			
		||||
    raw.event_type = 0;
 | 
			
		||||
    let result: Option<InputEvent> = raw.into();
 | 
			
		||||
    assert!(result.is_none());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										345
									
								
								espanso-detect/src/x11/native.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								espanso-detect/src/x11/native.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,345 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
This code uses the X11 Record Extension to receive keyboard
 | 
			
		||||
events. Documentation of this library can be found here:
 | 
			
		||||
https://www.x.org/releases/X11R7.6/doc/libXtst/recordlib.html
 | 
			
		||||
We will refer to this extension as RE from now on.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "native.h"
 | 
			
		||||
 | 
			
		||||
#include <locale.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include <X11/Xlibint.h>
 | 
			
		||||
#include <X11/Xlib.h>
 | 
			
		||||
#include <X11/Xutil.h>
 | 
			
		||||
#include <X11/cursorfont.h>
 | 
			
		||||
#include <X11/keysymdef.h>
 | 
			
		||||
#include <X11/keysym.h>
 | 
			
		||||
#include <X11/extensions/record.h>
 | 
			
		||||
#include <X11/extensions/XTest.h>
 | 
			
		||||
#include <X11/XKBlib.h>
 | 
			
		||||
#include <X11/Xatom.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
This struct is needed to receive events from the RE.
 | 
			
		||||
The funny thing is: it's not defined there, it should though.
 | 
			
		||||
The only place this is mentioned is the libxnee library,
 | 
			
		||||
so check that out if you need a reference.
 | 
			
		||||
*/
 | 
			
		||||
typedef union
 | 
			
		||||
{
 | 
			
		||||
  unsigned char type;
 | 
			
		||||
  xEvent event;
 | 
			
		||||
  xResourceReq req;
 | 
			
		||||
  xGenericReply reply;
 | 
			
		||||
  xError error;
 | 
			
		||||
  xConnSetupPrefix setup;
 | 
			
		||||
} XRecordDatum;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  // Connections to the X server, RE recommends 2 connections:
 | 
			
		||||
  // one for recording control and one for reading the recorded data.
 | 
			
		||||
  Display *data_disp;
 | 
			
		||||
  Display *ctrl_disp;
 | 
			
		||||
  XRecordRange *record_range;
 | 
			
		||||
  XRecordContext x_context;
 | 
			
		||||
 | 
			
		||||
  void *rust_instance;
 | 
			
		||||
  EventCallback event_callback;
 | 
			
		||||
} DetectContext;
 | 
			
		||||
 | 
			
		||||
void detect_event_callback(XPointer, XRecordInterceptData *);
 | 
			
		||||
int detect_error_callback(Display *display, XErrorEvent *error);
 | 
			
		||||
 | 
			
		||||
int32_t detect_check_x11()
 | 
			
		||||
{
 | 
			
		||||
  Display *check_disp = XOpenDisplay(NULL);
 | 
			
		||||
 | 
			
		||||
  if (!check_disp)
 | 
			
		||||
  {
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  XCloseDisplay(check_disp);
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *detect_initialize(void *_rust_instance, int32_t *error_code)
 | 
			
		||||
{
 | 
			
		||||
  setlocale(LC_ALL, "");
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<DetectContext> context;
 | 
			
		||||
  context.reset(new DetectContext());
 | 
			
		||||
  context->rust_instance = _rust_instance;
 | 
			
		||||
 | 
			
		||||
  // Open the connections to the X server.
 | 
			
		||||
  // RE recommends to open 2 connections to the X server:
 | 
			
		||||
  // one for the recording control and one to read the protocol
 | 
			
		||||
  // data.
 | 
			
		||||
  context->ctrl_disp = XOpenDisplay(NULL);
 | 
			
		||||
  context->data_disp = XOpenDisplay(NULL);
 | 
			
		||||
 | 
			
		||||
  if (!context->ctrl_disp || !context->data_disp)
 | 
			
		||||
  { // Display error
 | 
			
		||||
    *error_code = -1;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // We must set the ctrl_disp to sync mode, or, when we the enable
 | 
			
		||||
  // context in data_disp, there will be a fatal X error.
 | 
			
		||||
  XSynchronize(context->ctrl_disp, True);
 | 
			
		||||
 | 
			
		||||
  int dummy;
 | 
			
		||||
 | 
			
		||||
  // Make sure the X RE is installed in this system.
 | 
			
		||||
  if (!XRecordQueryVersion(context->ctrl_disp, &dummy, &dummy))
 | 
			
		||||
  {
 | 
			
		||||
    *error_code = -2;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Make sure the X Keyboard Extension is installed
 | 
			
		||||
  if (!XkbQueryExtension(context->ctrl_disp, &dummy, &dummy, &dummy, &dummy, &dummy))
 | 
			
		||||
  {
 | 
			
		||||
    *error_code = -3;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Initialize the record range, that is the kind of events we want to track.
 | 
			
		||||
  context->record_range = XRecordAllocRange();
 | 
			
		||||
  if (!context->record_range)
 | 
			
		||||
  {
 | 
			
		||||
    *error_code = -4;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
  context->record_range->device_events.first = KeyPress;
 | 
			
		||||
  context->record_range->device_events.last = ButtonRelease;
 | 
			
		||||
 | 
			
		||||
  // We want to get the keys from all clients
 | 
			
		||||
  XRecordClientSpec client_spec;
 | 
			
		||||
  client_spec = XRecordAllClients;
 | 
			
		||||
 | 
			
		||||
  // Initialize the context
 | 
			
		||||
  context->x_context = XRecordCreateContext(context->ctrl_disp, 0, &client_spec, 1, &context->record_range, 1);
 | 
			
		||||
  if (!context->x_context)
 | 
			
		||||
  {
 | 
			
		||||
    *error_code = -5;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!XRecordEnableContextAsync(context->data_disp, context->x_context, detect_event_callback, (XPointer)context.get()))
 | 
			
		||||
  {
 | 
			
		||||
    *error_code = -6;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Setup a custom error handler
 | 
			
		||||
  XSetErrorHandler(&detect_error_callback);
 | 
			
		||||
 | 
			
		||||
  // Note: We might never get a MappingNotify event if the
 | 
			
		||||
  // modifier and keymap information was never cached in Xlib.
 | 
			
		||||
  // The next line makes sure that this happens initially.
 | 
			
		||||
  XKeysymToKeycode(context->ctrl_disp, XK_F1);
 | 
			
		||||
 | 
			
		||||
  // Release the unique_ptr to avoid freeing the context right-away.
 | 
			
		||||
  return context.release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t detect_eventloop(void *_context, EventCallback _callback)
 | 
			
		||||
{
 | 
			
		||||
  DetectContext *context = (DetectContext *)_context;
 | 
			
		||||
  if (!context)
 | 
			
		||||
  {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  context->event_callback = _callback;
 | 
			
		||||
 | 
			
		||||
  bool running = true;
 | 
			
		||||
 | 
			
		||||
  int ctrl_fd = XConnectionNumber(context->ctrl_disp);
 | 
			
		||||
  int data_fd = XConnectionNumber(context->data_disp);
 | 
			
		||||
 | 
			
		||||
  while (running)
 | 
			
		||||
  {
 | 
			
		||||
    fd_set fds;
 | 
			
		||||
    FD_ZERO(&fds);
 | 
			
		||||
    FD_SET(ctrl_fd, &fds);
 | 
			
		||||
    FD_SET(data_fd, &fds);
 | 
			
		||||
    timeval timeout;
 | 
			
		||||
    timeout.tv_sec = 2;
 | 
			
		||||
    timeout.tv_usec = 0;
 | 
			
		||||
    select(max(ctrl_fd, data_fd) + 1,
 | 
			
		||||
                        &fds, NULL, NULL, &timeout);
 | 
			
		||||
 | 
			
		||||
    if (FD_ISSET(data_fd, &fds))
 | 
			
		||||
    {
 | 
			
		||||
      XRecordProcessReplies(context->data_disp);
 | 
			
		||||
    }
 | 
			
		||||
    if (FD_ISSET(ctrl_fd, &fds))
 | 
			
		||||
    {
 | 
			
		||||
      XEvent event;
 | 
			
		||||
      XNextEvent(context->ctrl_disp, &event);
 | 
			
		||||
      if (event.type == MappingNotify)
 | 
			
		||||
      {
 | 
			
		||||
        XMappingEvent *e = (XMappingEvent *)&event;
 | 
			
		||||
        if (e->request == MappingKeyboard)
 | 
			
		||||
        {
 | 
			
		||||
          XRefreshKeyboardMapping(e);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t detect_destroy(void *_context)
 | 
			
		||||
{
 | 
			
		||||
  DetectContext *context = (DetectContext *)_context;
 | 
			
		||||
  if (!context)
 | 
			
		||||
  {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  XRecordDisableContext(context->ctrl_disp, context->x_context);
 | 
			
		||||
  XRecordFreeContext(context->ctrl_disp, context->x_context);
 | 
			
		||||
  XFree(context->record_range);
 | 
			
		||||
  XCloseDisplay(context->data_disp);
 | 
			
		||||
  XCloseDisplay(context->ctrl_disp);
 | 
			
		||||
  delete context;
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void detect_event_callback(XPointer p, XRecordInterceptData *hook)
 | 
			
		||||
{
 | 
			
		||||
  DetectContext *context = (DetectContext *)p;
 | 
			
		||||
  if (!context)
 | 
			
		||||
  {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Make sure the event comes from the X11 server
 | 
			
		||||
  if (hook->category != XRecordFromServer)
 | 
			
		||||
  {
 | 
			
		||||
    XRecordFreeData(hook);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Cast the event payload to a XRecordDatum, needed later to access the fields
 | 
			
		||||
  // This struct was hard to find and understand. Turn's out that all the
 | 
			
		||||
  // required data are included in the "event" field of this structure.
 | 
			
		||||
  // The funny thing is that it's not a XEvent as one might expect,
 | 
			
		||||
  // but a xEvent, a very different beast defined in the Xproto.h header.
 | 
			
		||||
  // I suggest you to look at that header if you want to understand where the
 | 
			
		||||
  // upcoming field where taken from.
 | 
			
		||||
  XRecordDatum *data = (XRecordDatum *)hook->data;
 | 
			
		||||
 | 
			
		||||
  int event_type = data->type;
 | 
			
		||||
  int key_code = data->event.u.u.detail;
 | 
			
		||||
 | 
			
		||||
  // In order to convert the key_code into the corresponding string,
 | 
			
		||||
  // we need to synthesize an artificial XKeyEvent, to feed later to the
 | 
			
		||||
  // XLookupString function.
 | 
			
		||||
  XKeyEvent raw_event;
 | 
			
		||||
  raw_event.display = context->ctrl_disp;
 | 
			
		||||
  raw_event.window = data->event.u.focus.window;
 | 
			
		||||
  raw_event.root = XDefaultRootWindow(context->ctrl_disp);
 | 
			
		||||
  raw_event.subwindow = None;
 | 
			
		||||
  raw_event.time = data->event.u.keyButtonPointer.time;
 | 
			
		||||
  raw_event.x = 1;
 | 
			
		||||
  raw_event.y = 1;
 | 
			
		||||
  raw_event.x_root = 1;
 | 
			
		||||
  raw_event.y_root = 1;
 | 
			
		||||
  raw_event.same_screen = True;
 | 
			
		||||
  raw_event.keycode = key_code;
 | 
			
		||||
  raw_event.state = data->event.u.keyButtonPointer.state;
 | 
			
		||||
  raw_event.type = event_type;
 | 
			
		||||
 | 
			
		||||
  InputEvent event = {};
 | 
			
		||||
 | 
			
		||||
  // Extract the corresponding chars.
 | 
			
		||||
  int res = XLookupString(&raw_event, event.buffer, sizeof(event.buffer) - 1, NULL, NULL);
 | 
			
		||||
  if (res > 0)
 | 
			
		||||
  {
 | 
			
		||||
    event.buffer_len = res;
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    memset(event.buffer, 0, sizeof(event.buffer));
 | 
			
		||||
    event.buffer_len = 0;
 | 
			
		||||
  }
 | 
			
		||||
  KeySym key_sym = XLookupKeysym(&raw_event, 0);
 | 
			
		||||
 | 
			
		||||
  switch (event_type)
 | 
			
		||||
  {
 | 
			
		||||
  case KeyPress:
 | 
			
		||||
  {
 | 
			
		||||
    event.event_type = INPUT_EVENT_TYPE_KEYBOARD;
 | 
			
		||||
    event.key_code = key_code;
 | 
			
		||||
    event.key_sym = key_sym;
 | 
			
		||||
    event.status = INPUT_STATUS_PRESSED;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case KeyRelease:
 | 
			
		||||
  {
 | 
			
		||||
    event.event_type = INPUT_EVENT_TYPE_KEYBOARD;
 | 
			
		||||
    event.key_code = key_code;
 | 
			
		||||
    event.key_sym = key_sym;
 | 
			
		||||
    event.status = INPUT_STATUS_RELEASED;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case ButtonPress:
 | 
			
		||||
  {
 | 
			
		||||
    event.event_type = INPUT_EVENT_TYPE_MOUSE;
 | 
			
		||||
    event.key_code = key_code;
 | 
			
		||||
    event.status = INPUT_STATUS_PRESSED;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case ButtonRelease:
 | 
			
		||||
  {
 | 
			
		||||
    event.event_type = INPUT_EVENT_TYPE_MOUSE;
 | 
			
		||||
    event.key_code = key_code;
 | 
			
		||||
    event.status = INPUT_STATUS_RELEASED;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (event.event_type != 0 && context->event_callback)
 | 
			
		||||
  {
 | 
			
		||||
    context->event_callback(context->rust_instance, event);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  XRecordFreeData(hook);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int detect_error_callback(Display *, XErrorEvent *error)
 | 
			
		||||
{
 | 
			
		||||
  fprintf(stderr, "X11 Reported an error, code: %d, request_code: %d, minor_code: %d\n", error->error_code, error->request_code, error->minor_code);
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								espanso-detect/src/x11/native.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								espanso-detect/src/x11/native.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef ESPANSO_DETECT_H
 | 
			
		||||
#define ESPANSO_DETECT_H
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define INPUT_EVENT_TYPE_KEYBOARD 1
 | 
			
		||||
#define INPUT_EVENT_TYPE_MOUSE 2
 | 
			
		||||
 | 
			
		||||
#define INPUT_STATUS_PRESSED 1
 | 
			
		||||
#define INPUT_STATUS_RELEASED 2
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  // Keyboard or Mouse event
 | 
			
		||||
  int32_t event_type;
 | 
			
		||||
 | 
			
		||||
  // Contains the string corresponding to the key, if any
 | 
			
		||||
  char buffer[24];
 | 
			
		||||
  // Length of the extracted string. Equals 0 if no string is extracted
 | 
			
		||||
  int32_t buffer_len;
 | 
			
		||||
 | 
			
		||||
  // Code of the pressed key.
 | 
			
		||||
  int32_t key_sym;
 | 
			
		||||
 | 
			
		||||
  // Virtual key code of the pressed key in case of keyboard events
 | 
			
		||||
  // Mouse button code otherwise.
 | 
			
		||||
  int32_t key_code;
 | 
			
		||||
 | 
			
		||||
  // Pressed or Released status
 | 
			
		||||
  int32_t status;
 | 
			
		||||
} InputEvent;
 | 
			
		||||
 | 
			
		||||
typedef void (*EventCallback)(void *rust_istance, InputEvent data);
 | 
			
		||||
 | 
			
		||||
// Check if a X11 context is available, returning a non-zero code if true.
 | 
			
		||||
extern "C" int32_t detect_check_x11();
 | 
			
		||||
 | 
			
		||||
// Initialize the XRecord API and return the context pointer
 | 
			
		||||
extern "C" void *detect_initialize(void *rust_istance, int32_t *error_code);
 | 
			
		||||
 | 
			
		||||
// Run the event loop. Blocking call.
 | 
			
		||||
extern "C" int32_t detect_eventloop(void *context, EventCallback callback);
 | 
			
		||||
 | 
			
		||||
// Unregister from the XRecord API and destroy the context.
 | 
			
		||||
extern "C" int32_t detect_destroy(void *context);
 | 
			
		||||
 | 
			
		||||
#endif //ESPANSO_DETECT_H
 | 
			
		||||
| 
						 | 
				
			
			@ -40,11 +40,11 @@ fn cc_config() {
 | 
			
		|||
 | 
			
		||||
#[cfg(target_os = "linux")]
 | 
			
		||||
fn cc_config() {
 | 
			
		||||
  println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/");
 | 
			
		||||
  println!("cargo:rustc-link-lib=static=linuxbridge");
 | 
			
		||||
  println!("cargo:rustc-link-lib=dylib=X11");
 | 
			
		||||
  println!("cargo:rustc-link-lib=dylib=Xtst");
 | 
			
		||||
  println!("cargo:rustc-link-lib=dylib=xdo");
 | 
			
		||||
  // println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/");
 | 
			
		||||
  // println!("cargo:rustc-link-lib=static=linuxbridge");
 | 
			
		||||
  // println!("cargo:rustc-link-lib=dylib=X11");
 | 
			
		||||
  // println!("cargo:rustc-link-lib=dylib=Xtst");
 | 
			
		||||
  // println!("cargo:rustc-link-lib=dylib=xdo");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_os = "macos")]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,15 +25,17 @@ fn main() {
 | 
			
		|||
    ),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  let (remote, mut eventloop) = espanso_ui::win32::create(espanso_ui::win32::Win32UIOptions {
 | 
			
		||||
    show_icon: true,
 | 
			
		||||
    icon_paths: &icon_paths,
 | 
			
		||||
    notification_icon_path: r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png"
 | 
			
		||||
      .to_string(),
 | 
			
		||||
  });
 | 
			
		||||
 
 | 
			
		||||
  // let (remote, mut eventloop) = espanso_ui::win32::create(espanso_ui::win32::Win32UIOptions {
 | 
			
		||||
  //   show_icon: true,
 | 
			
		||||
  //   icon_paths: &icon_paths,
 | 
			
		||||
  //   notification_icon_path: r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png"
 | 
			
		||||
  //     .to_string(),
 | 
			
		||||
  // });
 | 
			
		||||
 | 
			
		||||
  std::thread::spawn(move || {
 | 
			
		||||
    let mut source = espanso_detect::win32::Win32Source::new();
 | 
			
		||||
  let handle = std::thread::spawn(move || {
 | 
			
		||||
    //let mut source = espanso_detect::win32::Win32Source::new();
 | 
			
		||||
    let mut source = espanso_detect::x11::X11Source::new();
 | 
			
		||||
    source.initialize();
 | 
			
		||||
    source.eventloop(Box::new(move |event: InputEvent| {
 | 
			
		||||
      println!("ev {:?}", event);
 | 
			
		||||
| 
						 | 
				
			
			@ -49,21 +51,22 @@ fn main() {
 | 
			
		|||
    }));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  eventloop.initialize();
 | 
			
		||||
  eventloop.run(Box::new(move |event| {
 | 
			
		||||
    println!("ui {:?}", event);
 | 
			
		||||
    let menu = Menu::from(vec![
 | 
			
		||||
      MenuItem::Simple(SimpleMenuItem::new("open", "Open")),
 | 
			
		||||
      MenuItem::Separator,
 | 
			
		||||
      MenuItem::Sub(SubMenuItem::new(
 | 
			
		||||
        "Sub",
 | 
			
		||||
        vec![
 | 
			
		||||
          MenuItem::Simple(SimpleMenuItem::new("sub1", "Sub 1")),
 | 
			
		||||
          MenuItem::Simple(SimpleMenuItem::new("sub2", "Sub 2")),
 | 
			
		||||
        ],
 | 
			
		||||
      )),
 | 
			
		||||
    ])
 | 
			
		||||
    .unwrap();
 | 
			
		||||
    remote.show_context_menu(&menu);
 | 
			
		||||
  }))
 | 
			
		||||
  // eventloop.initialize();
 | 
			
		||||
  // eventloop.run(Box::new(move |event| {
 | 
			
		||||
  //   println!("ui {:?}", event);
 | 
			
		||||
  //   let menu = Menu::from(vec![
 | 
			
		||||
  //     MenuItem::Simple(SimpleMenuItem::new("open", "Open")),
 | 
			
		||||
  //     MenuItem::Separator,
 | 
			
		||||
  //     MenuItem::Sub(SubMenuItem::new(
 | 
			
		||||
  //       "Sub",
 | 
			
		||||
  //       vec![
 | 
			
		||||
  //         MenuItem::Simple(SimpleMenuItem::new("sub1", "Sub 1")),
 | 
			
		||||
  //         MenuItem::Simple(SimpleMenuItem::new("sub2", "Sub 2")),
 | 
			
		||||
  //       ],
 | 
			
		||||
  //     )),
 | 
			
		||||
  //   ])
 | 
			
		||||
  //   .unwrap();
 | 
			
		||||
  //   remote.show_context_menu(&menu);
 | 
			
		||||
  // }))
 | 
			
		||||
  handle.join();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user