Compare commits
	
		
			41 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a65332c2a9 | |||
| 2faa98d85f | |||
| b594fcf905 | |||
| 313dbdce33 | |||
| c51b001836 | |||
| bcbc9f7ba6 | |||
| b56500af0a | |||
| c912b73c4b | |||
| 7b8ba7ab82 | |||
| dd2871b6bb | |||
| 216deecd33 | |||
| 22cc85b2e2 | |||
| 0b9350d19b | |||
| aad71116c5 | |||
| 16d6871422 | |||
| 78ff02e255 | |||
| a3f91701d0 | |||
| 697be64418 | |||
| a4b67c65ff | |||
| f040e708a4 | |||
| e6c19a1e6a | |||
| 98ecce518b | |||
| 500375dcf2 | |||
| 4e04bc306f | |||
| dfcb3ce020 | |||
| 5cf007f419 | |||
| 417fe35e91 | |||
| 3f598086b7 | |||
| dfe2c60646 | |||
| 8ec79428a9 | |||
| 3b0b8bea0d | |||
| ba00ec86a1 | |||
| f15f97d4c9 | |||
| d3eb68ed5a | |||
| 14a485deba | |||
| 236f7d4daf | |||
| f4f44a49ae | |||
| 1b72cbdfaa | |||
| ff3f7228b5 | |||
| b6ce4151bd | |||
| db6109b298 | 
@ -72,6 +72,28 @@ build_deb:arm64:
 | 
			
		||||
    - debuild -i -us -uc -b
 | 
			
		||||
    - cp ../*.deb .
 | 
			
		||||
 | 
			
		||||
build_deb:future:
 | 
			
		||||
  image: debian:sid
 | 
			
		||||
  allow_failure: true
 | 
			
		||||
  tags:
 | 
			
		||||
    - aarch64
 | 
			
		||||
  stage: build
 | 
			
		||||
  artifacts:
 | 
			
		||||
    paths:
 | 
			
		||||
      - '*.deb'
 | 
			
		||||
  script:
 | 
			
		||||
    - rm -f ../*.deb
 | 
			
		||||
    - mv debian/control-newer debian/control
 | 
			
		||||
    - apt-get -y build-dep .
 | 
			
		||||
    - apt-get -y install devscripts
 | 
			
		||||
    - REV=$(git log -1 --format=%h)
 | 
			
		||||
    - VER=$(dpkg-parsechangelog -SVersion)
 | 
			
		||||
    - DEBFULLNAME="Librem5 CI"
 | 
			
		||||
    - EMAIL="librem5-builds@lists.community.puri.sm"
 | 
			
		||||
    - dch -v"$VER+librem5ci$CI_PIPELINE_ID.$REV" "$MSG"
 | 
			
		||||
    - debuild -i -us -uc -b
 | 
			
		||||
    - cp ../*.deb .
 | 
			
		||||
 | 
			
		||||
test_lintian:
 | 
			
		||||
  stage: test
 | 
			
		||||
  needs:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								Cargo.deps
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.deps
									
									
									
									
									
								
							@ -5,6 +5,9 @@ clap = { version = "2.33.*", default-features = false }
 | 
			
		||||
[dependencies.cairo-rs]
 | 
			
		||||
version = "0.7.*"
 | 
			
		||||
 | 
			
		||||
[dependencies.cairo-sys-rs]
 | 
			
		||||
version = "0.9"
 | 
			
		||||
 | 
			
		||||
[dependencies.gdk]
 | 
			
		||||
version = "0.11.*"
 | 
			
		||||
 | 
			
		||||
@ -16,6 +19,14 @@ features = ["v2_44"]
 | 
			
		||||
version = "0.8.*"
 | 
			
		||||
features = ["v2_44"]
 | 
			
		||||
 | 
			
		||||
[dependencies.glib-sys]
 | 
			
		||||
version = "*"
 | 
			
		||||
features = ["v2_44"]
 | 
			
		||||
 | 
			
		||||
[dependencies.gtk]
 | 
			
		||||
version = "0.7.*"
 | 
			
		||||
features = ["v3_22"]
 | 
			
		||||
 | 
			
		||||
[dependencies.gtk-sys]
 | 
			
		||||
version = "0.9"
 | 
			
		||||
features = ["v3_22"]
 | 
			
		||||
@ -1,21 +0,0 @@
 | 
			
		||||
# Dependencies which change based on build flags
 | 
			
		||||
bitflags = "1.0.*"
 | 
			
		||||
clap = { version = "2.32.*", default-features = false }
 | 
			
		||||
 | 
			
		||||
[dependencies.cairo-rs]
 | 
			
		||||
version = "0.5.*"
 | 
			
		||||
 | 
			
		||||
[dependencies.gdk]
 | 
			
		||||
version = "0.9.*"
 | 
			
		||||
 | 
			
		||||
[dependencies.gio]
 | 
			
		||||
version = "0.5.*"
 | 
			
		||||
features = ["v2_44"]
 | 
			
		||||
 | 
			
		||||
[dependencies.glib]
 | 
			
		||||
version = "0.6.*"
 | 
			
		||||
features = ["v2_44"]
 | 
			
		||||
 | 
			
		||||
[dependencies.gtk]
 | 
			
		||||
version = "0.5.*"
 | 
			
		||||
features = ["v3_22"]
 | 
			
		||||
							
								
								
									
										33
									
								
								Cargo.deps.newer
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Cargo.deps.newer
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
# Dependencies which change based on build flags
 | 
			
		||||
# For the newer-than-Byzantium config
 | 
			
		||||
bitflags = "1.3.*"
 | 
			
		||||
clap = { version = "2.33.*", default-features = false }
 | 
			
		||||
 | 
			
		||||
[dependencies.cairo-rs]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
 | 
			
		||||
[dependencies.cairo-sys-rs]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
 | 
			
		||||
[dependencies.gdk]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
 | 
			
		||||
[dependencies.gio]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
features = ["v2_58"]
 | 
			
		||||
 | 
			
		||||
[dependencies.glib]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
features = ["v2_58"]
 | 
			
		||||
 | 
			
		||||
[dependencies.glib-sys]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
features = ["v2_58"]
 | 
			
		||||
 | 
			
		||||
[dependencies.gtk]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
features = ["v3_22"]
 | 
			
		||||
 | 
			
		||||
[dependencies.gtk-sys]
 | 
			
		||||
version = "0.14.*"
 | 
			
		||||
features = ["v3_22"]
 | 
			
		||||
							
								
								
									
										4
									
								
								Cargo.deps.online
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Cargo.deps.online
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
# Dependencies which are only used with online, crates.io builds.
 | 
			
		||||
[patch.crates-io]
 | 
			
		||||
# Dependency was yanked, but gio 0.7 needs it.
 | 
			
		||||
fragile = { git = "https://source.puri.sm/dorota.czaplejewicz/fragile.git", tag = "0.3.0" }
 | 
			
		||||
							
								
								
									
										20
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -30,9 +30,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "autocfg"
 | 
			
		||||
version = "1.0.1"
 | 
			
		||||
version = "1.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 | 
			
		||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bitflags"
 | 
			
		||||
@ -67,9 +67,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cc"
 | 
			
		||||
version = "1.0.72"
 | 
			
		||||
version = "1.0.73"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
 | 
			
		||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap"
 | 
			
		||||
@ -283,9 +283,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libc"
 | 
			
		||||
version = "0.2.113"
 | 
			
		||||
version = "0.2.119"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9"
 | 
			
		||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "linked-hash-map"
 | 
			
		||||
@ -388,18 +388,18 @@ checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde"
 | 
			
		||||
version = "1.0.135"
 | 
			
		||||
version = "1.0.136"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2cf9235533494ea2ddcdb794665461814781c53f19d87b76e571a1c35acbad2b"
 | 
			
		||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "serde_derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_derive"
 | 
			
		||||
version = "1.0.135"
 | 
			
		||||
version = "1.0.136"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8dcde03d87d4c973c04be249e7d8f0b35db1c848c487bd43032808e59dd8328d"
 | 
			
		||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 | 
			
		||||
@ -17,22 +17,9 @@ name = "test_layout"
 | 
			
		||||
path = "@path@/examples/test_layout.rs"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
gio_v0_5 = []
 | 
			
		||||
gtk_v0_5 = []
 | 
			
		||||
rustc_less_1_36 = []
 | 
			
		||||
glib_v0_14 = []
 | 
			
		||||
 | 
			
		||||
# Dependencies which don't change based on build flags
 | 
			
		||||
[dependencies.cairo-sys-rs]
 | 
			
		||||
version = "0.9"
 | 
			
		||||
 | 
			
		||||
[dependencies.glib-sys]
 | 
			
		||||
version = "*"
 | 
			
		||||
features = ["v2_44"]
 | 
			
		||||
 | 
			
		||||
[dependencies.gtk-sys]
 | 
			
		||||
version = "0.9"
 | 
			
		||||
features = ["v3_22"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
maplit = "1.0.*"
 | 
			
		||||
serde = { version = "1.0.*", features = ["derive"] }
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,31 @@
 | 
			
		||||
/* Theme independent style */
 | 
			
		||||
/* Theme independent styles */
 | 
			
		||||
 | 
			
		||||
sq_view {
 | 
			
		||||
    font-family: cantarell, sans-serif;
 | 
			
		||||
    font-size: 1.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    margin: 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_view.wide sq_button {
 | 
			
		||||
    margin: 3px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.latched,
 | 
			
		||||
sq_button.locked {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.action {
 | 
			
		||||
    font-size: 0.75em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.small {
 | 
			
		||||
    font-size: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_view.pin sq_button {
 | 
			
		||||
    border-radius: 0px;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										89
									
								
								data/keyboards/ro.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								data/keyboards/ro.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: 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        . 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        , Return"
 | 
			
		||||
    numbers:
 | 
			
		||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
			
		||||
        - "@ # € % & - _ + ( )"
 | 
			
		||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
			
		||||
        - "show_letters show_eschars preferences         space        . Return"
 | 
			
		||||
    symbols:
 | 
			
		||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
			
		||||
        - "© ® £ $ ¥ ^ ° * { }"
 | 
			
		||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
			
		||||
        - "show_letters show_eschars preferences         space        , Return"
 | 
			
		||||
    eschars:
 | 
			
		||||
        - "ă â î ș ț á é í ó ü"
 | 
			
		||||
        - "Ă Â Î Ș Ț Á É Í Ó Ü"
 | 
			
		||||
        - "show_numbers_from_symbols „ ” « » ― { } 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: "special"
 | 
			
		||||
        icon: "keyboard-mode-symbolic"
 | 
			
		||||
    show_numbers:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "numbers"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "123"
 | 
			
		||||
    show_numbers_from_symbols:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "numbers"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "123"
 | 
			
		||||
    show_letters:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "base"
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        label: "abc"
 | 
			
		||||
    show_symbols:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "symbols"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "*/="
 | 
			
		||||
    show_eschars:
 | 
			
		||||
        action:
 | 
			
		||||
            locking:
 | 
			
		||||
                lock_view: "eschars"
 | 
			
		||||
                unlock_view: "base"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "ăĂ"
 | 
			
		||||
    space:
 | 
			
		||||
        outline: "spaceline"
 | 
			
		||||
        text: " "
 | 
			
		||||
    Return:
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        icon: "key-enter"
 | 
			
		||||
        keysym: "Return"
 | 
			
		||||
    colon:
 | 
			
		||||
        text: ":"
 | 
			
		||||
    "\"":
 | 
			
		||||
        keysym: "quotedbl"
 | 
			
		||||
							
								
								
									
										89
									
								
								data/keyboards/ro_wide.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								data/keyboards/ro_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: 153, height: 42 }
 | 
			
		||||
    special: { width: 54, height: 42 }
 | 
			
		||||
 | 
			
		||||
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        . 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        , Return"
 | 
			
		||||
    numbers:
 | 
			
		||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
			
		||||
        - "@ # € % & - _ + ( )"
 | 
			
		||||
        - "show_symbols   , \" ' colon ; ! ?  BackSpace"
 | 
			
		||||
        - "show_letters show_eschars preferences         space        . Return"
 | 
			
		||||
    symbols:
 | 
			
		||||
        - "~ ` | · √ π τ ÷ × ¶"
 | 
			
		||||
        - "© ® £ $ ¥ ^ ° * { }"
 | 
			
		||||
        - "show_numbers_from_symbols   \\ / < > = [ ]  BackSpace"
 | 
			
		||||
        - "show_letters show_eschars preferences         space        , Return"
 | 
			
		||||
    eschars:
 | 
			
		||||
        - "ă â î ș ț á é í ó ü"
 | 
			
		||||
        - "Ă Â Î Ș Ț Á É Í Ó Ü"
 | 
			
		||||
        - "show_numbers_from_symbols „ ” « » ― { } 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: "special"
 | 
			
		||||
        icon: "keyboard-mode-symbolic"
 | 
			
		||||
    show_numbers:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "numbers"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "123"
 | 
			
		||||
    show_numbers_from_symbols:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "numbers"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "123"
 | 
			
		||||
    show_letters:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "base"
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        label: "abc"
 | 
			
		||||
    show_symbols:
 | 
			
		||||
        action:
 | 
			
		||||
            set_view: "symbols"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "*/="
 | 
			
		||||
    show_eschars:
 | 
			
		||||
        action:
 | 
			
		||||
            locking:
 | 
			
		||||
                lock_view: "eschars"
 | 
			
		||||
                unlock_view: "base"
 | 
			
		||||
        outline: "altline"
 | 
			
		||||
        label: "ăĂ"
 | 
			
		||||
    space:
 | 
			
		||||
        outline: "spaceline"
 | 
			
		||||
        text: " "
 | 
			
		||||
    Return:
 | 
			
		||||
        outline: "wide"
 | 
			
		||||
        icon: "key-enter"
 | 
			
		||||
        keysym: "Return"
 | 
			
		||||
    colon:
 | 
			
		||||
        text: ":"
 | 
			
		||||
    "\"":
 | 
			
		||||
        keysym: "quotedbl"
 | 
			
		||||
@ -1,34 +1,22 @@
 | 
			
		||||
/* Adwaita-dark style keyboard */
 | 
			
		||||
sq_view {
 | 
			
		||||
    background-color: rgba(0, 0, 0, 255);
 | 
			
		||||
    color: #ffffff;
 | 
			
		||||
    font-family: cantarell, sans-serif;
 | 
			
		||||
    font-size: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_view sq_button {
 | 
			
		||||
    color: #deddda;
 | 
			
		||||
    background: #464448;
 | 
			
		||||
    border-style: solid;
 | 
			
		||||
    border-width: 1px;
 | 
			
		||||
    border-color: #5e5c64;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    margin: 4px 2px 4px 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_view.wide sq_button {
 | 
			
		||||
    margin: 1px 1px 1px 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button:active {
 | 
			
		||||
    background: #747077;
 | 
			
		||||
    border-color: #96949d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.altline,
 | 
			
		||||
sq_button.special,
 | 
			
		||||
sq_button.wide {
 | 
			
		||||
    background: #2b292f;
 | 
			
		||||
    border-color: #3e3a44;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.latched {
 | 
			
		||||
@ -41,22 +29,12 @@ sq_button.locked {
 | 
			
		||||
    color: #1c71d8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.action {
 | 
			
		||||
    font-size: 0.75em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.small {
 | 
			
		||||
    font-size: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Return {
 | 
			
		||||
    background: #1c71d8;
 | 
			
		||||
    border-color: #1a5fb4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Return:active {
 | 
			
		||||
    background: #1c71d8;
 | 
			
		||||
    border-color: #3584e4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@import url("resource:///sm/puri/squeekboard/common.css");
 | 
			
		||||
 | 
			
		||||
@ -1,65 +1,47 @@
 | 
			
		||||
/* Keyboard style */
 | 
			
		||||
sq_view {
 | 
			
		||||
    background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/
 | 
			
		||||
    color: @theme_text_color; /*#ffffff;*/
 | 
			
		||||
    font-family: cantarell, sans-serif;
 | 
			
		||||
    font-size: 25px;
 | 
			
		||||
    background-color: mix(@theme_base_color, @theme_fg_color, 0.1);
 | 
			
		||||
    box-shadow:inset 0 1px 0 0 mix(@borders, @theme_base_color, 0.8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_view sq_button {
 | 
			
		||||
    color: @theme_fg_color; /*#deddda;*/
 | 
			
		||||
    background: mix(@theme_bg_color, @theme_base_color, -0.5); /* #464448; */
 | 
			
		||||
    border-style: solid;
 | 
			
		||||
    border-width: 1px;
 | 
			
		||||
    border-color: @borders; /* #5e5c64;*/
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    margin: 4px 2px 4px 2px;
 | 
			
		||||
sq_button {
 | 
			
		||||
    color: @theme_fg_color;
 | 
			
		||||
    background: alpha(@theme_fg_color, 0.07); 
 | 
			
		||||
    box-shadow: 0 1px 0 0 rgba(0,0,0,0.2); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_view.wide sq_button {
 | 
			
		||||
    margin: 1px 1px 1px 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button:active,
 | 
			
		||||
sq_button.altline:active,
 | 
			
		||||
sq_button.special:active,
 | 
			
		||||
sq_button.wide:active {
 | 
			
		||||
    background: mix(@theme_bg_color, @theme_selected_bg_color, 0.4);/* #747077; */
 | 
			
		||||
    border-color: mix(@borders, @theme_selected_fg_color, 0.5);/* #96949d; */
 | 
			
		||||
sq_button:active {
 | 
			
		||||
    background: alpha(@theme_fg_color, 0.11);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.altline,
 | 
			
		||||
sq_button.special,
 | 
			
		||||
sq_button.wide {
 | 
			
		||||
    background: mix(@theme_bg_color, @theme_base_color, 0.5); /*#2b292f;*/
 | 
			
		||||
    border-color: @borders; /* #3e3a44; */
 | 
			
		||||
sq_button.special {
 | 
			
		||||
    background: alpha(@theme_fg_color, 0.15); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.altline:active,
 | 
			
		||||
sq_button.special:active {
 | 
			
		||||
    background: alpha(@theme_fg_color, 0.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.latched {
 | 
			
		||||
    background: @theme_fg_color; /*#ffffff;*/
 | 
			
		||||
    color: @theme_bg_color; /*#2b292f;*/
 | 
			
		||||
    background: alpha(@theme_fg_color, 0.2); 
 | 
			
		||||
    color: alpha(@theme_fg_color, 0.8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sq_button.small {
 | 
			
		||||
    font-size: 0.5em;
 | 
			
		||||
    background: alpha(@theme_fg_color, 0.5);
 | 
			
		||||
    color: @theme_base_color;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Return {
 | 
			
		||||
    background: @theme_selected_bg_color; /* #1c71d8; */
 | 
			
		||||
    border-color: @borders; /*#1a5fb4;*/
 | 
			
		||||
    background: @theme_selected_bg_color;
 | 
			
		||||
    color: @theme_selected_fg_color;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#Return:active {
 | 
			
		||||
    background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/
 | 
			
		||||
    border-color: @borders; /*#3584e4;*/
 | 
			
		||||
    background: mix(@theme_selected_bg_color, black, 0.2);
 | 
			
		||||
    color: mix(@theme_selected_fg_color, black, 0.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@import url("resource:///sm/puri/squeekboard/common.css");
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/cargo/config
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/cargo/config
									
									
									
									
										vendored
									
									
								
							@ -9,4 +9,3 @@ replace-with = 'vendored-sources'
 | 
			
		||||
 | 
			
		||||
[source.vendored-sources]
 | 
			
		||||
directory = '/usr/share/cargo/registry'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										60
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,63 @@
 | 
			
		||||
squeekboard (1.17.1-1) experimental; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Dorota Czaplejewicz ]
 | 
			
		||||
  * build: Replace missing crates.io dependency with Purism-hosted one
 | 
			
		||||
  * ci: Allow failure on sid
 | 
			
		||||
  * panel: Use scaling to set height
 | 
			
		||||
 | 
			
		||||
  [ William Wold ]
 | 
			
		||||
  * Do not reset pending state on zwp_input_method_v2.done
 | 
			
		||||
 | 
			
		||||
  [ Arnaud Ferraris ]
 | 
			
		||||
  * state: fix "wide mode" detection in portrait orientation
 | 
			
		||||
 | 
			
		||||
 -- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>  Tue, 05 Apr 2022 13:32:53 +0000
 | 
			
		||||
 | 
			
		||||
squeekboard (1.17.0-1) experimental; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Dorota Czaplejewicz ]
 | 
			
		||||
  * docs: Detail the release process better
 | 
			
		||||
  * cleanup: Remove unused header lines
 | 
			
		||||
  * docstrings: Clarify the purpose of Receiver
 | 
			
		||||
  * wayland: Move initialization to the Rust side
 | 
			
		||||
  * ffi: Remove unnecessary pointers to InputMethod
 | 
			
		||||
  * outputs: Clean up for more Rust usage
 | 
			
		||||
  * outputs: Notify the state manager about changes
 | 
			
		||||
  * outputs: Handle removal
 | 
			
		||||
  * Save outputs state
 | 
			
		||||
  * Store preferred output
 | 
			
		||||
  * deps: Vendor assert_matches
 | 
			
		||||
  * Carry output information on visible command all the way to C
 | 
			
		||||
  * Don't reach for globals to choose output
 | 
			
		||||
  * visibility: Forward panel height information to window creation
 | 
			
		||||
  * outputs: Remove ui manager
 | 
			
		||||
  * output: Use new source of panel height information
 | 
			
		||||
  * panel: Apply a hard limit of 1/2 height
 | 
			
		||||
  * cargo: Update lockfile
 | 
			
		||||
 | 
			
		||||
  [ Cosmin Humeniuc ]
 | 
			
		||||
  * Add Romanian layout
 | 
			
		||||
 | 
			
		||||
  [ Sam Hewitt ]
 | 
			
		||||
  * data: Update stylesheet with upstream design
 | 
			
		||||
 | 
			
		||||
  [ Tor ]
 | 
			
		||||
  * Make compatible with latest cargo deps
 | 
			
		||||
 | 
			
		||||
  [ Luís Fernando Stürmer da Rosa ]
 | 
			
		||||
  * Update Brazilian Portuguese translation
 | 
			
		||||
 | 
			
		||||
  [ Fran Dieguez ]
 | 
			
		||||
  * Add Galician translation
 | 
			
		||||
 | 
			
		||||
  [ William Wold ]
 | 
			
		||||
  * Check if dbus handler is null before using
 | 
			
		||||
 | 
			
		||||
  [ Yosef Or Boczko ]
 | 
			
		||||
  * Add Hebrew translation
 | 
			
		||||
 | 
			
		||||
 -- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>  Tue, 25 Jan 2022 11:24:04 +0000
 | 
			
		||||
 | 
			
		||||
squeekboard (1.16.0-1) experimental; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Dorota Czaplejewicz ]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										58
									
								
								debian/control-newer
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								debian/control-newer
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
Source: squeekboard
 | 
			
		||||
Section: x11
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
 | 
			
		||||
Build-Depends:
 | 
			
		||||
 cargo,
 | 
			
		||||
 debhelper-compat (= 13),
 | 
			
		||||
 meson (>=0.51.0),
 | 
			
		||||
 ninja-build,
 | 
			
		||||
 pkg-config,
 | 
			
		||||
 libglib2.0-dev,
 | 
			
		||||
 libgnome-desktop-3-dev,
 | 
			
		||||
 libgtk-3-dev,
 | 
			
		||||
 libfeedback-dev,
 | 
			
		||||
 librust-bitflags-dev (>= 1.0),
 | 
			
		||||
 librust-clap-dev (>= 2.32),
 | 
			
		||||
 librust-gio+v2-58-dev,
 | 
			
		||||
 librust-glib+v2-58-dev,
 | 
			
		||||
 librust-glib-sys-dev,
 | 
			
		||||
 librust-gtk+v3-22-dev (>= 0.5),
 | 
			
		||||
 librust-gtk-sys-dev,
 | 
			
		||||
 librust-maplit-1-dev (>= 1.0),
 | 
			
		||||
 librust-serde-derive-1-dev (>= 1.0),
 | 
			
		||||
 librust-serde-yaml-0.8-dev (>= 0.8),
 | 
			
		||||
 librust-xkbcommon-0.4+wayland-dev (>= 0.4),
 | 
			
		||||
 libwayland-dev (>= 1.16),
 | 
			
		||||
 lsb-release,
 | 
			
		||||
 python3,
 | 
			
		||||
 python3-ruamel.yaml,
 | 
			
		||||
 rustc,
 | 
			
		||||
 wayland-protocols (>= 1.14),
 | 
			
		||||
Standards-Version: 4.1.3
 | 
			
		||||
Homepage: https://source.puri.sm/Librem5/squeekboard
 | 
			
		||||
 | 
			
		||||
Package: squeekboard
 | 
			
		||||
Architecture: linux-any
 | 
			
		||||
Depends:
 | 
			
		||||
# for the Adwaita-dark theme
 | 
			
		||||
 gnome-themes-extra-data,
 | 
			
		||||
 ${shlibs:Depends},
 | 
			
		||||
 ${misc:Depends},
 | 
			
		||||
Breaks:
 | 
			
		||||
 librem5-base (<< 24),
 | 
			
		||||
Description: On-screen keyboard for Wayland
 | 
			
		||||
 Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
 | 
			
		||||
 | 
			
		||||
Package: squeekboard-devel
 | 
			
		||||
Architecture: linux-any
 | 
			
		||||
Depends:
 | 
			
		||||
 python3,
 | 
			
		||||
 python3-gi,
 | 
			
		||||
 ${shlibs:Depends},
 | 
			
		||||
 ${misc:Depends},
 | 
			
		||||
Description: Resources for making Squeekboard layouts
 | 
			
		||||
 Tools for creating and testing Squeekboard layouts:
 | 
			
		||||
 .
 | 
			
		||||
  * squeekboard-entry
 | 
			
		||||
  * squeekboard-test-layout
 | 
			
		||||
							
								
								
									
										8
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@ -25,10 +25,10 @@ export RUSTFLAGS = --remap-path-prefix=$(CURDIR)=/remap-pwd $(xgot)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
distrel := $(shell lsb_release --codename --short)
 | 
			
		||||
ifneq (,$(filter $(distrel),buster amber))
 | 
			
		||||
	legacy = true
 | 
			
		||||
ifneq (,$(filter $(distrel),sid))
 | 
			
		||||
	newer = true
 | 
			
		||||
else
 | 
			
		||||
	legacy = false
 | 
			
		||||
	newer = false
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
%:
 | 
			
		||||
@ -38,6 +38,6 @@ endif
 | 
			
		||||
# causing Cargo to refuse to build with a crates.io copy
 | 
			
		||||
override_dh_auto_configure:
 | 
			
		||||
	[ ! -f Cargo.lock ] || rm Cargo.lock
 | 
			
		||||
	dh_auto_configure -- -Dlegacy=$(legacy)
 | 
			
		||||
	dh_auto_configure -- -Dnewer=$(newer) -Donline=false
 | 
			
		||||
 | 
			
		||||
override_dh_autoreconf:
 | 
			
		||||
 | 
			
		||||
@ -205,6 +205,7 @@ While the file is not actually used, it's a good idea to save the config in case
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
cd squeekboard-build
 | 
			
		||||
.../squeekboard-source/cargo.sh update
 | 
			
		||||
ninja test
 | 
			
		||||
cp ./Cargo.lock .../squeekboard-source
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								meson.build
									
									
									
									
									
								
							@ -1,7 +1,7 @@
 | 
			
		||||
project(
 | 
			
		||||
    'squeekboard',
 | 
			
		||||
    'c', 'rust',
 | 
			
		||||
    version: '1.16.0',
 | 
			
		||||
    version: '1.17.1',
 | 
			
		||||
    license: 'GPLv3',
 | 
			
		||||
    meson_version: '>=0.51.0',
 | 
			
		||||
    default_options: [
 | 
			
		||||
@ -96,19 +96,23 @@ cargo_toml_base = configure_file(
 | 
			
		||||
    configuration: path_data,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
cargo_patch = []
 | 
			
		||||
 | 
			
		||||
cargo_deps = files('Cargo.deps')
 | 
			
		||||
 | 
			
		||||
if get_option('legacy') == true
 | 
			
		||||
    cargo_build_flags += ['--features', 'gtk_v0_5,gio_v0_5,rustc_less_1_36']
 | 
			
		||||
    cargo_deps = files('Cargo.deps.legacy')
 | 
			
		||||
if get_option('newer') == true
 | 
			
		||||
    cargo_build_flags += ['--features', 'glib_v0_14']
 | 
			
		||||
    cargo_deps = files('Cargo.deps.newer')
 | 
			
		||||
else
 | 
			
		||||
    cargo_deps = files('Cargo.deps')
 | 
			
		||||
    if get_option('online') == true
 | 
			
		||||
        cargo_patch = [files('Cargo.deps.online')]
 | 
			
		||||
    endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
cat = find_program('cat')
 | 
			
		||||
cargo_toml = custom_target(
 | 
			
		||||
    'Cargo.toml',
 | 
			
		||||
    output: 'Cargo.toml',
 | 
			
		||||
    command: [cat, cargo_toml_base, cargo_deps],
 | 
			
		||||
    command: [cat, cargo_toml_base, cargo_deps] + cargo_patch,
 | 
			
		||||
    capture: true,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,9 +7,13 @@ option('tests',
 | 
			
		||||
       type: 'boolean', value: true,
 | 
			
		||||
       description: 'Whether to compile unit tests')
 | 
			
		||||
 | 
			
		||||
option('legacy',
 | 
			
		||||
option('newer',
 | 
			
		||||
       type: 'boolean', value: false,
 | 
			
		||||
       description: 'Build with Deban Buster versions of dependencies')
 | 
			
		||||
       description: 'Build with dependencies newer than those of Byzantium')
 | 
			
		||||
 | 
			
		||||
option('online',
 | 
			
		||||
       type: 'boolean', value: true,
 | 
			
		||||
       description: 'Pull packages from the internet while building, as opposed to a local regstry.')
 | 
			
		||||
       
 | 
			
		||||
option('strict',
 | 
			
		||||
       type: 'boolean', value: true,
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,8 @@ de
 | 
			
		||||
fa
 | 
			
		||||
fi
 | 
			
		||||
fur
 | 
			
		||||
gl
 | 
			
		||||
he
 | 
			
		||||
nl
 | 
			
		||||
pt_BR
 | 
			
		||||
ro
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										52
									
								
								po/gl.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								po/gl.po
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
# Galician translation for squeekboard.
 | 
			
		||||
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
 | 
			
		||||
# This file is distributed under the same license as the squeekboard package.
 | 
			
		||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
 | 
			
		||||
# Fran Diéguez <frandieguez@gnome.org>, 2022.
 | 
			
		||||
#
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: squeekboard master\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
 | 
			
		||||
"issues\n"
 | 
			
		||||
"POT-Creation-Date: 2022-02-02 17:41+0000\n"
 | 
			
		||||
"PO-Revision-Date: 2022-02-04 16:18+0100\n"
 | 
			
		||||
"Last-Translator: Fran Diéguez <frandieguez@gnome.org>\n"
 | 
			
		||||
"Language-Team: Galician <proxecto@trasno.gal>\n"
 | 
			
		||||
"Language: gl\n"
 | 
			
		||||
"MIME-Version: 1.0\n"
 | 
			
		||||
"Content-Type: text/plain; charset=UTF-8\n"
 | 
			
		||||
"Content-Transfer-Encoding: 8bit\n"
 | 
			
		||||
"X-DL-Team: gl\n"
 | 
			
		||||
"X-DL-Module: squeekboard\n"
 | 
			
		||||
"X-DL-Branch: master\n"
 | 
			
		||||
"X-DL-Domain: po\n"
 | 
			
		||||
"X-DL-State: Translating\n"
 | 
			
		||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
 | 
			
		||||
"X-Generator: Gtranslator 41.0\n"
 | 
			
		||||
 | 
			
		||||
#. translators: This is a emmoji keyboard layout
 | 
			
		||||
#: data/popover.ui:6
 | 
			
		||||
msgid "Emoji"
 | 
			
		||||
msgstr "Emoticono"
 | 
			
		||||
 | 
			
		||||
#. translators: This is a terminal keyboard layout
 | 
			
		||||
#: data/popover.ui:12
 | 
			
		||||
msgid "Terminal"
 | 
			
		||||
msgstr "Terminal"
 | 
			
		||||
 | 
			
		||||
#: data/popover.ui:18
 | 
			
		||||
msgid "Keyboard Settings"
 | 
			
		||||
msgstr "Preferencias de teclado"
 | 
			
		||||
 | 
			
		||||
#: data/sm.puri.Squeekboard.desktop.in.in:3
 | 
			
		||||
msgid "Squeekboard"
 | 
			
		||||
msgstr "Squeekboard"
 | 
			
		||||
 | 
			
		||||
#: data/sm.puri.Squeekboard.desktop.in.in:4
 | 
			
		||||
msgid "On Screen Keyboard"
 | 
			
		||||
msgstr "Teclado en pantalla"
 | 
			
		||||
 | 
			
		||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
 | 
			
		||||
msgid "An on screen virtual keyboard"
 | 
			
		||||
msgstr "Un teclado en pantalla virtual"
 | 
			
		||||
							
								
								
									
										46
									
								
								po/he.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								po/he.po
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
# Hebrew translation for squeekboard.
 | 
			
		||||
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
 | 
			
		||||
# This file is distributed under the same license as the squeekboard package.
 | 
			
		||||
# Yosef Or Boczko <yoseforb@gmail.com>, 2022.
 | 
			
		||||
#
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: squeekboard master\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
 | 
			
		||||
"issues\n"
 | 
			
		||||
"POT-Creation-Date: 2022-02-04 15:22+0000\n"
 | 
			
		||||
"PO-Revision-Date: 2022-02-14 18:05+0200\n"
 | 
			
		||||
"Last-Translator: Yosef Or Boczko <yoseforb@gmail.com>\n"
 | 
			
		||||
"Language-Team: Hebrew <>\n"
 | 
			
		||||
"Language: he\n"
 | 
			
		||||
"MIME-Version: 1.0\n"
 | 
			
		||||
"Content-Type: text/plain; charset=UTF-8\n"
 | 
			
		||||
"Content-Transfer-Encoding: 8bit\n"
 | 
			
		||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 | 
			
		||||
"X-Generator: Gtranslator 40.0\n"
 | 
			
		||||
 | 
			
		||||
#. translators: This is a emmoji keyboard layout
 | 
			
		||||
#: data/popover.ui:6
 | 
			
		||||
msgid "Emoji"
 | 
			
		||||
msgstr "רגשון"
 | 
			
		||||
 | 
			
		||||
#. translators: This is a terminal keyboard layout
 | 
			
		||||
#: data/popover.ui:12
 | 
			
		||||
msgid "Terminal"
 | 
			
		||||
msgstr "מסוף"
 | 
			
		||||
 | 
			
		||||
#: data/popover.ui:18
 | 
			
		||||
msgid "Keyboard Settings"
 | 
			
		||||
msgstr "הגדרות מקלדת"
 | 
			
		||||
 | 
			
		||||
#: data/sm.puri.Squeekboard.desktop.in.in:3
 | 
			
		||||
msgid "Squeekboard"
 | 
			
		||||
msgstr "Squeekboard"
 | 
			
		||||
 | 
			
		||||
#: data/sm.puri.Squeekboard.desktop.in.in:4
 | 
			
		||||
msgid "On Screen Keyboard"
 | 
			
		||||
msgstr "מקלדת על המסך"
 | 
			
		||||
 | 
			
		||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
 | 
			
		||||
msgid "An on screen virtual keyboard"
 | 
			
		||||
msgstr "מקלדת מדומה על המסך"
 | 
			
		||||
@ -2,22 +2,23 @@
 | 
			
		||||
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
 | 
			
		||||
# This file is distributed under the same license as the squeekboard package.
 | 
			
		||||
# Rafael Fontenelle <rafaelff@gnome.org>, 2021.
 | 
			
		||||
# Luís Fernando Stürmer da Rosa <luisfsr@dismail.de>, 2021.
 | 
			
		||||
#
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: squeekboard master\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
 | 
			
		||||
"issues\n"
 | 
			
		||||
"POT-Creation-Date: 2021-12-22 10:36+0000\n"
 | 
			
		||||
"PO-Revision-Date: 2021-12-22 09:38-0300\n"
 | 
			
		||||
"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
 | 
			
		||||
"POT-Creation-Date: 2021-12-22 12:39+0000\n"
 | 
			
		||||
"PO-Revision-Date: 2022-01-30 12:34-0300\n"
 | 
			
		||||
"Last-Translator: Luís Fernando Stürmer da Rosa <luisfsr@dismail.de>\n"
 | 
			
		||||
"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
 | 
			
		||||
"Language: pt_BR\n"
 | 
			
		||||
"MIME-Version: 1.0\n"
 | 
			
		||||
"Content-Type: text/plain; charset=UTF-8\n"
 | 
			
		||||
"Content-Transfer-Encoding: 8bit\n"
 | 
			
		||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
 | 
			
		||||
"X-Generator: Poedit 3.0.1\n"
 | 
			
		||||
"X-Generator: Poedit 3.0\n"
 | 
			
		||||
 | 
			
		||||
#. translators: This is a emmoji keyboard layout
 | 
			
		||||
#: data/popover.ui:6
 | 
			
		||||
 | 
			
		||||
@ -6,12 +6,18 @@
 | 
			
		||||
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use crate::main::PixelSize;
 | 
			
		||||
use crate::outputs::OutputId;
 | 
			
		||||
 | 
			
		||||
/// The keyboard should hide after this has elapsed to prevent flickering.
 | 
			
		||||
pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200);
 | 
			
		||||
 | 
			
		||||
/// The outwardly visible state of visibility
 | 
			
		||||
#[derive(PartialEq, Debug, Clone)]
 | 
			
		||||
pub enum Outcome {
 | 
			
		||||
    Visible,
 | 
			
		||||
    Visible {
 | 
			
		||||
        output: OutputId,
 | 
			
		||||
        height: PixelSize,
 | 
			
		||||
    },
 | 
			
		||||
    Hidden,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										358
									
								
								src/assert_matches.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								src/assert_matches.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,358 @@
 | 
			
		||||
/* Taken from https://github.com/murarth/assert_matches
 | 
			
		||||
 *
 | 
			
		||||
 * git commit: 26b8b40a12823c068a829ba475d0eccc13dfc221
 | 
			
		||||
 * 
 | 
			
		||||
 * assert_matches is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
 | 
			
		||||
 *
 | 
			
		||||
Copyright (c) 2016 Murarth
 | 
			
		||||
	
 | 
			
		||||
Permission is hereby granted, free of charge, to any
 | 
			
		||||
person obtaining a copy of this software and associated
 | 
			
		||||
documentation files (the "Software"), to deal in the
 | 
			
		||||
Software without restriction, including without
 | 
			
		||||
limitation the rights to use, copy, modify, merge,
 | 
			
		||||
publish, distribute, sublicense, and/or sell copies of
 | 
			
		||||
the Software, and to permit persons to whom the Software
 | 
			
		||||
is furnished to do so, subject to the following
 | 
			
		||||
conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice
 | 
			
		||||
shall be included in all copies or substantial portions
 | 
			
		||||
of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
 | 
			
		||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 | 
			
		||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 | 
			
		||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 | 
			
		||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 | 
			
		||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 | 
			
		||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 | 
			
		||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
			
		||||
DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
//! Provides a macro, `assert_matches!`, which tests whether a value
 | 
			
		||||
//! matches a given pattern, causing a panic if the match fails.
 | 
			
		||||
//!
 | 
			
		||||
//! See the macro [`assert_matches!`] documentation for more information.
 | 
			
		||||
//!
 | 
			
		||||
//! Also provides a debug-only counterpart, [`debug_assert_matches!`].
 | 
			
		||||
//!
 | 
			
		||||
//! See the macro [`debug_assert_matches!`] documentation for more information
 | 
			
		||||
//! about this macro.
 | 
			
		||||
//!
 | 
			
		||||
//! [`assert_matches!`]: macro.assert_matches.html
 | 
			
		||||
//! [`debug_assert_matches!`]: macro.debug_assert_matches.html
 | 
			
		||||
 | 
			
		||||
#![deny(missing_docs)]
 | 
			
		||||
#![cfg_attr(not(test), no_std)]
 | 
			
		||||
 | 
			
		||||
/// Asserts that an expression matches a given pattern.
 | 
			
		||||
///
 | 
			
		||||
/// A guard expression may be supplied to add further restrictions to the
 | 
			
		||||
/// expected value of the expression.
 | 
			
		||||
///
 | 
			
		||||
/// A `match` arm may be supplied to perform additional assertions or to yield
 | 
			
		||||
/// a value from the macro invocation.
 | 
			
		||||
///
 | 
			
		||||
/// # Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// #[macro_use] extern crate assert_matches;
 | 
			
		||||
///
 | 
			
		||||
/// #[derive(Debug)]
 | 
			
		||||
/// enum Foo {
 | 
			
		||||
///     A(i32),
 | 
			
		||||
///     B(&'static str),
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// # fn main() {
 | 
			
		||||
/// let a = Foo::A(1);
 | 
			
		||||
///
 | 
			
		||||
/// // Assert that `a` matches the pattern `Foo::A(_)`.
 | 
			
		||||
/// assert_matches!(a, Foo::A(_));
 | 
			
		||||
///
 | 
			
		||||
/// // Assert that `a` matches the pattern and
 | 
			
		||||
/// // that the contained value meets the condition `i > 0`.
 | 
			
		||||
/// assert_matches!(a, Foo::A(i) if i > 0);
 | 
			
		||||
///
 | 
			
		||||
/// let b = Foo::B("foobar");
 | 
			
		||||
/// 
 | 
			
		||||
/// // Assert that `b` matches the pattern `Foo::B(_)`.
 | 
			
		||||
/// assert_matches!(b, Foo::B(s) => {
 | 
			
		||||
///     // Perform additional assertions on the variable binding `s`.
 | 
			
		||||
///     assert!(s.starts_with("foo"));
 | 
			
		||||
///     assert!(s.ends_with("bar"));
 | 
			
		||||
/// });
 | 
			
		||||
///
 | 
			
		||||
/// // Assert that `b` matches the pattern and yield the string `s`.
 | 
			
		||||
/// let s = assert_matches!(b, Foo::B(s) => s);
 | 
			
		||||
///
 | 
			
		||||
/// // Perform an assertion on the value `s`.
 | 
			
		||||
/// assert_eq!(s, "foobar");
 | 
			
		||||
/// # }
 | 
			
		||||
/// ```
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! assert_matches {
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ => (),
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`",
 | 
			
		||||
                e, stringify!($($pat)|+))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ if $cond:expr ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ if $cond => (),
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`",
 | 
			
		||||
                e, stringify!($($pat)|+ if $cond))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ => $arm:expr ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ => $arm,
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`",
 | 
			
		||||
                e, stringify!($($pat)|+))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ if $cond:expr => $arm:expr ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ if $cond => $arm,
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`",
 | 
			
		||||
                e, stringify!($($pat)|+ if $cond))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ , $($arg:tt)* ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ => (),
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
 | 
			
		||||
                e, stringify!($($pat)|+), format_args!($($arg)*))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ if $cond:expr , $($arg:tt)* ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ if $cond => (),
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
 | 
			
		||||
                e, stringify!($($pat)|+ if $cond), format_args!($($arg)*))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ => $arm:expr , $($arg:tt)* ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ => $arm,
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
 | 
			
		||||
                e, stringify!($($pat)|+), format_args!($($arg)*))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ( $e:expr , $($pat:pat)|+ if $cond:expr => $arm:expr , $($arg:tt)* ) => {
 | 
			
		||||
        match $e {
 | 
			
		||||
            $($pat)|+ if $cond => $arm,
 | 
			
		||||
            ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
 | 
			
		||||
                e, stringify!($($pat)|+ if $cond), format_args!($($arg)*))
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Asserts that an expression matches a given pattern.
 | 
			
		||||
///
 | 
			
		||||
/// Unlike [`assert_matches!`], `debug_assert_matches!` statements are only enabled
 | 
			
		||||
/// in non-optimized builds by default. An optimized build will omit all
 | 
			
		||||
/// `debug_assert_matches!` statements unless `-C debug-assertions` is passed
 | 
			
		||||
/// to the compiler.
 | 
			
		||||
///
 | 
			
		||||
/// See the macro [`assert_matches!`] documentation for more information.
 | 
			
		||||
///
 | 
			
		||||
/// [`assert_matches!`]: macro.assert_matches.html
 | 
			
		||||
#[macro_export(local_inner_macros)]
 | 
			
		||||
macro_rules! debug_assert_matches {
 | 
			
		||||
    ( $($tt:tt)* ) => { {
 | 
			
		||||
        if _assert_matches_cfg!(debug_assertions) {
 | 
			
		||||
            assert_matches!($($tt)*);
 | 
			
		||||
        }
 | 
			
		||||
    } }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[doc(hidden)]
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! _assert_matches_cfg {
 | 
			
		||||
    ( $($tt:tt)* ) => { cfg!($($tt)*) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use std::panic::{catch_unwind, UnwindSafe};
 | 
			
		||||
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    enum Foo {
 | 
			
		||||
        A(i32),
 | 
			
		||||
        B(&'static str),
 | 
			
		||||
        C(&'static str),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_assert_succeed() {
 | 
			
		||||
        let a = Foo::A(123);
 | 
			
		||||
 | 
			
		||||
        assert_matches!(a, Foo::A(_));
 | 
			
		||||
        assert_matches!(a, Foo::A(123));
 | 
			
		||||
        assert_matches!(a, Foo::A(i) if i == 123);
 | 
			
		||||
        assert_matches!(a, Foo::A(42) | Foo::A(123));
 | 
			
		||||
 | 
			
		||||
        let b = Foo::B("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(b, Foo::B(_));
 | 
			
		||||
        assert_matches!(b, Foo::B("foo"));
 | 
			
		||||
        assert_matches!(b, Foo::B(s) if s == "foo");
 | 
			
		||||
        assert_matches!(b, Foo::B(s) => assert_eq!(s, "foo"));
 | 
			
		||||
        assert_matches!(b, Foo::B(s) => { assert_eq!(s, "foo"); assert!(true) });
 | 
			
		||||
        assert_matches!(b, Foo::B(s) if s == "foo" => assert_eq!(s, "foo"));
 | 
			
		||||
        assert_matches!(b, Foo::B(s) if s == "foo" => { assert_eq!(s, "foo"); assert!(true) });
 | 
			
		||||
 | 
			
		||||
        let c = Foo::C("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(c, Foo::B(_) | Foo::C(_));
 | 
			
		||||
        assert_matches!(c, Foo::B("foo") | Foo::C("foo"));
 | 
			
		||||
        assert_matches!(c, Foo::B(s) | Foo::C(s) if s == "foo");
 | 
			
		||||
        assert_matches!(c, Foo::B(s) | Foo::C(s) => assert_eq!(s, "foo"));
 | 
			
		||||
        assert_matches!(c, Foo::B(s) | Foo::C(s) => { assert_eq!(s, "foo"); assert!(true) });
 | 
			
		||||
        assert_matches!(c, Foo::B(s) | Foo::C(s) if s == "foo" => assert_eq!(s, "foo"));
 | 
			
		||||
        assert_matches!(c, Foo::B(s) | Foo::C(s) if s == "foo" => { assert_eq!(s, "foo"); assert!(true) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[should_panic]
 | 
			
		||||
    fn test_assert_panic_0() {
 | 
			
		||||
        let a = Foo::A(123);
 | 
			
		||||
 | 
			
		||||
        assert_matches!(a, Foo::B(_));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[should_panic]
 | 
			
		||||
    fn test_assert_panic_1() {
 | 
			
		||||
        let b = Foo::B("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(b, Foo::B("bar"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[should_panic]
 | 
			
		||||
    fn test_assert_panic_2() {
 | 
			
		||||
        let b = Foo::B("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(b, Foo::B(s) if s == "bar");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[should_panic]
 | 
			
		||||
    fn test_assert_panic_3() {
 | 
			
		||||
        let b = Foo::B("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(b, Foo::B(s) => assert_eq!(s, "bar"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[should_panic]
 | 
			
		||||
    fn test_assert_panic_4() {
 | 
			
		||||
        let b = Foo::B("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(b, Foo::B(s) if s == "bar" => assert_eq!(s, "foo"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[should_panic]
 | 
			
		||||
    fn test_assert_panic_5() {
 | 
			
		||||
        let b = Foo::B("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(b, Foo::B(s) if s == "foo" => assert_eq!(s, "bar"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[should_panic]
 | 
			
		||||
    fn test_assert_panic_6() {
 | 
			
		||||
        let b = Foo::B("foo");
 | 
			
		||||
 | 
			
		||||
        assert_matches!(b, Foo::B(s) if s == "foo" => { assert_eq!(s, "foo"); assert!(false) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_assert_no_move() {
 | 
			
		||||
        let b = &mut Foo::A(0);
 | 
			
		||||
        assert_matches!(*b, Foo::A(0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn assert_with_message() {
 | 
			
		||||
        let a = Foo::A(0);
 | 
			
		||||
 | 
			
		||||
        assert_matches!(a, Foo::A(_), "o noes");
 | 
			
		||||
        assert_matches!(a, Foo::A(n) if n == 0, "o noes");
 | 
			
		||||
        assert_matches!(a, Foo::A(n) => assert_eq!(n, 0), "o noes");
 | 
			
		||||
        assert_matches!(a, Foo::A(n) => { assert_eq!(n, 0); assert!(n < 1) }, "o noes");
 | 
			
		||||
        assert_matches!(a, Foo::A(n) if n == 0 => assert_eq!(n, 0), "o noes");
 | 
			
		||||
        assert_matches!(a, Foo::A(n) if n == 0 => { assert_eq!(n, 0); assert!(n < 1) }, "o noes");
 | 
			
		||||
        assert_matches!(a, Foo::A(_), "o noes {:?}", a);
 | 
			
		||||
        assert_matches!(a, Foo::A(n) if n == 0, "o noes {:?}", a);
 | 
			
		||||
        assert_matches!(a, Foo::A(n) => assert_eq!(n, 0), "o noes {:?}", a);
 | 
			
		||||
        assert_matches!(a, Foo::A(n) => { assert_eq!(n, 0); assert!(n < 1) }, "o noes {:?}", a);
 | 
			
		||||
        assert_matches!(a, Foo::A(_), "o noes {value:?}", value=a);
 | 
			
		||||
        assert_matches!(a, Foo::A(n) if n == 0, "o noes {value:?}", value=a);
 | 
			
		||||
        assert_matches!(a, Foo::A(n) => assert_eq!(n, 0), "o noes {value:?}", value=a);
 | 
			
		||||
        assert_matches!(a, Foo::A(n) => { assert_eq!(n, 0); assert!(n < 1) }, "o noes {value:?}", value=a);
 | 
			
		||||
        assert_matches!(a, Foo::A(n) if n == 0 => assert_eq!(n, 0), "o noes {value:?}", value=a);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn panic_message<F>(f: F) -> String
 | 
			
		||||
            where F: FnOnce() + UnwindSafe {
 | 
			
		||||
        let err = catch_unwind(f)
 | 
			
		||||
            .expect_err("function did not panic");
 | 
			
		||||
 | 
			
		||||
        *err.downcast::<String>()
 | 
			
		||||
            .expect("function panicked with non-String value")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_panic_message() {
 | 
			
		||||
        let a = Foo::A(1);
 | 
			
		||||
 | 
			
		||||
        // expr, pat
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(_));
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(_)`"#);
 | 
			
		||||
 | 
			
		||||
        // expr, pat if cond
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(s) if s == "foo");
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`"#);
 | 
			
		||||
 | 
			
		||||
        // expr, pat => arm
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(_) => {});
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(_)`"#);
 | 
			
		||||
 | 
			
		||||
        // expr, pat if cond => arm
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(s) if s == "foo" => {});
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`"#);
 | 
			
		||||
 | 
			
		||||
        // expr, pat, args
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(_), "msg");
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(_)`: msg"#);
 | 
			
		||||
 | 
			
		||||
        // expr, pat if cond, args
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(s) if s == "foo", "msg");
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`: msg"#);
 | 
			
		||||
 | 
			
		||||
        // expr, pat => arm, args
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(_) => {}, "msg");
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(_)`: msg"#);
 | 
			
		||||
 | 
			
		||||
        // expr, pat if cond => arm, args
 | 
			
		||||
        assert_eq!(panic_message(|| {
 | 
			
		||||
            assert_matches!(a, Foo::B(s) if s == "foo" => {}, "msg");
 | 
			
		||||
        }), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`: msg"#);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -10,7 +10,7 @@ use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
 | 
			
		||||
use ::submission::c::Submission as CSubmission;
 | 
			
		||||
 | 
			
		||||
use glib::translate::FromGlibPtrNone;
 | 
			
		||||
use gtk::WidgetExt;
 | 
			
		||||
use gtk::prelude::WidgetExt;
 | 
			
		||||
 | 
			
		||||
use std::collections::HashSet;
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,9 @@ use std::time::Instant;
 | 
			
		||||
use crate::logging::Warn;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Type of the sender that waits for external events
 | 
			
		||||
type Sender = mpsc::Sender<Event>;
 | 
			
		||||
/// Type of the sender that waits for internal state changes
 | 
			
		||||
type UISender = glib::Sender<Commands>;
 | 
			
		||||
 | 
			
		||||
/// This loop driver spawns a new thread which updates the state in a loop,
 | 
			
		||||
 | 
			
		||||
@ -153,6 +153,7 @@ mod test {
 | 
			
		||||
    use crate::imservice::{ ContentHint, ContentPurpose };
 | 
			
		||||
    use crate::main::PanelCommand;
 | 
			
		||||
    use crate::state::{ Application, InputMethod, InputMethodDetails, Presence, visibility };
 | 
			
		||||
    use crate::state::test::application_with_fake_output;
 | 
			
		||||
 | 
			
		||||
    fn imdetails_new() -> InputMethodDetails {
 | 
			
		||||
        InputMethodDetails {
 | 
			
		||||
@ -170,11 +171,12 @@ mod test {
 | 
			
		||||
            im: InputMethod::Active(imdetails_new()),
 | 
			
		||||
            physical_keyboard: Presence::Missing,
 | 
			
		||||
            visibility_override: visibility::State::NotForced,
 | 
			
		||||
            ..application_with_fake_output(start)
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let l = State::new(state, now);
 | 
			
		||||
        let (l, commands) = handle_event(l, InputMethod::InactiveSince(now).into(), now);
 | 
			
		||||
        assert_eq!(commands.panel_visibility, Some(PanelCommand::Show));
 | 
			
		||||
        assert_matches!(commands.panel_visibility, Some(PanelCommand::Show{..}));
 | 
			
		||||
        assert_eq!(l.scheduled_wakeup, Some(now + animation::HIDING_TIMEOUT));
 | 
			
		||||
        
 | 
			
		||||
        now += animation::HIDING_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
@ -26,21 +26,32 @@ pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    use std::os::raw::{c_char, c_void};
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
        
 | 
			
		||||
    /// struct zwp_input_method_v2*
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(PartialEq, Clone, Copy)]
 | 
			
		||||
    pub struct InputMethod(*const c_void);
 | 
			
		||||
 | 
			
		||||
    impl InputMethod {
 | 
			
		||||
        pub fn is_null(&self) -> bool {
 | 
			
		||||
            self.0.is_null()
 | 
			
		||||
        }
 | 
			
		||||
        pub fn null() -> Self {
 | 
			
		||||
            Self(ptr::null())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        fn imservice_destroy_im(im: *mut c::InputMethod);
 | 
			
		||||
        fn imservice_destroy_im(im: 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);
 | 
			
		||||
        pub fn eek_input_method_delete_surrounding_text(im: *mut InputMethod, before: u32, after: u32);
 | 
			
		||||
        pub fn eek_input_method_commit(im: *mut InputMethod, serial: u32);
 | 
			
		||||
        pub fn imservice_connect_listeners(im: InputMethod, imservice: *const IMService);
 | 
			
		||||
        pub fn eek_input_method_commit_string(im: InputMethod, text: *const c_char);
 | 
			
		||||
        pub fn eek_input_method_delete_surrounding_text(im: InputMethod, before: u32, after: u32);
 | 
			
		||||
        pub fn eek_input_method_commit(im: InputMethod, serial: u32);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
			
		||||
@ -49,7 +60,7 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn imservice_handle_input_method_activate(imservice: *mut IMService,
 | 
			
		||||
        im: *const InputMethod)
 | 
			
		||||
        im: InputMethod)
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
        imservice.preedit_string = String::new();
 | 
			
		||||
@ -62,7 +73,7 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn imservice_handle_input_method_deactivate(imservice: *mut IMService,
 | 
			
		||||
        im: *const InputMethod)
 | 
			
		||||
        im: InputMethod)
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
        imservice.pending = IMProtocolState {
 | 
			
		||||
@ -74,7 +85,7 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn imservice_handle_surrounding_text(imservice: *mut IMService,
 | 
			
		||||
        im: *const InputMethod,
 | 
			
		||||
        im: InputMethod,
 | 
			
		||||
        text: *const c_char, cursor: u32, _anchor: u32)
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
@ -90,7 +101,7 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn imservice_handle_content_type(imservice: *mut IMService,
 | 
			
		||||
        im: *const InputMethod,
 | 
			
		||||
        im: InputMethod,
 | 
			
		||||
        hint: u32, purpose: u32)
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
@ -118,7 +129,7 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn imservice_handle_text_change_cause(imservice: *mut IMService,
 | 
			
		||||
        im: *const InputMethod,
 | 
			
		||||
        im: InputMethod,
 | 
			
		||||
        cause: u32)
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
@ -138,16 +149,11 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn imservice_handle_done(imservice: *mut IMService,
 | 
			
		||||
        im: *const InputMethod)
 | 
			
		||||
        im: InputMethod)
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
 | 
			
		||||
        imservice.current = imservice.pending.clone();
 | 
			
		||||
        imservice.pending = IMProtocolState {
 | 
			
		||||
            active: imservice.current.active,
 | 
			
		||||
            ..IMProtocolState::default()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        imservice.serial += Wrapping(1u32);
 | 
			
		||||
        imservice.send_event();
 | 
			
		||||
    }
 | 
			
		||||
@ -156,7 +162,7 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn imservice_handle_unavailable(imservice: *mut IMService,
 | 
			
		||||
        im: *mut InputMethod)
 | 
			
		||||
        im: InputMethod)
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
        unsafe { imservice_destroy_im(im); }
 | 
			
		||||
@ -181,7 +187,7 @@ pub mod c {
 | 
			
		||||
    /// Care must be take
 | 
			
		||||
    /// not to exceed the lifetime of the pointer with the reference,
 | 
			
		||||
    /// especially not to store it.
 | 
			
		||||
    fn check_imservice(imservice: *mut IMService, im: *const InputMethod)
 | 
			
		||||
    fn check_imservice(imservice: *mut IMService, im: InputMethod)
 | 
			
		||||
        -> Result<&'static mut IMService, &'static str>
 | 
			
		||||
    {
 | 
			
		||||
        if imservice.is_null() {
 | 
			
		||||
@ -315,7 +321,7 @@ impl Default for IMProtocolState {
 | 
			
		||||
 | 
			
		||||
pub struct IMService {
 | 
			
		||||
    /// Owned reference (still created and destroyed in C)
 | 
			
		||||
    pub im: *mut c::InputMethod,
 | 
			
		||||
    pub im: c::InputMethod,
 | 
			
		||||
    sender: driver::Threaded,
 | 
			
		||||
 | 
			
		||||
    pending: IMProtocolState,
 | 
			
		||||
@ -331,7 +337,7 @@ pub enum SubmitError {
 | 
			
		||||
 | 
			
		||||
impl IMService {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        im: *mut c::InputMethod,
 | 
			
		||||
        im: c::InputMethod,
 | 
			
		||||
        sender: driver::Threaded,
 | 
			
		||||
    ) -> Box<IMService> {
 | 
			
		||||
        // IMService will be referenced to by C,
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,9 @@ extern crate maplit;
 | 
			
		||||
extern crate serde;
 | 
			
		||||
extern crate xkbcommon;
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
#[macro_use]
 | 
			
		||||
mod assert_matches;
 | 
			
		||||
#[macro_use]
 | 
			
		||||
mod logging;
 | 
			
		||||
 | 
			
		||||
@ -37,6 +40,5 @@ mod style;
 | 
			
		||||
mod submission;
 | 
			
		||||
pub mod tests;
 | 
			
		||||
pub mod util;
 | 
			
		||||
mod ui_manager;
 | 
			
		||||
mod vkeyboard;
 | 
			
		||||
mod xdg;
 | 
			
		||||
 | 
			
		||||
@ -21,11 +21,12 @@ struct rsobjects {
 | 
			
		||||
    struct receiver *receiver;
 | 
			
		||||
    struct squeek_state_manager *state_manager;
 | 
			
		||||
    struct submission *submission;
 | 
			
		||||
    struct squeek_wayland *wayland;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void register_ui_loop_handler(struct receiver *receiver, ServerContextService *ui, DBusHandler *dbus_handler);
 | 
			
		||||
 | 
			
		||||
struct rsobjects squeek_rsobjects_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk);
 | 
			
		||||
struct rsobjects squeek_init(void);
 | 
			
		||||
 | 
			
		||||
void squeek_state_send_force_visible(struct squeek_state_manager *state);
 | 
			
		||||
void squeek_state_send_force_hidden(struct squeek_state_manager *state);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										97
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								src/main.rs
									
									
									
									
									
								
							@ -1,9 +1,9 @@
 | 
			
		||||
/* Copyright (C) 2020 Purism SPC
 | 
			
		||||
/* Copyright (C) 2020,2022 Purism SPC
 | 
			
		||||
 * SPDX-License-Identifier: GPL-3.0+
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! Glue for the main loop. */
 | 
			
		||||
 | 
			
		||||
use crate::outputs::OutputId;
 | 
			
		||||
use crate::state;
 | 
			
		||||
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,15 @@ use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
 | 
			
		||||
mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
    use std::rc::Rc;
 | 
			
		||||
    use std::time::Instant;
 | 
			
		||||
 | 
			
		||||
    use crate::event_loop::driver;
 | 
			
		||||
    use crate::imservice::IMService;
 | 
			
		||||
    use crate::imservice::c::InputMethod;
 | 
			
		||||
    use crate::outputs::Outputs;
 | 
			
		||||
    use crate::outputs::c::WlOutput;
 | 
			
		||||
    use crate::state;
 | 
			
		||||
    use crate::submission::Submission;
 | 
			
		||||
    use crate::util::c::Wrapped;
 | 
			
		||||
@ -33,13 +36,46 @@ mod c {
 | 
			
		||||
    /// Holds the Rust structures that are interesting from C.
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct RsObjects {
 | 
			
		||||
        /// The handle to which Commands should be sent
 | 
			
		||||
        /// for processing in the main loop.
 | 
			
		||||
        receiver: Wrapped<Receiver<Commands>>,
 | 
			
		||||
        state_manager: Wrapped<driver::Threaded>,
 | 
			
		||||
        submission: Wrapped<Submission>,
 | 
			
		||||
        /// Not wrapped, because C needs to access this.
 | 
			
		||||
        wayland: *mut Wayland,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Corresponds to wayland.h::squeek_wayland.
 | 
			
		||||
    /// Fields unused by Rust are marked as generic data types.
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct Wayland {
 | 
			
		||||
        layer_shell: *const c_void,
 | 
			
		||||
        virtual_keyboard_manager: *const c_void,
 | 
			
		||||
        input_method_manager: *const c_void,
 | 
			
		||||
        outputs: Wrapped<Outputs>,
 | 
			
		||||
        seat: *const c_void,
 | 
			
		||||
        input_method: InputMethod,
 | 
			
		||||
        virtual_keyboard: ZwpVirtualKeyboardV1,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Wayland {
 | 
			
		||||
        fn new(outputs_manager: Outputs) -> Self {
 | 
			
		||||
            Wayland {
 | 
			
		||||
                layer_shell: ptr::null(),
 | 
			
		||||
                virtual_keyboard_manager: ptr::null(),
 | 
			
		||||
                input_method_manager: ptr::null(),
 | 
			
		||||
                outputs: Wrapped::new(outputs_manager),
 | 
			
		||||
                seat: ptr::null(),
 | 
			
		||||
                input_method: InputMethod::null(),
 | 
			
		||||
                virtual_keyboard: ZwpVirtualKeyboardV1::null(),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        fn server_context_service_real_show_keyboard(service: *const UIManager);
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        fn init_wayland(wayland: *mut Wayland);
 | 
			
		||||
        fn server_context_service_update_keyboard(service: *const UIManager, output: WlOutput, scaled_height: u32);
 | 
			
		||||
        fn server_context_service_real_hide_keyboard(service: *const UIManager);
 | 
			
		||||
        fn server_context_service_set_hint_purpose(service: *const UIManager, hint: u32, purpose: u32);
 | 
			
		||||
        // This should probably only get called from the gtk main loop,
 | 
			
		||||
@ -52,19 +88,23 @@ mod c {
 | 
			
		||||
    /// and that leads to suffering.
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_rsobjects_new(
 | 
			
		||||
        im: *mut InputMethod,
 | 
			
		||||
        vk: ZwpVirtualKeyboardV1,
 | 
			
		||||
    ) -> RsObjects {
 | 
			
		||||
    fn squeek_init() -> RsObjects {
 | 
			
		||||
        // Set up channels
 | 
			
		||||
        let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT);
 | 
			
		||||
        
 | 
			
		||||
        let now = Instant::now();
 | 
			
		||||
        let state_manager = driver::Threaded::new(sender, state::Application::new(now));
 | 
			
		||||
 | 
			
		||||
        let imservice = if im.is_null() {
 | 
			
		||||
        let outputs = Outputs::new(state_manager.clone());
 | 
			
		||||
        let mut wayland = Box::new(Wayland::new(outputs));
 | 
			
		||||
        let wayland_raw = &mut *wayland as *mut _;
 | 
			
		||||
        unsafe { init_wayland(wayland_raw); }
 | 
			
		||||
 | 
			
		||||
        let vk = wayland.virtual_keyboard;
 | 
			
		||||
 | 
			
		||||
        let imservice = if wayland.input_method.is_null() {
 | 
			
		||||
            None
 | 
			
		||||
        } else {
 | 
			
		||||
            Some(IMService::new(im, state_manager.clone()))
 | 
			
		||||
            Some(IMService::new(wayland.input_method, state_manager.clone()))
 | 
			
		||||
        };
 | 
			
		||||
        let submission = Submission::new(vk, imservice);
 | 
			
		||||
        
 | 
			
		||||
@ -72,6 +112,7 @@ mod c {
 | 
			
		||||
            submission: Wrapped::new(submission),
 | 
			
		||||
            state_manager: Wrapped::new(state_manager),
 | 
			
		||||
            receiver: Wrapped::new(receiver),
 | 
			
		||||
            wayland: Box::into_raw(wayland),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -87,7 +128,7 @@ mod c {
 | 
			
		||||
        let receiver = Rc::try_unwrap(receiver).expect("References still present");
 | 
			
		||||
        let receiver = receiver.into_inner();
 | 
			
		||||
        let ctx = MainContext::default();
 | 
			
		||||
        ctx.acquire();
 | 
			
		||||
        let _acqu = ctx.acquire();
 | 
			
		||||
        receiver.attach(
 | 
			
		||||
            Some(&ctx),
 | 
			
		||||
            move |msg| {
 | 
			
		||||
@ -95,6 +136,7 @@ mod c {
 | 
			
		||||
                Continue(true)
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
        ctx.release();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -108,8 +150,8 @@ mod c {
 | 
			
		||||
        dbus_handler: *const DBusHandler,
 | 
			
		||||
    ) {
 | 
			
		||||
        match msg.panel_visibility {
 | 
			
		||||
            Some(PanelCommand::Show) => unsafe {
 | 
			
		||||
                server_context_service_real_show_keyboard(ui_manager);
 | 
			
		||||
            Some(PanelCommand::Show { output, height }) => unsafe {
 | 
			
		||||
                server_context_service_update_keyboard(ui_manager, output.0, height.as_scaled_ceiling());
 | 
			
		||||
            },
 | 
			
		||||
            Some(PanelCommand::Hide) => unsafe {
 | 
			
		||||
                server_context_service_real_hide_keyboard(ui_manager);
 | 
			
		||||
@ -118,8 +160,10 @@ mod c {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if let Some(visible) = msg.dbus_visible_set {
 | 
			
		||||
            if dbus_handler != std::ptr::null() {
 | 
			
		||||
                unsafe { dbus_handler_set_visible(dbus_handler, visible as u8) };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(hints) = msg.layout_hint_set {
 | 
			
		||||
            unsafe {
 | 
			
		||||
@ -133,9 +177,34 @@ mod c {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Size in pixels that is aware of scaling
 | 
			
		||||
#[derive(Clone, Copy, PartialEq, Debug)]
 | 
			
		||||
pub struct PixelSize {
 | 
			
		||||
    pub pixels: u32,
 | 
			
		||||
    pub scale_factor: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn div_ceil(a: u32, b: u32) -> u32 {
 | 
			
		||||
    // Given that it's for pixels on a screen, an overflow is unlikely.
 | 
			
		||||
    (a + b - 1) / b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PixelSize {
 | 
			
		||||
    pub fn as_scaled_floor(&self) -> u32 {
 | 
			
		||||
        self.pixels / self.scale_factor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_scaled_ceiling(&self) -> u32 {
 | 
			
		||||
        div_ceil(self.pixels, self.scale_factor)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, PartialEq, Debug)]
 | 
			
		||||
pub enum PanelCommand {
 | 
			
		||||
    Show,
 | 
			
		||||
    Show {
 | 
			
		||||
        output: OutputId,
 | 
			
		||||
        height: PixelSize,
 | 
			
		||||
    },
 | 
			
		||||
    Hide,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,8 @@ struct squeek_output_handle {
 | 
			
		||||
 | 
			
		||||
struct squeek_outputs *squeek_outputs_new(void);
 | 
			
		||||
void squeek_outputs_free(struct squeek_outputs*);
 | 
			
		||||
void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output);
 | 
			
		||||
void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output, uint32_t id);
 | 
			
		||||
struct wl_output *squeek_outputs_try_unregister(struct squeek_outputs*, uint32_t id);
 | 
			
		||||
struct squeek_output_handle squeek_outputs_get_current(struct squeek_outputs*);
 | 
			
		||||
int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										211
									
								
								src/outputs.rs
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								src/outputs.rs
									
									
									
									
									
								
							@ -1,6 +1,11 @@
 | 
			
		||||
/* Copyright (C) 2019-2022 Purism SPC
 | 
			
		||||
 * SPDX-License-Identifier: GPL-3.0+
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! Managing Wayland outputs */
 | 
			
		||||
 | 
			
		||||
use std::vec::Vec;
 | 
			
		||||
use crate::event_loop;
 | 
			
		||||
use ::logging;
 | 
			
		||||
 | 
			
		||||
// traits
 | 
			
		||||
@ -11,15 +16,22 @@ pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
 | 
			
		||||
    use ::util::c::COpaquePtr;
 | 
			
		||||
    use ::util::c::{COpaquePtr, Wrapped};
 | 
			
		||||
 | 
			
		||||
    // Defined in C
 | 
			
		||||
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Clone, PartialEq, Copy)]
 | 
			
		||||
    #[derive(Clone, PartialEq, Copy, Debug, Eq, Hash)]
 | 
			
		||||
    pub struct WlOutput(*const c_void);
 | 
			
		||||
 | 
			
		||||
    impl WlOutput {
 | 
			
		||||
        fn null() -> Self {
 | 
			
		||||
            Self(ptr::null())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    struct WlOutputListener<T: COpaquePtr> {
 | 
			
		||||
        geometry: extern fn(
 | 
			
		||||
@ -63,7 +75,7 @@ pub mod c {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Map to `wl_output.transform` values
 | 
			
		||||
    #[derive(Clone)]
 | 
			
		||||
    #[derive(Clone, Copy, Debug)]
 | 
			
		||||
    pub enum Transform {
 | 
			
		||||
        Normal = 0,
 | 
			
		||||
        Rotated90 = 1,
 | 
			
		||||
@ -103,28 +115,13 @@ pub mod c {
 | 
			
		||||
        ) -> i32;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    type COutputs = ::util::c::Wrapped<Outputs>;
 | 
			
		||||
 | 
			
		||||
    /// A stable reference to an output.
 | 
			
		||||
    #[derive(Clone)]
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct OutputHandle {
 | 
			
		||||
        wl_output: WlOutput,
 | 
			
		||||
        outputs: COutputs,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl OutputHandle {
 | 
			
		||||
        // Cannot return an Output reference
 | 
			
		||||
        // because COutputs is too deeply wrapped
 | 
			
		||||
        pub fn get_state(&self) -> Option<OutputState> {
 | 
			
		||||
            let outputs = self.outputs.clone_ref();
 | 
			
		||||
            let outputs = outputs.borrow();
 | 
			
		||||
            find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// Wrapping Outputs is required for calling its methods from C
 | 
			
		||||
    type COutputs = Wrapped<Outputs>;
 | 
			
		||||
 | 
			
		||||
    // Defined in Rust
 | 
			
		||||
 | 
			
		||||
    // Callbacks from the output listener follow
 | 
			
		||||
 | 
			
		||||
    extern fn outputs_handle_geometry(
 | 
			
		||||
        outputs: COutputs,
 | 
			
		||||
        wl_output: WlOutput,
 | 
			
		||||
@ -143,7 +140,8 @@ pub mod c {
 | 
			
		||||
        let outputs = outputs.clone_ref();
 | 
			
		||||
        let mut collection = outputs.borrow_mut();
 | 
			
		||||
        let output_state: Option<&mut OutputState>
 | 
			
		||||
            = find_output_mut(&mut collection, wl_output)
 | 
			
		||||
            = collection
 | 
			
		||||
                .find_output_mut(wl_output)
 | 
			
		||||
                .map(|o| &mut o.pending);
 | 
			
		||||
        match output_state {
 | 
			
		||||
            Some(state) => { state.transform = Some(transform) },
 | 
			
		||||
@ -171,7 +169,8 @@ pub mod c {
 | 
			
		||||
        let outputs = outputs.clone_ref();
 | 
			
		||||
        let mut collection = outputs.borrow_mut();
 | 
			
		||||
        let output_state: Option<&mut OutputState>
 | 
			
		||||
            = find_output_mut(&mut collection, wl_output)
 | 
			
		||||
            = collection
 | 
			
		||||
                .find_output_mut(wl_output)
 | 
			
		||||
                .map(|o| &mut o.pending);
 | 
			
		||||
        match output_state {
 | 
			
		||||
            Some(state) => {
 | 
			
		||||
@ -192,14 +191,27 @@ pub mod c {
 | 
			
		||||
    ) {
 | 
			
		||||
        let outputs = outputs.clone_ref();
 | 
			
		||||
        let mut collection = outputs.borrow_mut();
 | 
			
		||||
        let output = find_output_mut(&mut collection, wl_output);
 | 
			
		||||
        match output {
 | 
			
		||||
            Some(output) => { output.current = output.pending.clone(); }
 | 
			
		||||
            None => log_print!(
 | 
			
		||||
        let output = collection
 | 
			
		||||
            .find_output_mut(wl_output);
 | 
			
		||||
        let event = match output {
 | 
			
		||||
            Some(output) => {
 | 
			
		||||
                output.current = output.pending.clone();
 | 
			
		||||
                Some(Event {
 | 
			
		||||
                    output: OutputId(wl_output),
 | 
			
		||||
                    change: ChangeType::Altered(output.current),
 | 
			
		||||
                })
 | 
			
		||||
            },
 | 
			
		||||
            None => {
 | 
			
		||||
                log_print!(
 | 
			
		||||
                    logging::Level::Warning,
 | 
			
		||||
                    "Got done on unknown output",
 | 
			
		||||
            ),
 | 
			
		||||
                );
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        if let Some(event) = event {
 | 
			
		||||
            collection.send_event(event);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extern fn outputs_handle_scale(
 | 
			
		||||
@ -210,7 +222,8 @@ pub mod c {
 | 
			
		||||
        let outputs = outputs.clone_ref();
 | 
			
		||||
        let mut collection = outputs.borrow_mut();
 | 
			
		||||
        let output_state: Option<&mut OutputState>
 | 
			
		||||
            = find_output_mut(&mut collection, wl_output)
 | 
			
		||||
            = collection
 | 
			
		||||
                .find_output_mut(wl_output)
 | 
			
		||||
                .map(|o| &mut o.pending);
 | 
			
		||||
        match output_state {
 | 
			
		||||
            Some(state) => { state.scale = factor; }
 | 
			
		||||
@ -221,11 +234,7 @@ pub mod c {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_outputs_new() -> COutputs {
 | 
			
		||||
        COutputs::new(Outputs { outputs: Vec::new() })
 | 
			
		||||
    }
 | 
			
		||||
    // End callbacks
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
@ -235,14 +244,17 @@ pub mod c {
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_outputs_register(raw_collection: COutputs, output: WlOutput) {
 | 
			
		||||
    fn squeek_outputs_register(raw_collection: COutputs, output: WlOutput, id: u32) {
 | 
			
		||||
        let collection = raw_collection.clone_ref();
 | 
			
		||||
        let mut collection = collection.borrow_mut();
 | 
			
		||||
        collection.outputs.push(Output {
 | 
			
		||||
        collection.outputs.push((
 | 
			
		||||
            Output {
 | 
			
		||||
                output: output.clone(),
 | 
			
		||||
                pending: OutputState::uninitialized(),
 | 
			
		||||
                current: OutputState::uninitialized(),
 | 
			
		||||
        });
 | 
			
		||||
            },
 | 
			
		||||
            id,
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        unsafe { squeek_output_add_listener(
 | 
			
		||||
            output,
 | 
			
		||||
@ -256,40 +268,22 @@ pub mod c {
 | 
			
		||||
        )};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// This will try to unregister the output, if the id matches a registered one.
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_outputs_get_current(raw_collection: COutputs) -> OutputHandle {
 | 
			
		||||
    fn squeek_outputs_try_unregister(raw_collection: COutputs, id: u32) -> WlOutput {
 | 
			
		||||
        let collection = raw_collection.clone_ref();
 | 
			
		||||
        let collection = collection.borrow();
 | 
			
		||||
        OutputHandle {
 | 
			
		||||
            wl_output: collection.outputs[0].output.clone(),
 | 
			
		||||
            outputs: raw_collection.clone(),
 | 
			
		||||
        }
 | 
			
		||||
        let mut collection = collection.borrow_mut();
 | 
			
		||||
        collection.remove_output_by_global(id)
 | 
			
		||||
            .map_err(|e| log_print!(
 | 
			
		||||
                logging::Level::Debug,
 | 
			
		||||
                "Tried to remove global {:x} but it is not registered as an output: {:?}",
 | 
			
		||||
                id, e,
 | 
			
		||||
            ))
 | 
			
		||||
            .unwrap_or(WlOutput::null())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: handle unregistration
 | 
			
		||||
    
 | 
			
		||||
    fn find_output(
 | 
			
		||||
        collection: &Outputs,
 | 
			
		||||
        wl_output: WlOutput,
 | 
			
		||||
    ) -> Option<&Output> {
 | 
			
		||||
        collection.outputs
 | 
			
		||||
            .iter()
 | 
			
		||||
            .find_map(|o|
 | 
			
		||||
                if o.output == wl_output { Some(o) } else { None }
 | 
			
		||||
            )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn find_output_mut(
 | 
			
		||||
        collection: &mut Outputs,
 | 
			
		||||
        wl_output: WlOutput,
 | 
			
		||||
    ) -> Option<&mut Output> {
 | 
			
		||||
        collection.outputs
 | 
			
		||||
            .iter_mut()
 | 
			
		||||
            .find_map(|o|
 | 
			
		||||
                if o.output == wl_output { Some(o) } else { None }
 | 
			
		||||
            )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generic size
 | 
			
		||||
@ -300,16 +294,16 @@ pub struct Size {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// wl_output mode
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct Mode {
 | 
			
		||||
#[derive(Clone, Copy, Debug)]
 | 
			
		||||
pub struct Mode {
 | 
			
		||||
    width: i32,
 | 
			
		||||
    height: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
#[derive(Clone, Copy, Debug)]
 | 
			
		||||
pub struct OutputState {
 | 
			
		||||
    current_mode: Option<Mode>,
 | 
			
		||||
    transform: Option<c::Transform>,
 | 
			
		||||
    pub current_mode: Option<Mode>,
 | 
			
		||||
    pub transform: Option<c::Transform>,
 | 
			
		||||
    pub scale: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -355,12 +349,83 @@ impl OutputState {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Output {
 | 
			
		||||
/// Not guaranteed to exist,
 | 
			
		||||
/// but can be used to look up state.
 | 
			
		||||
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)]
 | 
			
		||||
pub struct OutputId(pub c::WlOutput);
 | 
			
		||||
 | 
			
		||||
// WlOutput is a pointer,
 | 
			
		||||
// but in the public interface,
 | 
			
		||||
// we're only using it as a lookup key.
 | 
			
		||||
unsafe impl Send for OutputId {}
 | 
			
		||||
 | 
			
		||||
struct Output {
 | 
			
		||||
    output: c::WlOutput,
 | 
			
		||||
    pending: OutputState,
 | 
			
		||||
    current: OutputState,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct NotFound;
 | 
			
		||||
 | 
			
		||||
/// Wayland global ID type
 | 
			
		||||
type GlobalId = u32;
 | 
			
		||||
 | 
			
		||||
/// The outputs manager
 | 
			
		||||
pub struct Outputs {
 | 
			
		||||
    outputs: Vec<Output>,
 | 
			
		||||
    outputs: Vec<(Output, GlobalId)>,
 | 
			
		||||
    sender: event_loop::driver::Threaded,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Outputs {
 | 
			
		||||
    pub fn new(sender: event_loop::driver::Threaded) -> Outputs {
 | 
			
		||||
        Outputs {
 | 
			
		||||
            outputs: Vec::new(),
 | 
			
		||||
            sender,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn send_event(&self, event: Event) {
 | 
			
		||||
        self.sender.send(event.into()).unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn remove_output_by_global(&mut self, id: GlobalId)
 | 
			
		||||
        -> Result<c::WlOutput, NotFound>
 | 
			
		||||
    {
 | 
			
		||||
        let index = self.outputs.iter()
 | 
			
		||||
            .position(|(_o, global_id)| *global_id == id);
 | 
			
		||||
        if let Some(index) = index {
 | 
			
		||||
            let (output, _id) = self.outputs.remove(index);
 | 
			
		||||
            self.send_event(Event {
 | 
			
		||||
                change: ChangeType::Removed,
 | 
			
		||||
                output: OutputId(output.output),
 | 
			
		||||
            });
 | 
			
		||||
            Ok(output.output)
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(NotFound)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn find_output_mut(&mut self, wl_output: c::WlOutput)
 | 
			
		||||
        -> Option<&mut Output>
 | 
			
		||||
    {
 | 
			
		||||
        self.outputs
 | 
			
		||||
            .iter_mut()
 | 
			
		||||
            .find_map(|(o, _global)|
 | 
			
		||||
                if o.output == wl_output { Some(o) } else { None }
 | 
			
		||||
            )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug)]
 | 
			
		||||
pub enum ChangeType {
 | 
			
		||||
    /// Added or changed
 | 
			
		||||
    Altered(OutputState),
 | 
			
		||||
    Removed,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug)]
 | 
			
		||||
pub struct Event {
 | 
			
		||||
    pub output: OutputId,
 | 
			
		||||
    pub change: ChangeType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,16 +11,11 @@ use ::manager;
 | 
			
		||||
use ::resources;
 | 
			
		||||
 | 
			
		||||
// Traits
 | 
			
		||||
use gio::ActionMapExt;
 | 
			
		||||
use gio::SettingsExt;
 | 
			
		||||
#[cfg(feature = "gio_v0_5")]
 | 
			
		||||
use gio::SimpleActionExt;
 | 
			
		||||
use gio::prelude::ActionMapExt;
 | 
			
		||||
use gio::prelude::SettingsExt;
 | 
			
		||||
use glib::translate::FromGlibPtrNone;
 | 
			
		||||
use glib::variant::ToVariant;
 | 
			
		||||
#[cfg(not(feature = "gtk_v0_5"))]
 | 
			
		||||
use gtk::prelude::*;
 | 
			
		||||
use gtk::PopoverExt;
 | 
			
		||||
use gtk::WidgetExt;
 | 
			
		||||
use ::logging::Warn;
 | 
			
		||||
 | 
			
		||||
mod c {
 | 
			
		||||
@ -110,8 +105,13 @@ mod variants {
 | 
			
		||||
 | 
			
		||||
fn get_settings(schema_name: &str) -> Option<gio::Settings> {
 | 
			
		||||
    let mut error_handler = logging::Print{};
 | 
			
		||||
    gio::SettingsSchemaSource::get_default()
 | 
			
		||||
        .or_warn(
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
    let ss = gio::SettingsSchemaSource::default();
 | 
			
		||||
    #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
    let ss = gio::SettingsSchemaSource::get_default();
 | 
			
		||||
    
 | 
			
		||||
    ss.or_warn(
 | 
			
		||||
            &mut error_handler,
 | 
			
		||||
            logging::Problem::Surprise,
 | 
			
		||||
            "No gsettings schemas installed.",
 | 
			
		||||
@ -130,7 +130,11 @@ fn get_settings(schema_name: &str) -> Option<gio::Settings> {
 | 
			
		||||
fn set_layout(kind: String, name: String) {
 | 
			
		||||
    let settings = get_settings("org.gnome.desktop.input-sources");
 | 
			
		||||
    if let Some(settings) = settings {
 | 
			
		||||
        #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
        let inputs = settings.value("sources");
 | 
			
		||||
        #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
        let inputs = settings.get_value("sources").unwrap();
 | 
			
		||||
 | 
			
		||||
        let current = (kind.clone(), name.clone());
 | 
			
		||||
        let inputs = variants::get_tuples(inputs).into_iter()
 | 
			
		||||
            .filter(|t| t != ¤t);
 | 
			
		||||
@ -254,7 +258,11 @@ pub fn show(
 | 
			
		||||
    let settings = get_settings("org.gnome.desktop.input-sources");
 | 
			
		||||
    let inputs = settings
 | 
			
		||||
        .map(|settings| {
 | 
			
		||||
            #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
            let inputs = settings.value("sources");
 | 
			
		||||
            #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
            let inputs = settings.get_value("sources").unwrap();
 | 
			
		||||
 | 
			
		||||
            variants::get_tuples(inputs)
 | 
			
		||||
        })
 | 
			
		||||
        .unwrap_or_else(|| Vec::new());
 | 
			
		||||
@ -285,8 +293,18 @@ pub fn show(
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let model: gio::Menu = {
 | 
			
		||||
        #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
        {
 | 
			
		||||
            let builder = gtk::Builder::from_resource("/sm/puri/squeekboard/popover.ui");
 | 
			
		||||
            builder.object("app-menu").unwrap()
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
        {
 | 
			
		||||
            let builder = gtk::Builder::new_from_resource("/sm/puri/squeekboard/popover.ui");
 | 
			
		||||
    let model: gio::Menu = builder.get_object("app-menu").unwrap();
 | 
			
		||||
            builder.get_object("app-menu").unwrap()
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (tr, l) in human_names.iter().rev() {
 | 
			
		||||
        let detailed_action = format!("layout::{}", l.get_name());
 | 
			
		||||
@ -294,7 +312,11 @@ pub fn show(
 | 
			
		||||
        model.prepend_item (&item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
    let menu = gtk::Popover::from_model(Some(&window), &model);
 | 
			
		||||
    #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
    let menu = gtk::Popover::new_from_model(Some(&window), &model);
 | 
			
		||||
 | 
			
		||||
    menu.set_pointing_to(>k::Rectangle {
 | 
			
		||||
        x: position.x.ceil() as i32,
 | 
			
		||||
        y: position.y.ceil() as i32,
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,9 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
 | 
			
		||||
    ("pl", include_str!("../data/keyboards/pl.yaml")),
 | 
			
		||||
    ("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("ro", include_str!("../data/keyboards/ro.yaml")),
 | 
			
		||||
    ("ro_wide", include_str!("../data/keyboards/ro_wide.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("ru", include_str!("../data/keyboards/ru.yaml")),
 | 
			
		||||
 | 
			
		||||
    ("se", include_str!("../data/keyboards/se.yaml")),
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@
 | 
			
		||||
#include "submission.h"
 | 
			
		||||
#include "wayland.h"
 | 
			
		||||
#include "server-context-service.h"
 | 
			
		||||
#include "wayland-client-protocol.h"
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    PROP_0,
 | 
			
		||||
@ -41,11 +42,12 @@ struct _ServerContextService {
 | 
			
		||||
    /// Needed for instantiating the widget
 | 
			
		||||
    struct submission *submission; // unowned
 | 
			
		||||
    struct squeek_layout_state *layout;
 | 
			
		||||
    struct ui_manager *manager; // unowned
 | 
			
		||||
    struct squeek_state_manager *state_manager; // shared reference
 | 
			
		||||
 | 
			
		||||
    PhoshLayerSurface *window;
 | 
			
		||||
    GtkWidget *widget; // nullable
 | 
			
		||||
 | 
			
		||||
    struct wl_output *current_output;
 | 
			
		||||
    guint last_requested_height;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -64,96 +66,17 @@ on_destroy (ServerContextService *self, GtkWidget *widget)
 | 
			
		||||
    //eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t
 | 
			
		||||
calculate_height(int32_t width, GdkRectangle *geometry)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t height;
 | 
			
		||||
    if (geometry->width > geometry->height) {
 | 
			
		||||
        // 1:5 ratio works fine on lanscape mode, and makes sure there's
 | 
			
		||||
        // room left for the app window
 | 
			
		||||
        height = width / 5;
 | 
			
		||||
    } else {
 | 
			
		||||
        if (width < 540 && width > 0) {
 | 
			
		||||
            height = ((unsigned)width * 7 / 12); // to match 360×210
 | 
			
		||||
        } else {
 | 
			
		||||
            // Here we switch to wide layout, less height needed
 | 
			
		||||
            height = ((unsigned)width * 7 / 22);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
 | 
			
		||||
{
 | 
			
		||||
    GdkDisplay *display = NULL;
 | 
			
		||||
    GdkWindow *window = NULL;
 | 
			
		||||
    GdkMonitor *monitor = NULL;
 | 
			
		||||
    GdkRectangle geometry;
 | 
			
		||||
    gint width;
 | 
			
		||||
    gint height;
 | 
			
		||||
 | 
			
		||||
    g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
 | 
			
		||||
    g_return_if_fail (PHOSH_IS_LAYER_SURFACE (surface));
 | 
			
		||||
 | 
			
		||||
    g_object_get(G_OBJECT(surface),
 | 
			
		||||
                 "configured-width", &width,
 | 
			
		||||
                 "configured-height", &height,
 | 
			
		||||
                 NULL);
 | 
			
		||||
 | 
			
		||||
    // In order to improve height calculation, we need the monitor geometry so
 | 
			
		||||
    // we can use different algorithms for portrait and landscape mode.
 | 
			
		||||
    // Note: this is a temporary fix until the size manager is complete.
 | 
			
		||||
    display = gdk_display_get_default ();
 | 
			
		||||
    if (display) {
 | 
			
		||||
        window = gtk_widget_get_window (GTK_WIDGET (surface));
 | 
			
		||||
    }
 | 
			
		||||
    if (window) {
 | 
			
		||||
        monitor = gdk_display_get_monitor_at_window (display, window);
 | 
			
		||||
    }
 | 
			
		||||
    if (monitor) {
 | 
			
		||||
        gdk_monitor_get_geometry (monitor, &geometry);
 | 
			
		||||
    } else {
 | 
			
		||||
        geometry.width = geometry.height = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
     // When the geometry event comes after surface.configure,
 | 
			
		||||
     // this entire height calculation does nothing.
 | 
			
		||||
     // guint desired_height = squeek_uiman_get_perceptual_height(context->manager);
 | 
			
		||||
     // Temporarily use old method, until the size manager is complete.
 | 
			
		||||
    guint desired_height = calculate_height(width, &geometry);
 | 
			
		||||
 | 
			
		||||
    guint configured_height = (guint)height;
 | 
			
		||||
    // if height was already requested once but a different one was given
 | 
			
		||||
    // (for the same set of surrounding properties),
 | 
			
		||||
    // then it's probably not reasonable to ask for it again,
 | 
			
		||||
    // as it's likely to create pointless loops
 | 
			
		||||
    // of request->reject->request_again->...
 | 
			
		||||
    if (desired_height != configured_height
 | 
			
		||||
            && self->last_requested_height != desired_height) {
 | 
			
		||||
        self->last_requested_height = desired_height;
 | 
			
		||||
        phosh_layer_surface_set_size(surface, 0,
 | 
			
		||||
                                     (gint)desired_height);
 | 
			
		||||
        phosh_layer_surface_set_exclusive_zone(surface, (gint)desired_height);
 | 
			
		||||
        phosh_layer_surface_wl_surface_commit (surface);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
make_window (ServerContextService *self)
 | 
			
		||||
make_window (ServerContextService *self, struct wl_output *output, uint32_t height)
 | 
			
		||||
{
 | 
			
		||||
    if (self->window) {
 | 
			
		||||
        g_error("Window already present");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct squeek_output_handle output = squeek_outputs_get_current(squeek_wayland->outputs);
 | 
			
		||||
    squeek_uiman_set_output(self->manager, output);
 | 
			
		||||
    uint32_t height = squeek_uiman_get_perceptual_height(self->manager);
 | 
			
		||||
 | 
			
		||||
    self->window = g_object_new (
 | 
			
		||||
        PHOSH_TYPE_LAYER_SURFACE,
 | 
			
		||||
        "layer-shell", squeek_wayland->layer_shell,
 | 
			
		||||
        "wl-output", output.output,
 | 
			
		||||
        "wl-output", output,
 | 
			
		||||
        "height", height,
 | 
			
		||||
        "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
 | 
			
		||||
                  | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
 | 
			
		||||
@ -167,7 +90,7 @@ make_window (ServerContextService *self)
 | 
			
		||||
 | 
			
		||||
    g_object_connect (self->window,
 | 
			
		||||
                      "swapped-signal::destroy", G_CALLBACK(on_destroy), self,
 | 
			
		||||
                      "swapped-signal::configured", G_CALLBACK(on_surface_configure), self,
 | 
			
		||||
                      //"swapped-signal::configured", G_CALLBACK(on_surface_configure), self,
 | 
			
		||||
                      NULL);
 | 
			
		||||
 | 
			
		||||
    // The properties below are just to make hacking easier.
 | 
			
		||||
@ -204,11 +127,50 @@ make_widget (ServerContextService *self)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Called from rust
 | 
			
		||||
/// Updates the type of hiddenness
 | 
			
		||||
void
 | 
			
		||||
server_context_service_real_show_keyboard (ServerContextService *self)
 | 
			
		||||
server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    //self->desired_height = 0;
 | 
			
		||||
    self->current_output = NULL;
 | 
			
		||||
    if (self->window) {
 | 
			
		||||
        gtk_widget_hide (GTK_WIDGET(self->window));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Called from rust
 | 
			
		||||
/// Updates the type of visibility.
 | 
			
		||||
/// Height is in scaled units.
 | 
			
		||||
void
 | 
			
		||||
server_context_service_update_keyboard (ServerContextService *self, struct wl_output *output, uint32_t scaled_height)
 | 
			
		||||
{
 | 
			
		||||
    if (output != self->current_output) {
 | 
			
		||||
        // Recreate on a new output
 | 
			
		||||
        server_context_service_real_hide_keyboard(self);
 | 
			
		||||
    } else {
 | 
			
		||||
        gint h;
 | 
			
		||||
        PhoshLayerSurface *surface = self->window;
 | 
			
		||||
        g_object_get(G_OBJECT(surface),
 | 
			
		||||
                     "configured-height", &h,
 | 
			
		||||
                     NULL);
 | 
			
		||||
 | 
			
		||||
        if ((uint32_t)h != scaled_height) {
 | 
			
		||||
 | 
			
		||||
            //TODO: make sure that redrawing happens in the correct place (it doesn't now).
 | 
			
		||||
            phosh_layer_surface_set_size(self->window, 0, scaled_height);
 | 
			
		||||
            phosh_layer_surface_set_exclusive_zone(self->window, scaled_height);
 | 
			
		||||
            phosh_layer_surface_wl_surface_commit(self->window);
 | 
			
		||||
 | 
			
		||||
            self->current_output = output;
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self->current_output = output;
 | 
			
		||||
 | 
			
		||||
    if (!self->window) {
 | 
			
		||||
        make_window (self);
 | 
			
		||||
        make_window (self, output, scaled_height);
 | 
			
		||||
    }
 | 
			
		||||
    if (!self->widget) {
 | 
			
		||||
        make_widget (self);
 | 
			
		||||
@ -216,14 +178,6 @@ server_context_service_real_show_keyboard (ServerContextService *self)
 | 
			
		||||
    gtk_widget_show (GTK_WIDGET(self->window));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Called from rust
 | 
			
		||||
void
 | 
			
		||||
server_context_service_real_hide_keyboard (ServerContextService *self)
 | 
			
		||||
{
 | 
			
		||||
    if (self->window) {
 | 
			
		||||
	    gtk_widget_hide (GTK_WIDGET(self->window));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
server_context_service_set_property (GObject      *object,
 | 
			
		||||
@ -318,13 +272,12 @@ init (ServerContextService *self) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ServerContextService *
 | 
			
		||||
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman,  struct squeek_state_manager *state_manager)
 | 
			
		||||
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct squeek_state_manager *state_manager)
 | 
			
		||||
{
 | 
			
		||||
    ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
 | 
			
		||||
    ui->submission = submission;
 | 
			
		||||
    ui->state = self;
 | 
			
		||||
    ui->layout = layout;
 | 
			
		||||
    ui->manager = uiman;
 | 
			
		||||
    ui->state_manager = state_manager;
 | 
			
		||||
    init(ui);
 | 
			
		||||
    return ui;
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,6 @@
 | 
			
		||||
 | 
			
		||||
#include "src/layout.h"
 | 
			
		||||
#include "src/submission.h"
 | 
			
		||||
#include "ui_manager.h"
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
@ -29,10 +28,8 @@ G_BEGIN_DECLS
 | 
			
		||||
/** Manages the lifecycle of the window displaying layouts. */
 | 
			
		||||
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject)
 | 
			
		||||
 | 
			
		||||
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct squeek_state_manager *state_manager);
 | 
			
		||||
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct squeek_state_manager *state_manager);
 | 
			
		||||
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
 | 
			
		||||
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 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,6 @@
 | 
			
		||||
#include "outputs.h"
 | 
			
		||||
#include "submission.h"
 | 
			
		||||
#include "server-context-service.h"
 | 
			
		||||
#include "ui_manager.h"
 | 
			
		||||
#include "wayland.h"
 | 
			
		||||
 | 
			
		||||
#include <gdk/gdkwayland.h>
 | 
			
		||||
@ -56,8 +55,6 @@ struct squeekboard {
 | 
			
		||||
    ServerContextService *ui_context; // mess, includes the entire UI
 | 
			
		||||
    /// Currently wanted layout. TODO: merge into state::Application
 | 
			
		||||
    struct squeek_layout_state layout_choice;
 | 
			
		||||
    /// UI shape tracker/chooser. TODO: merge into state::Application
 | 
			
		||||
    struct ui_manager *ui_manager;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -112,34 +109,38 @@ registry_handle_global (void *data,
 | 
			
		||||
    // Even when lower version would be served, it would not be supported,
 | 
			
		||||
    // causing a hard exit
 | 
			
		||||
    (void)version;
 | 
			
		||||
    struct squeekboard *instance = data;
 | 
			
		||||
    struct squeek_wayland *wayland = data;
 | 
			
		||||
 | 
			
		||||
    if (!strcmp (interface, zwlr_layer_shell_v1_interface.name)) {
 | 
			
		||||
        instance->wayland.layer_shell = wl_registry_bind (registry, name,
 | 
			
		||||
        wayland->layer_shell = wl_registry_bind (registry, name,
 | 
			
		||||
            &zwlr_layer_shell_v1_interface, 1);
 | 
			
		||||
    } else if (!strcmp (interface, zwp_virtual_keyboard_manager_v1_interface.name)) {
 | 
			
		||||
        instance->wayland.virtual_keyboard_manager = wl_registry_bind(registry, name,
 | 
			
		||||
        wayland->virtual_keyboard_manager = wl_registry_bind(registry, name,
 | 
			
		||||
            &zwp_virtual_keyboard_manager_v1_interface, 1);
 | 
			
		||||
    } else if (!strcmp (interface, zwp_input_method_manager_v2_interface.name)) {
 | 
			
		||||
        instance->wayland.input_method_manager = wl_registry_bind(registry, name,
 | 
			
		||||
        wayland->input_method_manager = wl_registry_bind(registry, name,
 | 
			
		||||
            &zwp_input_method_manager_v2_interface, 1);
 | 
			
		||||
    } else if (!strcmp (interface, "wl_output")) {
 | 
			
		||||
        struct wl_output *output = wl_registry_bind (registry, name,
 | 
			
		||||
            &wl_output_interface, 2);
 | 
			
		||||
        squeek_outputs_register(instance->wayland.outputs, output);
 | 
			
		||||
        squeek_outputs_register(wayland->outputs, output, name);
 | 
			
		||||
    } else if (!strcmp(interface, "wl_seat")) {
 | 
			
		||||
        instance->wayland.seat = wl_registry_bind(registry, name,
 | 
			
		||||
        wayland->seat = wl_registry_bind(registry, name,
 | 
			
		||||
            &wl_seat_interface, 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
registry_handle_global_remove (void *data,
 | 
			
		||||
                               struct wl_registry *registry,
 | 
			
		||||
                               uint32_t name)
 | 
			
		||||
{
 | 
			
		||||
  // TODO
 | 
			
		||||
    (void)registry;
 | 
			
		||||
    struct squeek_wayland *wayland = data;
 | 
			
		||||
    struct wl_output *output = squeek_outputs_try_unregister(wayland->outputs, name);
 | 
			
		||||
    if (output) {
 | 
			
		||||
        wl_output_destroy(output);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct wl_registry_listener registry_listener = {
 | 
			
		||||
@ -147,6 +148,54 @@ static const struct wl_registry_listener registry_listener = {
 | 
			
		||||
  registry_handle_global_remove
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void init_wayland(struct squeek_wayland *wayland) {
 | 
			
		||||
    // Set up Wayland
 | 
			
		||||
    gdk_set_allowed_backends ("wayland");
 | 
			
		||||
    GdkDisplay *gdk_display = gdk_display_get_default ();
 | 
			
		||||
    struct wl_display *display = gdk_wayland_display_get_wl_display (gdk_display);
 | 
			
		||||
 | 
			
		||||
    if (display == NULL) {
 | 
			
		||||
        g_error ("Failed to get display: %m\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct wl_registry *registry = wl_display_get_registry (display);
 | 
			
		||||
    wl_registry_add_listener (registry, ®istry_listener, wayland);
 | 
			
		||||
    wl_display_roundtrip(display); // wait until the registry is actually populated
 | 
			
		||||
 | 
			
		||||
    if (!wayland->seat) {
 | 
			
		||||
        g_error("No seat Wayland global available.");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    if (!wayland->virtual_keyboard_manager) {
 | 
			
		||||
        g_error("No virtual keyboard manager Wayland global available.");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    if (!wayland->layer_shell) {
 | 
			
		||||
        g_error("No layer shell global available.");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!wayland->input_method_manager) {
 | 
			
		||||
        g_warning("Wayland input method interface not available");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (wayland->input_method_manager) {
 | 
			
		||||
        wayland->input_method = zwp_input_method_manager_v2_get_input_method(
 | 
			
		||||
            wayland->input_method_manager,
 | 
			
		||||
            wayland->seat);
 | 
			
		||||
    }
 | 
			
		||||
    if (wayland->virtual_keyboard_manager) {
 | 
			
		||||
        wayland->virtual_keyboard = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
 | 
			
		||||
            wayland->virtual_keyboard_manager,
 | 
			
		||||
            wayland->seat);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // initialize global
 | 
			
		||||
    squeek_wayland = wayland;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SESSION_NAME "sm.puri.OSK0"
 | 
			
		||||
 | 
			
		||||
GDBusProxy *_proxy = NULL;
 | 
			
		||||
@ -284,22 +333,6 @@ phosh_theme_init (void)
 | 
			
		||||
    g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", TRUE, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create Rust objects in one go,
 | 
			
		||||
/// to avoid crossing the language barrier and losing type information
 | 
			
		||||
static struct rsobjects create_rsobjects(struct zwp_input_method_manager_v2 *immanager,
 | 
			
		||||
                                         struct zwp_virtual_keyboard_manager_v1 *vkmanager,
 | 
			
		||||
                                         struct wl_seat *seat) {
 | 
			
		||||
    struct zwp_input_method_v2 *im = NULL;
 | 
			
		||||
    if (immanager) {
 | 
			
		||||
        im = zwp_input_method_manager_v2_get_input_method(immanager, seat);
 | 
			
		||||
    }
 | 
			
		||||
    struct zwp_virtual_keyboard_v1 *vk = NULL;
 | 
			
		||||
    if (vkmanager) {
 | 
			
		||||
        vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmanager, seat);
 | 
			
		||||
    }
 | 
			
		||||
    return squeek_rsobjects_new(im, vk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GDebugKey debug_keys[] =
 | 
			
		||||
{
 | 
			
		||||
        { .key = "force-show",
 | 
			
		||||
@ -359,46 +392,10 @@ main (int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
    phosh_theme_init ();
 | 
			
		||||
 | 
			
		||||
    // Set up Wayland
 | 
			
		||||
    gdk_set_allowed_backends ("wayland");
 | 
			
		||||
    GdkDisplay *gdk_display = gdk_display_get_default ();
 | 
			
		||||
    struct wl_display *display = gdk_wayland_display_get_wl_display (gdk_display);
 | 
			
		||||
 | 
			
		||||
    if (display == NULL) {
 | 
			
		||||
        g_error ("Failed to get display: %m\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    struct squeekboard instance = {0};
 | 
			
		||||
    squeek_wayland_init (&instance.wayland);
 | 
			
		||||
    struct wl_registry *registry = wl_display_get_registry (display);
 | 
			
		||||
    wl_registry_add_listener (registry, ®istry_listener, &instance);
 | 
			
		||||
    wl_display_roundtrip(display); // wait until the registry is actually populated
 | 
			
		||||
    squeek_wayland_set_global(&instance.wayland);
 | 
			
		||||
 | 
			
		||||
    if (!instance.wayland.seat) {
 | 
			
		||||
        g_error("No seat Wayland global available.");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    if (!instance.wayland.virtual_keyboard_manager) {
 | 
			
		||||
        g_error("No virtual keyboard manager Wayland global available.");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    if (!instance.wayland.layer_shell) {
 | 
			
		||||
        g_error("No layer shell global available.");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!instance.wayland.input_method_manager) {
 | 
			
		||||
        g_warning("Wayland input method interface not available");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct rsobjects rsobjects = create_rsobjects(instance.wayland.input_method_manager,
 | 
			
		||||
        instance.wayland.virtual_keyboard_manager,
 | 
			
		||||
        instance.wayland.seat);
 | 
			
		||||
 | 
			
		||||
    instance.ui_manager = squeek_uiman_new();
 | 
			
		||||
    // Also initializes wayland
 | 
			
		||||
    struct rsobjects rsobjects = squeek_init();
 | 
			
		||||
 | 
			
		||||
    instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
 | 
			
		||||
 | 
			
		||||
@ -444,7 +441,6 @@ main (int argc, char **argv)
 | 
			
		||||
                instance.settings_context,
 | 
			
		||||
                rsobjects.submission,
 | 
			
		||||
                &instance.layout_choice,
 | 
			
		||||
                instance.ui_manager,
 | 
			
		||||
                rsobjects.state_manager);
 | 
			
		||||
    if (!ui_context) {
 | 
			
		||||
        g_error("Could not initialize GUI");
 | 
			
		||||
@ -477,6 +473,5 @@ main (int argc, char **argv)
 | 
			
		||||
    }
 | 
			
		||||
    g_main_loop_unref (loop);
 | 
			
		||||
 | 
			
		||||
    squeek_wayland_deinit (&instance.wayland);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										153
									
								
								src/state.rs
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								src/state.rs
									
									
									
									
									
								
							@ -7,10 +7,13 @@
 | 
			
		||||
 | 
			
		||||
use crate::animation;
 | 
			
		||||
use crate::imservice::{ ContentHint, ContentPurpose };
 | 
			
		||||
use crate::main::{ Commands, PanelCommand };
 | 
			
		||||
use crate::main::{ Commands, PanelCommand, PixelSize };
 | 
			
		||||
use crate::outputs;
 | 
			
		||||
use crate::outputs::{OutputId, OutputState};
 | 
			
		||||
use std::cmp;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::time::Instant;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Presence {
 | 
			
		||||
    Present,
 | 
			
		||||
@ -29,12 +32,14 @@ pub enum InputMethod {
 | 
			
		||||
    InactiveSince(Instant),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Incoming events
 | 
			
		||||
/// Incoming events.
 | 
			
		||||
/// This contains events that cause a change to the internal state.
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub enum Event {
 | 
			
		||||
    InputMethod(InputMethod),
 | 
			
		||||
    Visibility(visibility::Event),
 | 
			
		||||
    PhysicalKeyboard(Presence),
 | 
			
		||||
    Output(outputs::Event),
 | 
			
		||||
    /// Event triggered because a moment in time passed.
 | 
			
		||||
    /// Use to animate state transitions.
 | 
			
		||||
    /// The value is the ideal arrival time.
 | 
			
		||||
@ -47,6 +52,12 @@ impl From<InputMethod> for Event {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<outputs::Event> for Event {
 | 
			
		||||
    fn from(ev: outputs::Event) -> Self {
 | 
			
		||||
        Self::Output(ev)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub mod visibility {
 | 
			
		||||
    #[derive(Clone)]
 | 
			
		||||
    pub enum Event {
 | 
			
		||||
@ -83,12 +94,12 @@ impl Outcome {
 | 
			
		||||
    pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
 | 
			
		||||
        let layout_hint_set = match new_state {
 | 
			
		||||
            Outcome {
 | 
			
		||||
                visibility: animation::Outcome::Visible,
 | 
			
		||||
                visibility: animation::Outcome::Visible{..},
 | 
			
		||||
                im: InputMethod::Active(hints),
 | 
			
		||||
            } => Some(hints.clone()),
 | 
			
		||||
            
 | 
			
		||||
            Outcome {
 | 
			
		||||
                visibility: animation::Outcome::Visible,
 | 
			
		||||
                visibility: animation::Outcome::Visible{..},
 | 
			
		||||
                im: InputMethod::InactiveSince(_),
 | 
			
		||||
            } => Some(InputMethodDetails {
 | 
			
		||||
                hint: ContentHint::NONE,
 | 
			
		||||
@ -100,9 +111,10 @@ impl Outcome {
 | 
			
		||||
                ..
 | 
			
		||||
            } => None,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
// FIXME: handle switching outputs
 | 
			
		||||
        let (dbus_visible_set, panel_visibility) = match new_state.visibility {
 | 
			
		||||
            animation::Outcome::Visible => (Some(true), Some(PanelCommand::Show)),
 | 
			
		||||
            animation::Outcome::Visible{output, height}
 | 
			
		||||
                => (Some(true), Some(PanelCommand::Show{output, height})),
 | 
			
		||||
            animation::Outcome::Hidden => (Some(false), Some(PanelCommand::Hide)),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@ -130,6 +142,13 @@ pub struct Application {
 | 
			
		||||
    pub im: InputMethod,
 | 
			
		||||
    pub visibility_override: visibility::State,
 | 
			
		||||
    pub physical_keyboard: Presence,
 | 
			
		||||
    /// The output on which the panel should appear.
 | 
			
		||||
    /// This is stored as part of the state
 | 
			
		||||
    /// because it's not clear how to derive the output from the rest of the state.
 | 
			
		||||
    /// It should probably follow the focused input,
 | 
			
		||||
    /// but not sure about being allowed on non-touch displays.
 | 
			
		||||
    pub preferred_output: Option<OutputId>,
 | 
			
		||||
    pub outputs: HashMap<OutputId, OutputState>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Application {
 | 
			
		||||
@ -144,6 +163,8 @@ impl Application {
 | 
			
		||||
            im: InputMethod::InactiveSince(now),
 | 
			
		||||
            visibility_override: visibility::State::NotForced,
 | 
			
		||||
            physical_keyboard: Presence::Missing,
 | 
			
		||||
            preferred_output: None,
 | 
			
		||||
            outputs: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -164,6 +185,25 @@ impl Application {
 | 
			
		||||
                ..self
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            Event::Output(outputs::Event { output, change }) => {
 | 
			
		||||
                let mut app = self;
 | 
			
		||||
                match change {
 | 
			
		||||
                    outputs::ChangeType::Altered(state) => {
 | 
			
		||||
                        app.outputs.insert(output, state);
 | 
			
		||||
                        app.preferred_output = app.preferred_output.or(Some(output));
 | 
			
		||||
                    },
 | 
			
		||||
                    outputs::ChangeType::Removed => {
 | 
			
		||||
                        app.outputs.remove(&output);
 | 
			
		||||
                        if app.preferred_output == Some(output) {
 | 
			
		||||
                            // There's currently no policy to choose one output over another,
 | 
			
		||||
                            // so just take whichever comes first.
 | 
			
		||||
                            app.preferred_output = app.outputs.keys().next().map(|output| *output);
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                };
 | 
			
		||||
                app
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            Event::InputMethod(new_im) => match (self.im.clone(), new_im) {
 | 
			
		||||
                (InputMethod::Active(_old), InputMethod::Active(new_im))
 | 
			
		||||
                => Self {
 | 
			
		||||
@ -198,20 +238,60 @@ impl Application {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_preferred_height(output: &OutputState) -> Option<PixelSize> {
 | 
			
		||||
        output.get_pixel_size()
 | 
			
		||||
            .map(|px_size| {
 | 
			
		||||
                let height = {
 | 
			
		||||
                    if px_size.width > px_size.height {
 | 
			
		||||
                        px_size.width / 5
 | 
			
		||||
                    } else {
 | 
			
		||||
                        let abstract_width
 | 
			
		||||
                            = PixelSize {
 | 
			
		||||
                                scale_factor: output.scale as u32,
 | 
			
		||||
                                pixels: px_size.width,
 | 
			
		||||
                            } 
 | 
			
		||||
                            .as_scaled_ceiling();
 | 
			
		||||
                        if (abstract_width < 540) && (px_size.width > 0) {
 | 
			
		||||
                            px_size.width * 7 / 12 // to match 360×210
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // Here we switch to wide layout, less height needed
 | 
			
		||||
                            px_size.width * 7 / 22
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                PixelSize {
 | 
			
		||||
                    scale_factor: output.scale as u32,
 | 
			
		||||
                    pixels: cmp::min(height, px_size.height / 2),
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_outcome(&self, now: Instant) -> Outcome {
 | 
			
		||||
        // FIXME: include physical keyboard presence
 | 
			
		||||
        Outcome {
 | 
			
		||||
            visibility: match (self.physical_keyboard, self.visibility_override) {
 | 
			
		||||
            visibility: match self.preferred_output {
 | 
			
		||||
                None => animation::Outcome::Hidden,
 | 
			
		||||
                Some(output) => {
 | 
			
		||||
                    // Hoping that this will get optimized out on branches not using `visible`.
 | 
			
		||||
                    let height = Self::get_preferred_height(self.outputs.get(&output).unwrap())
 | 
			
		||||
                        .unwrap_or(PixelSize{pixels: 0, scale_factor: 1});
 | 
			
		||||
                    // TODO: Instead of setting size to 0 when the output is invalid,
 | 
			
		||||
                    // simply go invisible.
 | 
			
		||||
                    let visible = animation::Outcome::Visible{ output, height };
 | 
			
		||||
                    
 | 
			
		||||
                    match (self.physical_keyboard, self.visibility_override) {
 | 
			
		||||
                        (_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
 | 
			
		||||
                (_, visibility::State::ForcedVisible) => animation::Outcome::Visible,
 | 
			
		||||
                        (_, visibility::State::ForcedVisible) => visible,
 | 
			
		||||
                        (Presence::Present, visibility::State::NotForced) => animation::Outcome::Hidden,
 | 
			
		||||
                        (Presence::Missing, visibility::State::NotForced) => match self.im {
 | 
			
		||||
                    InputMethod::Active(_) => animation::Outcome::Visible,
 | 
			
		||||
                            InputMethod::Active(_) => visible,
 | 
			
		||||
                            InputMethod::InactiveSince(since) => {
 | 
			
		||||
                        if now < since + animation::HIDING_TIMEOUT { animation::Outcome::Visible }
 | 
			
		||||
                                if now < since + animation::HIDING_TIMEOUT { visible }
 | 
			
		||||
                                else { animation::Outcome::Hidden }
 | 
			
		||||
                            },
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            im: self.im.clone(),
 | 
			
		||||
        }
 | 
			
		||||
@ -236,9 +316,9 @@ impl Application {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
pub mod test {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    use crate::outputs::c::WlOutput;
 | 
			
		||||
    use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
    fn imdetails_new() -> InputMethodDetails {
 | 
			
		||||
@ -248,6 +328,30 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fake_output_id(id: usize) -> OutputId {
 | 
			
		||||
        OutputId(unsafe {
 | 
			
		||||
            std::mem::transmute::<_, WlOutput>(id)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn application_with_fake_output(start: Instant) -> Application {
 | 
			
		||||
        let id = fake_output_id(1);
 | 
			
		||||
        let mut outputs = HashMap::new();
 | 
			
		||||
        outputs.insert(
 | 
			
		||||
            id,
 | 
			
		||||
            OutputState {
 | 
			
		||||
                current_mode: None,
 | 
			
		||||
                transform: None,
 | 
			
		||||
                scale: 1,
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        Application {
 | 
			
		||||
            preferred_output: Some(id),
 | 
			
		||||
            outputs,
 | 
			
		||||
            ..Application::new(start)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Test the original delay scenario: no flicker on quick switches.
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn avoid_hide() {
 | 
			
		||||
@ -257,15 +361,16 @@ mod test {
 | 
			
		||||
            im: InputMethod::Active(imdetails_new()),
 | 
			
		||||
            physical_keyboard: Presence::Missing,
 | 
			
		||||
            visibility_override: visibility::State::NotForced,
 | 
			
		||||
            ..application_with_fake_output(start)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
 | 
			
		||||
        // Check 100ms at 1ms intervals. It should remain visible.
 | 
			
		||||
        for _i in 0..100 {
 | 
			
		||||
            now += Duration::from_millis(1);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
            assert_matches!(
 | 
			
		||||
                state.get_outcome(now).visibility,
 | 
			
		||||
                animation::Outcome::Visible,
 | 
			
		||||
                animation::Outcome::Visible{..},
 | 
			
		||||
                "Hidden when it should remain visible: {:?}",
 | 
			
		||||
                now.saturating_duration_since(start),
 | 
			
		||||
            )
 | 
			
		||||
@ -273,7 +378,7 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(state.get_outcome(now).visibility, animation::Outcome::Visible);
 | 
			
		||||
        assert_matches!(state.get_outcome(now).visibility, animation::Outcome::Visible{..});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make sure that hiding works when input method goes away
 | 
			
		||||
@ -285,11 +390,12 @@ mod test {
 | 
			
		||||
            im: InputMethod::Active(imdetails_new()),
 | 
			
		||||
            physical_keyboard: Presence::Missing,
 | 
			
		||||
            visibility_override: visibility::State::NotForced,
 | 
			
		||||
            ..application_with_fake_output(start)
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
 | 
			
		||||
 | 
			
		||||
        while let animation::Outcome::Visible = state.get_outcome(now).visibility {
 | 
			
		||||
        while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility {
 | 
			
		||||
            now += Duration::from_millis(1);
 | 
			
		||||
            assert!(
 | 
			
		||||
                now < start + Duration::from_millis(250),
 | 
			
		||||
@ -309,6 +415,7 @@ mod test {
 | 
			
		||||
            im: InputMethod::Active(imdetails_new()),
 | 
			
		||||
            physical_keyboard: Presence::Missing,
 | 
			
		||||
            visibility_override: visibility::State::NotForced,
 | 
			
		||||
            ..application_with_fake_output(start)
 | 
			
		||||
        };
 | 
			
		||||
        // This reflects the sequence from Wayland:
 | 
			
		||||
        // disable, disable, enable, disable
 | 
			
		||||
@ -318,7 +425,7 @@ mod test {
 | 
			
		||||
        let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
 | 
			
		||||
        let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
 | 
			
		||||
 | 
			
		||||
        while let animation::Outcome::Visible = state.get_outcome(now).visibility {
 | 
			
		||||
        while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility {
 | 
			
		||||
            now += Duration::from_millis(1);
 | 
			
		||||
            assert!(
 | 
			
		||||
                now < start + Duration::from_millis(250),
 | 
			
		||||
@ -347,13 +454,14 @@ mod test {
 | 
			
		||||
            im: InputMethod::InactiveSince(now),
 | 
			
		||||
            physical_keyboard: Presence::Missing,
 | 
			
		||||
            visibility_override: visibility::State::NotForced,
 | 
			
		||||
            ..application_with_fake_output(start)
 | 
			
		||||
        };
 | 
			
		||||
        now += Duration::from_secs(1);
 | 
			
		||||
 | 
			
		||||
        let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
        assert_matches!(
 | 
			
		||||
            state.get_outcome(now).visibility,
 | 
			
		||||
            animation::Outcome::Visible,
 | 
			
		||||
            animation::Outcome::Visible{..},
 | 
			
		||||
            "Failed to show: {:?}",
 | 
			
		||||
            now.saturating_duration_since(start),
 | 
			
		||||
        );
 | 
			
		||||
@ -380,6 +488,7 @@ mod test {
 | 
			
		||||
            im: InputMethod::Active(imdetails_new()),
 | 
			
		||||
            physical_keyboard: Presence::Missing,
 | 
			
		||||
            visibility_override: visibility::State::NotForced,
 | 
			
		||||
            ..application_with_fake_output(start)
 | 
			
		||||
        };
 | 
			
		||||
        now += Duration::from_secs(1);
 | 
			
		||||
 | 
			
		||||
@ -406,9 +515,9 @@ mod test {
 | 
			
		||||
        now += Duration::from_secs(1);
 | 
			
		||||
        let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
        assert_matches!(
 | 
			
		||||
            state.get_outcome(now).visibility,
 | 
			
		||||
            animation::Outcome::Visible,
 | 
			
		||||
            animation::Outcome::Visible{..},
 | 
			
		||||
            "Failed to appear: {:?}",
 | 
			
		||||
            now.saturating_duration_since(start),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								src/style.rs
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								src/style.rs
									
									
									
									
									
								
							@ -21,7 +21,7 @@
 | 
			
		||||
use std::env;
 | 
			
		||||
use ::logging;
 | 
			
		||||
 | 
			
		||||
use glib::object::ObjectExt;
 | 
			
		||||
use glib::prelude::ObjectExt;
 | 
			
		||||
use logging::Warn;
 | 
			
		||||
 | 
			
		||||
/// Gathers stuff defined in C or called by C
 | 
			
		||||
@ -31,7 +31,7 @@ pub mod c {
 | 
			
		||||
    use gtk;
 | 
			
		||||
    use gtk_sys;
 | 
			
		||||
    
 | 
			
		||||
    use gtk::CssProviderExt;
 | 
			
		||||
    use gtk::prelude::CssProviderExt;
 | 
			
		||||
    use glib::translate::ToGlibPtr;
 | 
			
		||||
 | 
			
		||||
    /// Loads the layout style based on current theme
 | 
			
		||||
@ -40,8 +40,13 @@ pub mod c {
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_load_style() -> *const gtk_sys::GtkCssProvider {
 | 
			
		||||
        unsafe { gtk::set_initialized() };
 | 
			
		||||
        let theme = gtk::Settings::get_default()
 | 
			
		||||
            .map(|settings| get_theme_name(&settings));
 | 
			
		||||
        
 | 
			
		||||
        #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
        let theme = gtk::Settings::default();
 | 
			
		||||
        #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
        let theme = gtk::Settings::get_default();
 | 
			
		||||
        
 | 
			
		||||
        let theme = theme.map(|settings| get_theme_name(&settings));
 | 
			
		||||
        
 | 
			
		||||
        let css_name = path_from_theme(theme);
 | 
			
		||||
 | 
			
		||||
@ -93,19 +98,31 @@ fn get_theme_name(settings: >k::Settings) -> GtkTheme {
 | 
			
		||||
            e
 | 
			
		||||
        }).ok();
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
    let prop = |s: >k::Settings, name| s.property(name);
 | 
			
		||||
    #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
    let prop = |s: >k::Settings, name| s.get_property(name);
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "glib_v0_14")]
 | 
			
		||||
    fn check<T, E: std::fmt::Display>(v: Result<T, E>) -> Option<T> {
 | 
			
		||||
        v.or_print(logging::Problem::Surprise, "Key not of expected type")
 | 
			
		||||
    }
 | 
			
		||||
    #[cfg(not(feature = "glib_v0_14"))]
 | 
			
		||||
    fn check<T>(v: Option<T>) -> Option<T> { v }
 | 
			
		||||
 | 
			
		||||
    match env_theme {
 | 
			
		||||
        Some(theme) => theme,
 | 
			
		||||
        None => GtkTheme {
 | 
			
		||||
            name: {
 | 
			
		||||
                settings.get_property("gtk-theme-name")
 | 
			
		||||
                prop(settings, "gtk-theme-name")
 | 
			
		||||
                    .or_print(logging::Problem::Surprise, "No theme name")
 | 
			
		||||
                    .and_then(|value| value.get::<String>())
 | 
			
		||||
                    .and_then(|value| check(value.get::<String>()))
 | 
			
		||||
                    .unwrap_or(DEFAULT_THEME_NAME.into())
 | 
			
		||||
            },
 | 
			
		||||
            variant: {
 | 
			
		||||
                settings.get_property("gtk-application-prefer-dark-theme")
 | 
			
		||||
                prop(settings, "gtk-application-prefer-dark-theme")
 | 
			
		||||
                    .or_print(logging::Problem::Surprise, "No settings key")
 | 
			
		||||
                    .and_then(|value| value.get::<bool>())
 | 
			
		||||
                    .and_then(|value| check(value.get::<bool>()))
 | 
			
		||||
                    .and_then(|dark_preferred| match dark_preferred {
 | 
			
		||||
                        true => Some("dark".into()),
 | 
			
		||||
                        false => None,
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@
 | 
			
		||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
 | 
			
		||||
#include "eek/eek-types.h"
 | 
			
		||||
#include "main.h"
 | 
			
		||||
#include "src/ui_manager.h"
 | 
			
		||||
 | 
			
		||||
struct squeek_layout;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,19 +0,0 @@
 | 
			
		||||
#ifndef UI_MANAGER__
 | 
			
		||||
#define UI_MANAGER__
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include "eek/eek-types.h"
 | 
			
		||||
#include "outputs.h"
 | 
			
		||||
#include "main.h"
 | 
			
		||||
 | 
			
		||||
struct ui_manager;
 | 
			
		||||
 | 
			
		||||
struct ui_manager *squeek_uiman_new(void);
 | 
			
		||||
void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output);
 | 
			
		||||
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
 | 
			
		||||
 | 
			
		||||
struct vis_manager;
 | 
			
		||||
 | 
			
		||||
struct vis_manager *squeek_visman_new(struct squeek_state_manager *state_manager);
 | 
			
		||||
#endif
 | 
			
		||||
@ -1,82 +0,0 @@
 | 
			
		||||
/* Copyright (C) 2020, 2021 Purism SPC
 | 
			
		||||
 * SPDX-License-Identifier: GPL-3.0+
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! Centrally manages the shape of the UI widgets, and the choice of layout.
 | 
			
		||||
 * 
 | 
			
		||||
 * Coordinates this based on information collated from all possible sources.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::cmp::min;
 | 
			
		||||
use ::outputs::c::OutputHandle;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use ::util::c::Wrapped;
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_uiman_new() -> Wrapped<Manager> {
 | 
			
		||||
        Wrapped::new(Manager { output: None })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Used to size the layer surface containing all the OSK widgets.
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_uiman_get_perceptual_height(
 | 
			
		||||
        uiman: Wrapped<Manager>,
 | 
			
		||||
    ) -> u32 {
 | 
			
		||||
        let uiman = uiman.clone_ref();
 | 
			
		||||
        let uiman = uiman.borrow();
 | 
			
		||||
        // TODO: what to do when there's no output?
 | 
			
		||||
        uiman.get_perceptual_height().unwrap_or(0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_uiman_set_output(
 | 
			
		||||
        uiman: Wrapped<Manager>,
 | 
			
		||||
        output: OutputHandle,
 | 
			
		||||
    ) {
 | 
			
		||||
        let uiman = uiman.clone_ref();
 | 
			
		||||
        let mut uiman = uiman.borrow_mut();
 | 
			
		||||
        uiman.output = Some(output);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Stores current state of all things influencing what the UI should look like.
 | 
			
		||||
pub struct Manager {
 | 
			
		||||
    /// Shared output handle, current state updated whenever it's needed.
 | 
			
		||||
    // TODO: Stop assuming that the output never changes.
 | 
			
		||||
    // (There's no way for the output manager to update the ui manager.)
 | 
			
		||||
    // FIXME: Turn into an OutputState and apply relevant connections elsewhere.
 | 
			
		||||
    // Otherwise testability and predictablity is low.
 | 
			
		||||
    output: Option<OutputHandle>,
 | 
			
		||||
    //// Pixel size of the surface. Needs explicit updating.
 | 
			
		||||
    //surface_size: Option<Size>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Manager {
 | 
			
		||||
    fn get_perceptual_height(&self) -> Option<u32> {
 | 
			
		||||
        let output_info = (&self.output).as_ref()
 | 
			
		||||
            .and_then(|o| o.get_state())
 | 
			
		||||
            .map(|os| (os.scale as u32, os.get_pixel_size()));
 | 
			
		||||
        match output_info {
 | 
			
		||||
            Some((scale, Some(px_size))) => Some({
 | 
			
		||||
                let height = if (px_size.width < 720) & (px_size.width > 0) {
 | 
			
		||||
                    px_size.width * 7 / 12 // to match 360×210
 | 
			
		||||
                } else if px_size.width < 1080 {
 | 
			
		||||
                    360 + (1080 - px_size.width) * 60 / 360 // smooth transition
 | 
			
		||||
                } else {
 | 
			
		||||
                    360
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                // Don't exceed half the display size
 | 
			
		||||
                min(height, px_size.height / 2) / scale
 | 
			
		||||
            }),
 | 
			
		||||
            Some((scale, None)) => Some(360 / scale),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -10,11 +10,18 @@ type KeyCode = u32;
 | 
			
		||||
pub mod c {
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct ZwpVirtualKeyboardV1(*const c_void);
 | 
			
		||||
 | 
			
		||||
    impl ZwpVirtualKeyboardV1 {
 | 
			
		||||
        pub fn null() -> Self {
 | 
			
		||||
            Self(ptr::null())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct KeyMap {
 | 
			
		||||
        fd: u32,
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,12 @@
 | 
			
		||||
 | 
			
		||||
struct squeek_wayland *squeek_wayland = NULL;
 | 
			
		||||
 | 
			
		||||
void squeek_wayland_init_global(struct squeek_outputs *outputs) {
 | 
			
		||||
    struct squeek_wayland *wayland = {0};
 | 
			
		||||
    wayland->outputs = outputs;
 | 
			
		||||
    squeek_wayland = wayland;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The following functions only exist
 | 
			
		||||
// to create linkable symbols out of inline functions,
 | 
			
		||||
// because those are not directly callable from Rust
 | 
			
		||||
 | 
			
		||||
@ -10,27 +10,18 @@
 | 
			
		||||
#include "outputs.h"
 | 
			
		||||
 | 
			
		||||
struct squeek_wayland {
 | 
			
		||||
    // globals
 | 
			
		||||
    struct zwlr_layer_shell_v1 *layer_shell;
 | 
			
		||||
    struct zwp_virtual_keyboard_manager_v1 *virtual_keyboard_manager;
 | 
			
		||||
    struct zwp_input_method_manager_v2 *input_method_manager;
 | 
			
		||||
    struct squeek_outputs *outputs;
 | 
			
		||||
    struct wl_seat *seat;
 | 
			
		||||
    // objects
 | 
			
		||||
    struct zwp_input_method_v2 *input_method;
 | 
			
		||||
    struct zwp_virtual_keyboard_v1 *virtual_keyboard;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
extern struct squeek_wayland *squeek_wayland;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static inline void squeek_wayland_init(struct squeek_wayland *wayland) {
 | 
			
		||||
    wayland->outputs = squeek_outputs_new();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) {
 | 
			
		||||
    squeek_wayland = wayland;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void squeek_wayland_deinit(struct squeek_wayland *wayland) {
 | 
			
		||||
    squeek_outputs_free(wayland->outputs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // WAYLAND_H
 | 
			
		||||
 | 
			
		||||
@ -84,6 +84,7 @@ foreach layout : [
 | 
			
		||||
    'jp+kana','jp+kana_wide',
 | 
			
		||||
    'no',
 | 
			
		||||
    'pl', 'pl_wide',
 | 
			
		||||
    'ro', 'ro_wide',
 | 
			
		||||
    'ru',
 | 
			
		||||
    'se',
 | 
			
		||||
    'th', 'th_wide',
 | 
			
		||||
@ -107,15 +108,8 @@ foreach layout : [
 | 
			
		||||
        extra += ['allow_missing_return']
 | 
			
		||||
    endif
 | 
			
		||||
    
 | 
			
		||||
    # Older Cargo seens to be sensitive to something
 | 
			
		||||
    # about the RUST_FLAGS env var, and rebuilds all tests when it's set,
 | 
			
		||||
    # increasing test time by 2 orders of magnitude.
 | 
			
		||||
    # Let it have its way.
 | 
			
		||||
    if get_option('legacy') == true
 | 
			
		||||
        timeout = 300
 | 
			
		||||
    else
 | 
			
		||||
    timeout = 30
 | 
			
		||||
    endif
 | 
			
		||||
 | 
			
		||||
    test(
 | 
			
		||||
        'test_layout_' + layout,
 | 
			
		||||
        cargo_script,
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user