Compare commits
218 Commits
pureos/1.1
...
1.17
| 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 | |||
| d49ce45de0 | |||
| a341fca43a | |||
| db6109b298 | |||
| 145d12d01a | |||
| 506df8cf15 | |||
| 21ecbb3ef3 | |||
| 29da31af20 | |||
| 23b35733cb | |||
| 55cd225c74 | |||
| e55ae67da6 | |||
| 79bc670ad0 | |||
| ebc2dd39f6 | |||
| 71768e27c0 | |||
| aaac755869 | |||
| 0430ba9213 | |||
| 0c17924c50 | |||
| 5286ff50a5 | |||
| 3ee2185714 | |||
| 7ea30819aa | |||
| 3647581cd7 | |||
| d76b385316 | |||
| 3240006516 | |||
| 2d0aa7aef1 | |||
| 4dd4c8c319 | |||
| 324438acac | |||
| a643b05f57 | |||
| 7adf325831 | |||
| b85903cb21 | |||
| 67d2f8d8e6 | |||
| 6979b6d08d | |||
| 44b9c8f869 | |||
| 23ae771605 | |||
| e0da17aa7d | |||
| 17b8e8cc2c | |||
| 9985f81624 | |||
| 589b5e490e | |||
| 6dae43b437 | |||
| 8eb1c9b4a7 | |||
| e1b3de9bf8 | |||
| c54b6e2d3b | |||
| 7b8cecbf90 | |||
| 4a7dc799be | |||
| b2485c343a | |||
| ac3f390cc6 | |||
| 9f74e95cef | |||
| 0b9d8acdd7 | |||
| 3d60dd96af | |||
| 4cc7017e1c | |||
| 53137fd2e2 | |||
| 334504a5b2 | |||
| f5a1d379ca | |||
| 2fea092691 | |||
| d64b8cdf1a | |||
| 81948425d8 | |||
| 7a07f1616d | |||
| f986f14220 | |||
| 2ac4980db9 | |||
| 1451c746e7 | |||
| 0a417fe4a2 | |||
| 750814e050 | |||
| 10ea276052 | |||
| d7e27bc793 | |||
| cd869e323e | |||
| 1a193a9aed | |||
| e380179144 | |||
| b8fece97d9 | |||
| f9c15845a1 | |||
| 72a7825c85 | |||
| da789bcb87 | |||
| 2aa9cf26ee | |||
| ac833950ab | |||
| 9b7e065b0f | |||
| 20ef5b6925 | |||
| 90df025b9f | |||
| e776bf005e | |||
| aea0d413f3 | |||
| a9f5429bcb | |||
| 12c10ad726 | |||
| 79ce23822d | |||
| 78735677d2 | |||
| 12cf628c71 | |||
| da7230e723 | |||
| e91f6419fb | |||
| 16dd4f9753 | |||
| b41aedabbf | |||
| 7a1e646bc9 | |||
| 4f19a81d78 | |||
| 5054f4c9de | |||
| 6eb3914ab3 | |||
| 674c1384db | |||
| 1b13df3364 | |||
| e48d8bee4d | |||
| 0c4d3e5450 | |||
| 9d97fa4d29 | |||
| 321dadba34 | |||
| 4903f3d1a0 | |||
| 3513bf8de4 | |||
| 5f40c31cab | |||
| 956377fd2f | |||
| f922374e27 | |||
| 3306907008 | |||
| fdcef4aa88 | |||
| 74f55b30ef | |||
| d696c9624e | |||
| 964f1da305 | |||
| 0a09036979 | |||
| 73e7908709 | |||
| 06b17907bf | |||
| 912fe0b7b7 | |||
| 937638c582 | |||
| 894191d3a0 | |||
| 9fbb974ce4 | |||
| e989df8b21 | |||
| 0d7fab2ef4 | |||
| f3eeedc92f | |||
| dd708456ff | |||
| f80faabfb1 | |||
| f225a34d70 | |||
| dce848b3ce | |||
| 03aa04d6d8 | |||
| 59662444d7 | |||
| 379a09e0d6 | |||
| 1c875cda6c | |||
| a6d766e650 | |||
| 74977ec88f | |||
| 2169e0393b | |||
| 9ba900bf42 | |||
| c3337b05b6 | |||
| 28a48635b3 | |||
| 475761ec73 | |||
| 1995285b65 | |||
| 73515b5fe1 | |||
| 8cfce093e7 | |||
| 359376041d | |||
| 6e1faafa2f | |||
| 5d6a9556ae | |||
| e487343046 | |||
| f15e4db818 | |||
| aa0e7d1c19 | |||
| dc7e84810b | |||
| 9f8df6f8cb | |||
| ef22432583 | |||
| fc411d680b | |||
| a74116a72f | |||
| fdb288c836 | |||
| 9cd56185c5 | |||
| 1f91cf00a4 | |||
| faccd32316 | |||
| 82c320a075 | |||
| 5db16f63ea | |||
| 135403d878 | |||
| 393c521216 | |||
| b5046d6cd3 | |||
| c31e10fc90 | |||
| d629542ce2 | |||
| e5984ed562 | |||
| 39c004ecfc | |||
| 31f8e4e49f | |||
| 2c662e0630 | |||
| c641035b1a | |||
| cce1f6b817 | |||
| 4efe57cbb4 | |||
| 19e22418bd | |||
| 6409ff3748 | |||
| e073434781 | |||
| 83942c27b8 | |||
| 29ef4f5bed | |||
| 9eb397151f | |||
| 3a1ea69006 | |||
| 5c5475d508 | |||
| 389aedac8d | |||
| 9b52edbf99 | |||
| 080186c18b | |||
| 7f0749483e | |||
| 97c7c4557d | |||
| 19630334b0 | |||
| 7e4487c757 | |||
| ebc8eafa07 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
.zanata-cache/
|
||||
_build
|
||||
po/squeekboard.pot
|
||||
po/*.mo
|
||||
TAGS
|
||||
tags
|
||||
vgdump
|
||||
|
||||
202
.gitlab-ci.yml
202
.gitlab-ci.yml
@ -1,34 +1,27 @@
|
||||
image: debian:bullseye
|
||||
image: pureos/byzantium
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
.tags: &tags
|
||||
tags:
|
||||
- librem5
|
||||
|
||||
before_script:
|
||||
- apt-get -y update
|
||||
- apt-get -y install wget ca-certificates gnupg
|
||||
- echo "deb [trusted=yes] http://ci.puri.sm/ bullseyeci main" > /etc/apt/sources.list.d/ci.list
|
||||
- wget -O- https://ci.puri.sm/ci-repo.key | apt-key add -
|
||||
- apt-get -y update
|
||||
- apt-get -y install ca-certificates
|
||||
|
||||
build_docs:
|
||||
<<: *tags
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- _build
|
||||
script:
|
||||
- apt-get -y install python3-pip python3-sphinx
|
||||
- pip3 install recommonmark
|
||||
- ./doc/build.sh _build
|
||||
- apt-get -y install python3-pip python3-sphinx
|
||||
- pip3 install recommonmark
|
||||
- ./doc/build.sh _build
|
||||
except:
|
||||
variables:
|
||||
- $PKG_ONLY == "1"
|
||||
|
||||
build_meson:
|
||||
tags:
|
||||
- librem5
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
@ -38,115 +31,114 @@ build_meson:
|
||||
- apt-get -y build-dep .
|
||||
- meson . _build/ -Ddepdatadir=/usr/share --werror
|
||||
- ninja -C _build install
|
||||
except:
|
||||
variables:
|
||||
- $PKG_ONLY == "1"
|
||||
|
||||
build_deb:
|
||||
tags:
|
||||
- librem5
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.deb"
|
||||
script:
|
||||
- rm -f ../*.deb
|
||||
- apt-get -y build-dep .
|
||||
- apt-get -y install devscripts
|
||||
- debuild -i -us -uc -b
|
||||
- cp ../*.deb .
|
||||
|
||||
build_deb:amber:
|
||||
image: pureos/amber
|
||||
tags:
|
||||
- librem5
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.deb"
|
||||
script:
|
||||
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
|
||||
- apt-get -y update
|
||||
- rm -f ../*.deb
|
||||
- apt-get -y build-dep .
|
||||
- apt-get -y install devscripts
|
||||
- debuild -i -us -uc -b
|
||||
- cp ../*.deb .
|
||||
|
||||
build_deb:buster:
|
||||
image: "debian:buster"
|
||||
tags:
|
||||
- librem5
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.deb"
|
||||
script:
|
||||
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
|
||||
- apt-get -y update
|
||||
- rm -f ../*.deb
|
||||
- apt-get -y build-dep .
|
||||
- apt-get -y install devscripts
|
||||
- debuild -i -us -uc -b
|
||||
- cp ../*.deb .
|
||||
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- '*.deb'
|
||||
script:
|
||||
- rm -f ../*.deb
|
||||
- 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 .
|
||||
|
||||
build_deb:arm64:
|
||||
tags:
|
||||
- librem5:arm64
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.deb"
|
||||
script:
|
||||
- rm -f ../*.deb
|
||||
- apt-get -y build-dep .
|
||||
- apt-get -y install devscripts
|
||||
- debuild -i -us -uc -b
|
||||
- cp ../*.deb .
|
||||
image: pureos/byzantium
|
||||
tags:
|
||||
- aarch64
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- '*.deb'
|
||||
script:
|
||||
- rm -f ../*.deb
|
||||
- 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 .
|
||||
|
||||
build_deb:arm64_buster:
|
||||
image: "debian:buster"
|
||||
tags:
|
||||
- librem5:arm64
|
||||
stage: build
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.deb"
|
||||
script:
|
||||
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
|
||||
- apt-get -y update
|
||||
- rm -f ../*.deb
|
||||
- apt-get -y build-dep .
|
||||
- apt-get -y install devscripts
|
||||
- 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:
|
||||
<<: *tags
|
||||
stage: test
|
||||
dependencies:
|
||||
- build_deb
|
||||
script:
|
||||
- apt-get -y install lintian
|
||||
- lintian *.deb
|
||||
|
||||
test:
|
||||
tags:
|
||||
- librem5
|
||||
stage: test
|
||||
needs:
|
||||
- build_meson
|
||||
- job: build_deb
|
||||
artifacts: true
|
||||
script:
|
||||
- apt-get -y install lintian
|
||||
- lintian *.deb
|
||||
except:
|
||||
variables:
|
||||
- $PKG_ONLY == "1"
|
||||
|
||||
test:
|
||||
stage: test
|
||||
needs:
|
||||
- job: build_meson
|
||||
artifacts: true
|
||||
script:
|
||||
- apt-get -y build-dep .
|
||||
- apt-get -y install clang-tidy
|
||||
- ninja -C _build test
|
||||
- cd _build
|
||||
- clang-tidy --checks=-clang-diagnostic-missing-braces,readability-braces-around-statements, --warnings-as-errors=readability-braces-around-statements -extra-arg=-Wno-unknown-warning-option ../src/*.c ../eek/*.c ../eekboard/*.c
|
||||
- tools/style-check_build _build
|
||||
except:
|
||||
variables:
|
||||
- $PKG_ONLY == "1"
|
||||
|
||||
test_style:
|
||||
stage: test
|
||||
needs: []
|
||||
script:
|
||||
- apt-get -y build-dep .
|
||||
- tools/style-check_source
|
||||
except:
|
||||
variables:
|
||||
- $PKG_ONLY == "1"
|
||||
|
||||
check_release:
|
||||
<<: *tags
|
||||
stage: test
|
||||
needs: []
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
script:
|
||||
- apt-get -y install git python3
|
||||
- (head -n 1 ./debian/changelog && git tag) | ./debian/check_release.py
|
||||
except:
|
||||
variables:
|
||||
- $PKG_ONLY == "1"
|
||||
|
||||
12
Cargo.deps
12
Cargo.deps
@ -1,11 +1,13 @@
|
||||
# Dependencies which change based on build flags
|
||||
bitflags = "1.2.*"
|
||||
clap = { version = "2.33.*", default-features = false }
|
||||
regex = { version = "1.3.*", default-features = false, features = ["std", "unicode-case"] }
|
||||
|
||||
[dependencies.cairo-rs]
|
||||
version = "0.7.*"
|
||||
|
||||
[dependencies.cairo-sys-rs]
|
||||
version = "0.9"
|
||||
|
||||
[dependencies.gdk]
|
||||
version = "0.11.*"
|
||||
|
||||
@ -17,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,22 +0,0 @@
|
||||
# Dependencies which change based on build flags
|
||||
bitflags = "1.0.*"
|
||||
clap = { version = "2.32.*", default-features = false }
|
||||
regex = { version = "1.1.*", default-features = false, features = ['use_std'] }
|
||||
|
||||
[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" }
|
||||
102
Cargo.lock
generated
102
Cargo.lock
generated
@ -1,5 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "atk"
|
||||
version = "0.7.0"
|
||||
@ -26,6 +28,12 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
@ -59,27 +67,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.67"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
version = "2.33.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
checksum = "826bf7bc84f9435630275cb8e802a4a0ec792b615969934bd16d42ffed10f207"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||
|
||||
[[package]]
|
||||
name = "fragile"
|
||||
version = "0.3.0"
|
||||
@ -257,6 +259,22 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -265,9 +283,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.94"
|
||||
version = "0.2.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
@ -320,43 +338,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.26"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rs"
|
||||
version = "0.1.0"
|
||||
@ -372,26 +375,31 @@ dependencies = [
|
||||
"gtk",
|
||||
"gtk-sys",
|
||||
"maplit",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"xkbcommon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -400,21 +408,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.17"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
|
||||
checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
|
||||
dependencies = [
|
||||
"dtoa",
|
||||
"linked-hash-map",
|
||||
"indexmap",
|
||||
"ryu",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.72"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -432,9 +440,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
|
||||
@ -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 = ""
|
||||
|
||||
[dependencies.glib-sys]
|
||||
version = ""
|
||||
features = ["v2_44"]
|
||||
|
||||
[dependencies.gtk-sys]
|
||||
version = ""
|
||||
features = ["v3_22"]
|
||||
|
||||
[dependencies]
|
||||
maplit = "1.0.*"
|
||||
serde = { version = "1.0.*", features = ["derive"] }
|
||||
|
||||
39
README.md
39
README.md
@ -1,7 +1,7 @@
|
||||
*squeekboard* - a Wayland virtual keyboard
|
||||
*squeekboard* - a Wayland on-screen keyboard
|
||||
========================================
|
||||
|
||||
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
|
||||
*Squeekboard* is a keyboard-shaped input method supporting Wayland, built primarily for the *Librem 5* phone.
|
||||
|
||||
It squeaks because some Rust got inside.
|
||||
|
||||
@ -11,20 +11,24 @@ Features
|
||||
### Present
|
||||
|
||||
- GTK3
|
||||
- Custom yaml-defined keyboards
|
||||
- Custom keyboard layouts defined in yaml
|
||||
- Input purpose dependent keyboard layouts
|
||||
- DBus interface to show and hide
|
||||
- Use Wayland input method protocol to show and hide
|
||||
- Use Wayland input method protocol to submit text
|
||||
- Use Wayland virtual keyboard protocol
|
||||
|
||||
### Temporarily dropped
|
||||
|
||||
- A settings interface
|
||||
|
||||
### TODO
|
||||
|
||||
- Use Wayland input method protocol
|
||||
- Text prediction/correction
|
||||
- Use preedit
|
||||
- Submit actions like "next field" using a future Wayland protocol
|
||||
- Pick up DBus interface files from /usr/share
|
||||
|
||||
Creating layouts
|
||||
-------------------
|
||||
|
||||
If you want to work on layouts, check out the [guide](doc/tutorial.md).
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
@ -35,7 +39,7 @@ See `.gitlab-ci.yml` or run `apt-get build-dep .`
|
||||
### Build from git repo
|
||||
|
||||
```bash
|
||||
$ git clone https://source.puri.sm/Librem5/squeekboard.git
|
||||
$ git clone https://gitlab.gnome.org/World/Phosh/squeekboard.git
|
||||
$ cd squeekboard
|
||||
$ mkdir _build
|
||||
$ meson _build/
|
||||
@ -54,18 +58,31 @@ $ cd ../build/
|
||||
$ src/squeekboard
|
||||
```
|
||||
|
||||
Squeekboard's panel will appear whenever a compatible application requests an input method. Click a text field in any GTK application, like `python3 ./tools/entry.py`.
|
||||
|
||||
Squeekboard honors the gnome "screen-keyboard-enabled" setting. Either enable this through gnome-settings under accessibility or run:
|
||||
|
||||
```bash
|
||||
$ gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true
|
||||
```
|
||||
|
||||
To make the keyboard show you can use either an application that does so automatically, like a text editor or `python3 ./tests/entry.py`, or you can manually trigger it with:
|
||||
Alternatively, force panel visibility manually with:
|
||||
|
||||
```bash
|
||||
busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
|
||||
```
|
||||
|
||||
### What the compositor has to support
|
||||
|
||||
A compatible compositor has to support the protocols:
|
||||
|
||||
- layer-shell
|
||||
- virtual-keyboard-v1
|
||||
|
||||
It's strongly recommended to support:
|
||||
|
||||
- input-method-v2
|
||||
|
||||
Developing
|
||||
----------
|
||||
|
||||
|
||||
33
data/common.css
Normal file
33
data/common.css
Normal file
@ -0,0 +1,33 @@
|
||||
/* 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;
|
||||
margin: 1px 1px 1px 1px;
|
||||
}
|
||||
84
data/keyboards/am+phonetic.yaml
Normal file
84
data/keyboards/am+phonetic.yaml
Normal file
@ -0,0 +1,84 @@
|
||||
# Armenian layout created by Norayr Chilingarian
|
||||
# Yerevan
|
||||
# Oct 2021
|
||||
---
|
||||
outlines:
|
||||
default: { width: 35.33, height: 52 }
|
||||
altline: { width: 52.67, height: 52 }
|
||||
wide: { width: 32, height: 32 }
|
||||
spaceline: { width: 142, height: 52 }
|
||||
special: { width: 44, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "՝ է թ փ ձ ջ ւ և ռ չ ճ ֊ ժ"
|
||||
- "ք ո ե ր տ ը ւ ի օ պ խ ծ շ"
|
||||
- "ա ս դ ֆ գ հ յ կ լ ․"
|
||||
- "Shift_L զ ղ ց վ բ ն մ ՛ BackSpace"
|
||||
- "show_numbers preferences space period Return"
|
||||
upper:
|
||||
- "՝ Է Թ Փ Ձ Ջ Ւ և Ռ Չ Ճ — Ժ"
|
||||
- "Ք Ո Ե Ր Տ Ը Ւ Ի Օ Պ Խ Ծ Շ"
|
||||
- "Ա Ս Դ Ֆ Գ Հ Յ Կ Լ ։"
|
||||
- "Shift_L Զ Ղ Ց Վ Բ Ն Մ ՞ BackSpace"
|
||||
- "show_numbers preferences space period Return"
|
||||
numbers:
|
||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||
- "ﬓ ﬔ ﬕ ﬖ ﬗ ՟ և"
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "show_letters preferences space period Return"
|
||||
symbols:
|
||||
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace"
|
||||
- "* # $ / & - _ + ( )"
|
||||
- "© ® £ € ¥ ^ ° @ { }"
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "show_letters preferences space period Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "upper"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
icon: "key-shift"
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
action: "erase"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
show_numbers:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "wide"
|
||||
label: "123"
|
||||
show_numbers_from_symbols:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "altline"
|
||||
label: "123"
|
||||
show_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "wide"
|
||||
label: "ԱԲԳ"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
period:
|
||||
outline: "special"
|
||||
text: "."
|
||||
space:
|
||||
outline: "spaceline"
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
text: ":"
|
||||
84
data/keyboards/am.yaml
Normal file
84
data/keyboards/am.yaml
Normal file
@ -0,0 +1,84 @@
|
||||
# Armenian layout created by Norayr Chilingarian
|
||||
# Yerevan
|
||||
# Oct 2021
|
||||
---
|
||||
outlines:
|
||||
default: { width: 35.33, height: 52 }
|
||||
altline: { width: 52.67, height: 52 }
|
||||
wide: { width: 32, height: 32 }
|
||||
spaceline: { width: 142, height: 52 }
|
||||
special: { width: 44, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "՝ ֆ ձ ֊ , ։ ՞ ․ ՛ ) օ է ղ"
|
||||
- "ճ փ բ ս մ ո ւ կ ը թ ծ ց »"
|
||||
- "ջ վ գ ե ա ն ի տ հ պ ր"
|
||||
- "Shift_L ժ դ չ յ զ լ ք խ շ ռ BackSpace"
|
||||
- "show_numbers preferences space period Return"
|
||||
upper:
|
||||
- "՜ Ֆ Ձ — $ … ՟ և ՚ ( Օ Է Ղ"
|
||||
- "Ճ Փ Բ Ս Մ Ո Ւ Կ Ը Թ Ծ Ց «"
|
||||
- "Ջ Վ Գ Ե Ա Ն Ի Տ Հ Պ Պ Ր"
|
||||
- "Shift_L Ժ Դ Չ Յ Զ Լ Ք Խ Շ Ռ BackSpace"
|
||||
- "show_numbers preferences space period Return"
|
||||
numbers:
|
||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||
- "ﬓ ﬔ ﬕ ﬖ ﬗ ՟ և"
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "show_letters preferences space period Return"
|
||||
symbols:
|
||||
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace"
|
||||
- "* # $ / & - _ + ( )"
|
||||
- "© ® £ € ¥ ^ ° @ { }"
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "show_letters preferences space period Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "upper"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
icon: "key-shift"
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
action: "erase"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
show_numbers:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "wide"
|
||||
label: "123"
|
||||
show_numbers_from_symbols:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "altline"
|
||||
label: "123"
|
||||
show_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "wide"
|
||||
label: "ԱԲԳ"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
period:
|
||||
outline: "special"
|
||||
text: "."
|
||||
space:
|
||||
outline: "spaceline"
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
text: ":"
|
||||
70
data/keyboards/ara.yaml
Normal file
70
data/keyboards/ara.yaml
Normal file
@ -0,0 +1,70 @@
|
||||
# Maintained by: Khaled Eldoheiri <khalid@kdehairy.com>
|
||||
---
|
||||
outlines:
|
||||
default: { width: 32.66, height: 52 }
|
||||
altline: { width: 48.99, height: 52 }
|
||||
wide: { width: 62, height: 52 }
|
||||
spaceline: { width: 195.96, height: 52 }
|
||||
special: { width: 35.66, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "ذ ض ص ث ق ف غ ع خ ح ج"
|
||||
- "ش س ي ب ل ا ت ن م ك ط"
|
||||
- "Shift_L ء ؤ ر ة و ز ظ د BackSpace"
|
||||
- "show_numbers preferences space . Return"
|
||||
extra:
|
||||
- "ذ ض ص ث ق لإ إ ع خ ح ج"
|
||||
- "ش س ى ب لأ أ ت ن م ك ط"
|
||||
- "Shift_L ئ لآ لا ه آ ز ظ د BackSpace"
|
||||
- "show_numbers preferences space . Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # € % & - _ + ( )"
|
||||
- "show_symbols ، \" ' : ؛ ! ؟ BackSpace"
|
||||
- "show_letters preferences space . Return"
|
||||
symbols:
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "© ® £ € ¥ ^ ° * { }"
|
||||
- "show_numbers \\ / < > = [ ] BackSpace"
|
||||
- "show_letters preferences space . Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "extra"
|
||||
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_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "altline"
|
||||
label: "ض"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "altline"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
70
data/keyboards/ara_wide.yaml
Normal file
70
data/keyboards/ara_wide.yaml
Normal file
@ -0,0 +1,70 @@
|
||||
# Maintained by: Khaled Eldoheiri <khalid@kdehairy.com>
|
||||
---
|
||||
outlines:
|
||||
default: { width: 49, height: 42 }
|
||||
altline: { width: 73.5, height: 42 }
|
||||
wide: { width: 108, height: 42 }
|
||||
spaceline: { width: 324, height: 42 }
|
||||
special: { width: 49, height: 42 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "ذ ض ص ث ق ف غ ع خ ح ج"
|
||||
- "ش س ي ب ل ا ت ن م ك ط"
|
||||
- "Shift_L ء ؤ ر ة و ز ظ د BackSpace"
|
||||
- "show_numbers preferences space . Return"
|
||||
extra:
|
||||
- "ذ ض ص ث ق لإ إ ع خ ح ج"
|
||||
- "ش س ى ب لأ أ ت ن م ك ط"
|
||||
- "Shift_L ئ لآ لا ه آ ز ظ د BackSpace"
|
||||
- "show_numbers preferences space . Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # € % & - _ + ( )"
|
||||
- "show_symbols ، \" ' : ؛ ! ؟ BackSpace"
|
||||
- "show_letters preferences space . Return"
|
||||
symbols:
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "© ® £ € ¥ ^ ° * { }"
|
||||
- "show_numbers \\ / < > = [ ] BackSpace"
|
||||
- "show_letters preferences space . Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "extra"
|
||||
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_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "altline"
|
||||
label: "ض"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "altline"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
78
data/keyboards/bg+phonetic.yaml
Normal file
78
data/keyboards/bg+phonetic.yaml
Normal file
@ -0,0 +1,78 @@
|
||||
---
|
||||
outlines:
|
||||
default: { width: 32.72, height: 52 }
|
||||
altline: { width: 47, height: 52 }
|
||||
wide: { width: 49.09, height: 52 }
|
||||
spaceline: { width: 185, height: 52 }
|
||||
special: { width: 44, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "я в е р т ъ у и о п ю"
|
||||
- "а с д ф г х й к л ш щ"
|
||||
- "Shift_L з ь ц ж б н м ч BackSpace"
|
||||
- "show_numbers preferences space . Return"
|
||||
upper:
|
||||
- "Я В Е Р Т Ъ У И О П Ю"
|
||||
- "А С Д Ф Г Х Й К Л Ш Щ"
|
||||
- "Shift_L З Ь Ц Ж Б Н М Ч BackSpace"
|
||||
- "show_numbers preferences space , Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # € % & - _ + ( )"
|
||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||
- "show_letters preferences space Return"
|
||||
symbols:
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "© ® £ $ ¥ ^ ° * { }"
|
||||
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||
- "show_letters preferences space Return"
|
||||
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "upper"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
icon: "key-shift"
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
action: erase
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
show_numbers:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "wide"
|
||||
label: "123"
|
||||
show_numbers_from_symbols:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "altline"
|
||||
label: "123"
|
||||
show_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "wide"
|
||||
label: "abc"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
space:
|
||||
outline: "spaceline"
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
text: ":"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
@ -8,19 +8,19 @@ outlines:
|
||||
|
||||
views:
|
||||
base:
|
||||
- "я в е р т ъ у и о п ю"
|
||||
- "а с д ф г х й к л ш щ"
|
||||
- "Shift_L з ь ц ж б н м ч BackSpace"
|
||||
- "у е и ш щ к с д з ц б"
|
||||
- "ь я а о ж г т н в м ч"
|
||||
- "Shift_L ю й ъ ф х п р л BackSpace"
|
||||
- "show_numbers preferences space . Return"
|
||||
upper:
|
||||
- "Я В Е Р Т Ъ У И О П Ю"
|
||||
- "А С Д Ф Г Х Й К Л Ш Щ"
|
||||
- "Shift_L З Ь Ц Ж Б Н М Ч BackSpace"
|
||||
- "У Е И Ш Щ К С Д З Ц Б"
|
||||
- "Ь Я А О Ж Г Т Н В М Ч"
|
||||
- "Shift_L Ю Й Ъ Ф Х П Р Л BackSpace"
|
||||
- "show_numbers preferences space , Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # € % & - _ + ( )"
|
||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||
- "show_symbols , \" ' colon ; ! ? ѝ BackSpace"
|
||||
- "show_letters preferences space Return"
|
||||
symbols:
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
|
||||
73
data/keyboards/ch+de.yaml
Normal file
73
data/keyboards/ch+de.yaml
Normal file
@ -0,0 +1,73 @@
|
||||
# Maintained by Patrick Jörg <patrickjoerg@gmx.ch>. No Copyright, enjoy!
|
||||
|
||||
---
|
||||
outlines:
|
||||
default: { width: 35.33, height: 52 }
|
||||
altline: { width: 48, height: 52 }
|
||||
wide: { width: 59, height: 52 }
|
||||
spaceline: { width: 70, height: 52 }
|
||||
special: { width: 28, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "q w e r t z u i o p ü"
|
||||
- "a s d f g h j k l ö ä"
|
||||
- "Shift_L y x c v b n m BackSpace"
|
||||
- "show_numbers ? ! preferences ' space , . Return"
|
||||
upper:
|
||||
- "Q W E R T Z U I O P Ü"
|
||||
- "A S D F G H J K L Ö Ä"
|
||||
- "Shift_L Y X C V B N M BackSpace"
|
||||
- "show_numbers - _ preferences \" space , . Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ * + - = ( ) ~ < >"
|
||||
- "show_symbols # & / \\ √ ; : BackSpace"
|
||||
- "show_letters ? ! preferences _ space , . Return"
|
||||
symbols:
|
||||
- "€ $ £ ¥ % | § µ [ ]"
|
||||
- "© ® § ` ^ { } · ¡ ¿"
|
||||
- "show_numbers « » ÷ × “ ” „ BackSpace"
|
||||
- "show_letters preferences - space , . Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "upper"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
icon: "key-shift"
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
action: erase
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
show_numbers:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "altline"
|
||||
label: "123"
|
||||
show_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "altline"
|
||||
label: "abc"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
91
data/keyboards/ch+fr.yaml
Normal file
91
data/keyboards/ch+fr.yaml
Normal file
@ -0,0 +1,91 @@
|
||||
# Maintained by: Jordi Bossy <jordi@bossy.space>. No Copyright, enjoy!
|
||||
---
|
||||
outlines:
|
||||
default: { width: 35.33, height: 52 }
|
||||
altline: { width: 48, height: 52 }
|
||||
wide: { width: 59, height: 52 }
|
||||
spaceline: { width: 70, height: 52 }
|
||||
special: { width: 28, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "q w e r t z u i o p"
|
||||
- "a s d f g h j k l ?"
|
||||
- "Shift_L y x c v b n m BackSpace"
|
||||
- "show_numbers show_eschars preferences ' space , . Return"
|
||||
upper:
|
||||
- "Q W E R T Z U I O P"
|
||||
- "A S D F G H J K L !"
|
||||
- "Shift_L Y 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 # & / \\ √ ; : BackSpace"
|
||||
- "show_letters show_eschars preferences _ space , . Return"
|
||||
symbols:
|
||||
- "€ $ £ ¥ % | § µ [ ]"
|
||||
- "© ® § ` ^ { } · ¡ ¿"
|
||||
- "show_numbers « » ÷ × “ ” „ BackSpace"
|
||||
- "show_letters show_eschars preferences - space , . Return"
|
||||
eschars:
|
||||
- "à â ç é è ê î ô ù û"
|
||||
- "À Â Ç É È Ê Î Ô Ù Û"
|
||||
- "show_numbers æ œ ä ë ï ö ü BackSpace"
|
||||
- "show_letters_from_accents 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_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "altline"
|
||||
label: "abc"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
show_eschars:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "âÂ"
|
||||
show_letters_from_accents:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "âÂ"
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
93
data/keyboards/ch.yaml
Normal file
93
data/keyboards/ch.yaml
Normal file
@ -0,0 +1,93 @@
|
||||
# Maintained by: Jordy Bossy <jordi@bossy.space>
|
||||
# and Patrick Jörg <patrickjoerg@gmx.ch>. No Copyright, enjoy!
|
||||
|
||||
---
|
||||
outlines:
|
||||
default: { width: 35.33, height: 52 }
|
||||
altline: { width: 48, height: 52 }
|
||||
wide: { width: 59, height: 52 }
|
||||
spaceline: { width: 70, height: 52 }
|
||||
special: { width: 28, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "q w e r t z u i o p ü"
|
||||
- "a s d f g h j k l ö ä"
|
||||
- "Shift_L y x c v b n m BackSpace"
|
||||
- "show_numbers show_eschars preferences ' space , . Return"
|
||||
upper:
|
||||
- "Q W E R T Z U I O P Ü"
|
||||
- "A S D F G H J K L Ö Ä"
|
||||
- "Shift_L Y 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 # & / \\ √ ; : BackSpace"
|
||||
- "show_letters show_eschars preferences _ space , . Return"
|
||||
symbols:
|
||||
- "€ $ £ ¥ % | § µ [ ]"
|
||||
- "© ® § ` ^ { } · ¡ ¿"
|
||||
- "show_numbers « » ÷ × “ ” „ BackSpace"
|
||||
- "show_letters show_eschars preferences - space , . Return"
|
||||
eschars:
|
||||
- "à â ç é è ê î ô ù û"
|
||||
- "À Â Ç É È Ê Î Ô Ù Û"
|
||||
- "show_numbers æ œ ä ë ï ö ü BackSpace"
|
||||
- "show_letters_from_accents 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_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "altline"
|
||||
label: "abc"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
show_eschars:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "âÂ"
|
||||
show_letters_from_accents:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "âÂ"
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
93
data/keyboards/ch_wide.yaml
Normal file
93
data/keyboards/ch_wide.yaml
Normal file
@ -0,0 +1,93 @@
|
||||
# Maintained by: Jordy Bossy <jordy@bossy.space>
|
||||
# and Patrick Jörg <patrickjoerg@gmx.ch>. No Copyright, enjoy!
|
||||
|
||||
---
|
||||
outlines:
|
||||
default: { width: 48, height: 42 }
|
||||
altline: { width: 81, height: 42 }
|
||||
wide: { width: 108, height: 42 }
|
||||
spaceline: { width: 216, height: 42 }
|
||||
special: { width: 48, height: 42 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "q w e r t z u i o p ü"
|
||||
- "a s d f g h j k l ö ä"
|
||||
- "Shift_L y x c v b n m BackSpace"
|
||||
- "show_numbers show_eschars preferences ' space , . Return"
|
||||
upper:
|
||||
- "Q W E R T Z U I O P Ü"
|
||||
- "A S D F G H J K L Ö Ä"
|
||||
- "Shift_L Y 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 # & / \\ √ ; : BackSpace"
|
||||
- "show_letters show_eschars preferences _ space , . Return"
|
||||
symbols:
|
||||
- "€ $ £ ¥ % | § µ [ ]"
|
||||
- "© ® § ` ^ { } · ¡ ¿"
|
||||
- "show_numbers « » ÷ × “ ” „ BackSpace"
|
||||
- "show_letters show_eschars preferences - space , . Return"
|
||||
eschars:
|
||||
- "à â ç é è ê î ô ù û"
|
||||
- "À Â Ç É È Ê Î Ô Ù Û"
|
||||
- "show_numbers æ œ ä ë ï ö ü BackSpace"
|
||||
- "show_letters_from_accents 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_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "altline"
|
||||
label: "abc"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
show_eschars:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "äÄ"
|
||||
show_letters_from_accents:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "âÂ"
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
81
data/keyboards/email/us.yaml
Normal file
81
data/keyboards/email/us.yaml
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
outlines:
|
||||
default: { width: 35.33, height: 52 }
|
||||
altline: { width: 52.67, height: 52 }
|
||||
wide: { width: 62, height: 52 }
|
||||
spaceline: { width: 106.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 preferences space at period Return"
|
||||
upper:
|
||||
- "Q W E R T Y U I O P"
|
||||
- "A S D F G H J K L"
|
||||
- "Shift_L Z X C V B N M BackSpace"
|
||||
- "show_numbers preferences space at period Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # $ % & - _ + ( )"
|
||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||
- "show_letters preferences space at period Return"
|
||||
symbols:
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "© ® £ € ¥ ^ ° * { }"
|
||||
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||
- "show_letters preferences space at period Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "upper"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
icon: "key-shift"
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
action: erase
|
||||
at:
|
||||
outline: "special"
|
||||
text: "@"
|
||||
preferences:
|
||||
action: show_prefs
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
show_numbers:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "wide"
|
||||
label: "123"
|
||||
show_numbers_from_symbols:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "altline"
|
||||
label: "123"
|
||||
show_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "wide"
|
||||
label: "ABC"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
period:
|
||||
outline: "special"
|
||||
text: "."
|
||||
space:
|
||||
outline: "spaceline"
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
text: ":"
|
||||
20
data/keyboards/pin/us.yaml
Normal file
20
data/keyboards/pin/us.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
margins: { top: 4, side: 0, bottom: 4 }
|
||||
outlines:
|
||||
default: { width: 120, height: 52 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "1 2 3"
|
||||
- "4 5 6"
|
||||
- "7 8 9"
|
||||
- "BackSpace 0 Return"
|
||||
|
||||
buttons:
|
||||
BackSpace:
|
||||
icon: "edit-clear-symbolic"
|
||||
action: erase
|
||||
Return:
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
|
||||
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"
|
||||
81
data/keyboards/url/us.yaml
Normal file
81
data/keyboards/url/us.yaml
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
outlines:
|
||||
default: { width: 35.33, height: 52 }
|
||||
altline: { width: 52.67, height: 52 }
|
||||
wide: { width: 62, height: 52 }
|
||||
spaceline: { width: 106.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 preferences space slash period Return"
|
||||
upper:
|
||||
- "Q W E R T Y U I O P"
|
||||
- "A S D F G H J K L"
|
||||
- "Shift_L Z X C V B N M BackSpace"
|
||||
- "show_numbers preferences space slash period Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # $ % & - _ + ( )"
|
||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||
- "show_letters preferences space slash period Return"
|
||||
symbols:
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "© ® £ € ¥ ^ ° * { }"
|
||||
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||
- "show_letters preferences space slash period Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "upper"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
icon: "key-shift"
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
action: erase
|
||||
preferences:
|
||||
action: show_prefs
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
show_numbers:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "wide"
|
||||
label: "123"
|
||||
show_numbers_from_symbols:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "altline"
|
||||
label: "123"
|
||||
show_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "wide"
|
||||
label: "ABC"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
period:
|
||||
outline: "special"
|
||||
text: "."
|
||||
slash:
|
||||
outline: "special"
|
||||
text: "/"
|
||||
space:
|
||||
outline: "spaceline"
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
text: ":"
|
||||
@ -1,13 +0,0 @@
|
||||
bg Български
|
||||
de Немски
|
||||
es Испански
|
||||
emoji Емоджи
|
||||
fi Френски
|
||||
gr Гръцки
|
||||
it Италянски
|
||||
no Норевежки
|
||||
pl Полски
|
||||
ru Руски
|
||||
se Шведски
|
||||
terminal Терминал
|
||||
us Английски (САЩ)
|
||||
@ -1,21 +0,0 @@
|
||||
be Belgická
|
||||
cz Česká
|
||||
cz+qwerty Česká (QWERTY)
|
||||
de Německá
|
||||
dk Dánská
|
||||
emoji Emoji
|
||||
es Španělská
|
||||
fi Finská
|
||||
fr Francouzská
|
||||
gr Řecká
|
||||
it Italská
|
||||
jp Japonská
|
||||
jp+kana Japonská (Kana)
|
||||
no Norská
|
||||
pl Polská
|
||||
ru Ruská
|
||||
se Švédská
|
||||
terminal Terminál
|
||||
th Thajská
|
||||
ua Ukrajinská
|
||||
us Anglická (USA)
|
||||
@ -1,2 +0,0 @@
|
||||
emoji Emoji
|
||||
terminal Terminal
|
||||
@ -1,7 +0,0 @@
|
||||
us Inglés (EE.UU.)
|
||||
de Alemán
|
||||
el Griego
|
||||
es Español
|
||||
it Italiano
|
||||
jp+kana Japonés (Kana)
|
||||
no Noruego
|
||||
@ -1,2 +0,0 @@
|
||||
emoji ایموجی
|
||||
terminal ترمینال
|
||||
@ -1,18 +0,0 @@
|
||||
be Belgjic
|
||||
br Brasilian
|
||||
de Todesc
|
||||
dk Danês
|
||||
es Spagnûl
|
||||
fi Finlandês
|
||||
fr Francês
|
||||
it+fur Furlan
|
||||
gr Grêc
|
||||
it Talian
|
||||
jp+kana Gjaponês (Kana)
|
||||
no Norvegjês
|
||||
pl Polac
|
||||
ru Rus
|
||||
se Svedês
|
||||
terminal Terminâl
|
||||
ua Ucrain
|
||||
us American (USA)
|
||||
@ -1,19 +0,0 @@
|
||||
be בלגית
|
||||
br פורטוגזית (ברזיל)
|
||||
cz צ'כית
|
||||
de גרמנית
|
||||
dk דנית
|
||||
es ספרדית
|
||||
emoji אימוג'י
|
||||
fi פינית
|
||||
fr צרפתית
|
||||
gr יוונית
|
||||
il עברית
|
||||
it איטלקית
|
||||
no נורווגית
|
||||
pl פולנית
|
||||
ru רוסית
|
||||
se שוודית
|
||||
terminal טרמינל
|
||||
ua אוקראינית
|
||||
us אנגלית (ארה"ב)
|
||||
@ -1,2 +0,0 @@
|
||||
emoji emoji
|
||||
terminal terminal
|
||||
@ -1,11 +0,0 @@
|
||||
de Немецкий
|
||||
es Испанский
|
||||
fi Финский
|
||||
gr Греческий
|
||||
it Итальянский
|
||||
no Норвежский
|
||||
pl Польский
|
||||
ru Русский
|
||||
se Шведский
|
||||
terminal Терминал
|
||||
us Английский (США)
|
||||
@ -12,7 +12,7 @@ desktopconf.set('bindir', bindir)
|
||||
|
||||
desktop_file = 'sm.puri.Squeekboard.desktop'
|
||||
|
||||
i18n.merge_file('desktop',
|
||||
i18n.merge_file(
|
||||
input: configure_file(
|
||||
input: desktop_file + '.in.in',
|
||||
output: desktop_file + '.in',
|
||||
|
||||
23
data/popover.ui
Normal file
23
data/popover.ui
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<menu id="app-menu">
|
||||
<item>
|
||||
<!-- translators: This is a emmoji keyboard layout -->
|
||||
<attribute name="label" translatable="yes">Emoji</attribute>
|
||||
<attribute name="action">layout</attribute>
|
||||
<attribute name="target">emoji</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<!-- translators: This is a terminal keyboard layout -->
|
||||
<attribute name="label" translatable="yes">Terminal</attribute>
|
||||
<attribute name="action">layout</attribute>
|
||||
<attribute name="target">terminal</attribute>
|
||||
</item>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Keyboard Settings</attribute>
|
||||
<attribute name="action">settings</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
</interface>
|
||||
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<object class="GtkPopoverMenu" id="main_menu">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="submenu">main</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Name=Squeekboard
|
||||
GenericName=Squeekboard Virtual Keyboard
|
||||
Comment=Virtual Keyboard
|
||||
GenericName=On Screen Keyboard
|
||||
Comment=An on screen virtual keyboard
|
||||
Exec=@bindir@/squeekboard
|
||||
Terminal=false
|
||||
Type=Application
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/sm/puri/squeekboard">
|
||||
<file compressed="true">common.css</file>
|
||||
<file compressed="true">style.css</file>
|
||||
<file compressed="true">style-Adwaita:dark.css</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">popup.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">popover.ui</file>
|
||||
<file>icons/key-enter.svg</file>
|
||||
<file>icons/key-shift.svg</file>
|
||||
<file>icons/keyboard-mode-symbolic.svg</file>
|
||||
|
||||
@ -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,20 +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,63 +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'
|
||||
|
||||
|
||||
242
debian/changelog
vendored
242
debian/changelog
vendored
@ -1,3 +1,245 @@
|
||||
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 ]
|
||||
* build: Remove regex crate
|
||||
* ci: Use bookworm image
|
||||
* build: Pin transitive dependencies
|
||||
* cargo: Update Cargo.lock with pinned dependencies
|
||||
* CI: Use byzantium as the base
|
||||
* cargo: Bump dependencies
|
||||
|
||||
[ Guido Günther ]
|
||||
* po: Fix ui file name
|
||||
* entry: Mark as executable
|
||||
* entry: Only activate purpose timer when focused
|
||||
* entry: Add another input hint
|
||||
* Add entry test using GTK4
|
||||
|
||||
[ Rafael Fontenelle ]
|
||||
* Add Brazilian Portuguese translation
|
||||
|
||||
[ Yuri Chornoivan ]
|
||||
* Add Ukrainian translation
|
||||
|
||||
[ Luna Jernberg ]
|
||||
* Add Swedish Translation
|
||||
* Update sv.po
|
||||
* Update LINGUAS
|
||||
|
||||
[ Fabio Tomat ]
|
||||
* Add Friulian translation
|
||||
|
||||
[ Daniel Șerbănescu ]
|
||||
* Add Romanian translation
|
||||
|
||||
[ Matej Urbančič ]
|
||||
* Add Slovenian translation
|
||||
|
||||
[ Nathan Follens ]
|
||||
* Add Dutch translation
|
||||
|
||||
[ Jiri Grönroos ]
|
||||
* Add Finnish translation
|
||||
|
||||
[ Danial Behzadi ]
|
||||
* Add Persian translation
|
||||
|
||||
[ Jordi Mas i Hernandez ]
|
||||
* Add Catalan translation
|
||||
|
||||
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 25 Jan 2022 11:24:04 +0000
|
||||
|
||||
squeekboard (1.15.0-1) experimental; urgency=medium
|
||||
|
||||
[ Khaled Eldoheiri ]
|
||||
* Introduce Arabic keyboard layout
|
||||
|
||||
[ Dorota Czaplejewicz ]
|
||||
* Docs: Release procedure
|
||||
* build: Fix "any" dependency versioning
|
||||
* readme: Mention the layout guide
|
||||
* dbus: Hint that maybe squeekboard is running
|
||||
* readme: Change self-reference to repo to gnome
|
||||
* docs: Move env vars section to debugging
|
||||
* readme: Clarify basic running steps
|
||||
* readme: Put emphasis on being an input method
|
||||
* readme: Update features
|
||||
* ci: Use cached artifacts in the test
|
||||
* ci: Move release test to the start
|
||||
* ci: Start lintian test right after deb build
|
||||
* ci: Add git revision and CI pipeline number to .deb artifacts
|
||||
* ci: Use bookworm image
|
||||
* ci: Reformat yaml file
|
||||
* ci: Include pre-build style check
|
||||
* popover: Fix reentrancy problem
|
||||
* submission: Wrap the structure in a safe wrapper
|
||||
* util: Add ArcWrapped
|
||||
* animation: Prototype a way of handling state and applying it separately
|
||||
* state: Connect the animation state machine to the rest
|
||||
* event_loop: Separate and use for physical keyboard presence
|
||||
* Revert "util: Add ArcWrapped"
|
||||
* Revert "ci: Use bookworm image"
|
||||
* ci: Fix formatting
|
||||
* ci: Make indentation close to original again
|
||||
* cargo: version bump
|
||||
|
||||
[ Jordi ]
|
||||
* Introduce Swiss French keyboard layout
|
||||
* improve accents layout behavior and code cleaning
|
||||
|
||||
[ Plamen Stoev ]
|
||||
* Rename bg to bg+phonetic
|
||||
* Add 'bg' layout
|
||||
* Translate more layout names in Bulgarian
|
||||
|
||||
[ William Wold ]
|
||||
* Show error when Layer Shell is not supported
|
||||
* Update entry.py file path in readme
|
||||
* Update zwp_text_input_v3 (comment changes only)
|
||||
* Update zwp_input_method_v2
|
||||
|
||||
[ Patrick Jörg ]
|
||||
* Introduce Swiss German keyboard layout
|
||||
* Introducing ch+de layout and modified ch.yaml fallback
|
||||
* Added ch_wide
|
||||
|
||||
[ ZenWalker ]
|
||||
* layersurface: avoid duplicate assignment
|
||||
|
||||
[ T. Zack Crawford ]
|
||||
* Update tutorial.md to clarify steps in creating a custom layout
|
||||
|
||||
[ Guido Günther ]
|
||||
* gitlab-ci: Adjust CI tags
|
||||
* gitlab-ci: Drop build for outdated distributions
|
||||
* data: Fix build with meson 0.60.0
|
||||
* main: Remove trailing whitespace
|
||||
* main: Honor --help and -h
|
||||
* eek-renderer: Add log domain
|
||||
* eek-renderer: Fix indentation
|
||||
* eek-renderer: Honor theme changes (Closes: #296)
|
||||
* main: Drop broken support G_BUS_TYPE_SYSTEM
|
||||
* main: Avoid two error variables in the same function
|
||||
* main: Use dark theme when run in a Phosh session (Closes: #242)
|
||||
* gtk-keyboard: Don't set variable to NULL twice in a row
|
||||
* renderer: Use `g_debug ()`
|
||||
* main: Add debug flag to always show squeekboard on start
|
||||
* renderer: Disconnect theme change signal handler
|
||||
* main: Add debug flag to show GTK inspector
|
||||
* README: Document SQUEEKBOARD_DEBUG environment variable
|
||||
* Move style-check to separate script
|
||||
* Honor input-purpose PIN
|
||||
* entry: Use a scrolled window
|
||||
* entry: Set a margin on the grids
|
||||
* entry: Add a random text entry field
|
||||
* imservice: Invoke eekboard_context_service_set_hint_purpose unconditionally
|
||||
(Closes: #311)
|
||||
* langs: Don't use empty translation file (Closes: #313)
|
||||
* Initialize gettext
|
||||
* Reuse the unused popover ui file for i18n (Closes: #315)
|
||||
* po: Add German translation
|
||||
* gresources: Drop popup.ui
|
||||
* Revert "gresources: Drop popup.ui"
|
||||
* gitlab-ci: Add PKG_ONLY
|
||||
* layout: Drop trailing whitespace
|
||||
* Use special pin keyboard
|
||||
* layout: Keep content purpose around
|
||||
* renderer: Set style class based on input purpose
|
||||
* pin: Use less margin
|
||||
* debian: Install translations
|
||||
* debian: Switch to dh 13
|
||||
* debian: Install desktop file
|
||||
* eekboard-context-service: Don't translate property names
|
||||
* server-context-servide: Don't translate application name
|
||||
* data: Make generic name truly generic
|
||||
* po: Add desktop file to translatable files
|
||||
* Add URL and EMail keyboard variants for us (Closes: #65)
|
||||
* gitignore: Drop zanata dir
|
||||
* gitignore: Ignore generated po files
|
||||
* popover: Move Emoji and Terminal to ui file
|
||||
* popover: Add translator notes
|
||||
* popover: Make the ui file match the code file name
|
||||
* Remove emoji and terminal from translations
|
||||
* popover: Don't complain about missing translations
|
||||
* Drop custom translation handling
|
||||
* Drop locale_config
|
||||
* Remove custom translations
|
||||
|
||||
[ PhilProg ]
|
||||
* Add documentation about compositors
|
||||
|
||||
[ Norayr Chilingarian ]
|
||||
* armenian typewriter and phonetic keyboards.
|
||||
* armenian layout also added to meson.build etc.
|
||||
|
||||
[ Arnaud Ferraris ]
|
||||
* resources: add wide FR terminal keyboard
|
||||
|
||||
[ Sebastian Krzyszkowiak ]
|
||||
* renderer: Take context scale into account when drawing icons (Closes: #139)
|
||||
|
||||
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Sun, 19 Dec 2021 14:11:06 +0000
|
||||
|
||||
squeekboard (1.14.0pureos0~amber0) amber-phone; urgency=medium
|
||||
|
||||
[ Dorota Czaplejewicz ]
|
||||
|
||||
1
debian/compat
vendored
1
debian/compat
vendored
@ -1 +0,0 @@
|
||||
10
|
||||
4
debian/control
vendored
4
debian/control
vendored
@ -4,7 +4,7 @@ Priority: optional
|
||||
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
|
||||
Build-Depends:
|
||||
cargo,
|
||||
debhelper (>= 10),
|
||||
debhelper-compat (= 13),
|
||||
meson (>=0.51.0),
|
||||
ninja-build,
|
||||
pkg-config,
|
||||
@ -20,13 +20,13 @@ Build-Depends:
|
||||
librust-gtk+v3-22-dev (>= 0.5),
|
||||
librust-gtk-sys-dev,
|
||||
librust-maplit-1-dev (>= 1.0),
|
||||
librust-regex-1-dev (>= 1.1),
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
2
debian/squeekboard.install
vendored
2
debian/squeekboard.install
vendored
@ -1,2 +1,4 @@
|
||||
tools/squeekboard-restyled usr/bin
|
||||
usr/bin/squeekboard /usr/bin
|
||||
usr/share/applications/
|
||||
usr/share/locale/
|
||||
|
||||
@ -90,6 +90,15 @@ Layouts can be selected using the GNOME Settings application.
|
||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]"
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Besides the environment variables supported by GTK and [GLib](https://docs.gtk.org/glib/running.html) applications
|
||||
squeekboard honors the `SQUEEKBOARD_DEBUG` environment variable which can
|
||||
contain a comma separated list of:
|
||||
|
||||
- `force-show` : Show squeekboard on startup independent of any gsettings or compositor requests
|
||||
- `gtk-inspector`: Spawn [gtk-inspector](https://wiki.gnome.org/Projects/GTK/Inspector)
|
||||
|
||||
Coding
|
||||
------
|
||||
|
||||
@ -111,6 +120,16 @@ User interface modules should:
|
||||
|
||||
### Style
|
||||
|
||||
Note that some portions, like the .gitlab-ci.yml file have accummulated enough style/whitespace conflicts that an enforced style checker is now applied.
|
||||
|
||||
To fix your contributions before submitting a change, use:
|
||||
|
||||
```
|
||||
./tools/style-check_source --apply
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
Code submitted should roughly match the style of surrounding code. Things that will *not* be accepted are ones that often lead to errors:
|
||||
|
||||
- skipping brackets `{}` after every `if()`, `else`, and similar ([SCI CERT C: EXP19-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP19-C.+Use+braces+for+the+body+of+an+if%2C+for%2C+or+while+statement))
|
||||
@ -175,14 +194,74 @@ All Cargo dependencies must be selected in the version available in PureOS, and
|
||||
|
||||
Dependencies must be specified in `Cargo.toml` with 2 numbers: "major.minor". Since bugfix version number is meant to not affect the interface, this allows for safe updates.
|
||||
|
||||
`Cargo.lock` is used for remembering the revisions of all Rust dependencies. It must correspond to the default dependency configuration: without flags to use older or newer versions of dependencies. It should be updated often, preferably with each bugfix revision, and in a commit on its own:
|
||||
Releases
|
||||
----------
|
||||
|
||||
Squeekboard should get a new release every time something interesting comes in. Preferably when there are no known bugs too. People will rely on theose releases, after all.
|
||||
|
||||
### 1. Update `Cargo.toml`.
|
||||
|
||||
While the file is not actually used, it's a good idea to save the config in case some rare bug appears in dependencies.
|
||||
|
||||
```
|
||||
cd build_dir
|
||||
ninja ./Cargo.toml
|
||||
sh /source_path/cargo.sh update
|
||||
cd squeekboard-build
|
||||
.../squeekboard-source/cargo.sh update
|
||||
ninja test
|
||||
cp ./Cargo.lock /source_path/
|
||||
cp ./Cargo.lock .../squeekboard-source
|
||||
```
|
||||
|
||||
Since version 1.9.3, `Cargo.lock` is not actually used by the build system, due to `Cargo.toml` being generated at every build.
|
||||
Then commit the updated `Cargo.lock`.
|
||||
|
||||
### 2. Choose the version number
|
||||
|
||||
Squeekboard tries to use semantic versioning. It's 3 numbers separated by dots: "a.b.c". Releases which only fix bugs and nothing else are "a.b.c+1". Releases which add user-visible features in addition to bug fixes are "a.b+1.0". Releases which, in addition to the previous, change *the user contract* in incompatible ways are "a+1.0.0". "The user contract" means plugin APIs that are deemed stable, or the way language switching works, etc. In other words, incompatible changes to developers, or big changes to users bump "a" to the next natural number.
|
||||
|
||||
### 3. Update the number in `meson.build`
|
||||
|
||||
It's in the `project(version: xxx)` statement.
|
||||
|
||||
### 4. Update packaging
|
||||
|
||||
Packaging is in the `debian/` directory, and creates builds that can be quickly tested.
|
||||
|
||||
```
|
||||
cd squeekboard-source
|
||||
gbp dch --multimaint-merge --ignore-branch
|
||||
```
|
||||
|
||||
Inspect `debian/changelog`, and make sure the first line contains the correct version number and suite. For example:
|
||||
|
||||
```
|
||||
squeekboard (1.13.0pureos0~amber0) amber-phone; urgency=medium
|
||||
```
|
||||
|
||||
Commit the updated `debian/changelog`. The commit message should contain the release version and a description of changes.
|
||||
|
||||
> Release 1.13.0 "Externality"
|
||||
>
|
||||
> Changes:
|
||||
>
|
||||
> - A system for latching and locking views
|
||||
> ...
|
||||
|
||||
### 5. Create a signed tag for downstreams
|
||||
|
||||
The tag should be the version number with "v" in front of it. The tag message should be "squeekboard" and the tag name. Push it to the upstream repository:
|
||||
|
||||
```
|
||||
git tag -s -u my_address@example.com v1.13.0 -m "squeekboard v1.13.0"
|
||||
git push v1.13.0
|
||||
```
|
||||
|
||||
### 5. Create a signed tag for packaging
|
||||
|
||||
Similar to the above, but format it for the PureOS downstream.
|
||||
|
||||
```
|
||||
git tag -s -u my_address@example.com 'pureos/1.13.0pureos0_amber0' -m 'squeekboard 1.13.0pureos0_amber0'
|
||||
git push 'pureos/1.13.0pureos0_amber0'
|
||||
```
|
||||
|
||||
### 6. Rejoice
|
||||
|
||||
You released a new version of Squeekboard, and made it available on PureOS. Congratulations.
|
||||
|
||||
@ -21,7 +21,8 @@ Creating a layout is easy. You don't need to recompile things, just edit and tes
|
||||
### Creating the keyboard layout
|
||||
|
||||
* To be written: For the time being, take a look at [Using non-latin language on Librem 5](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5)
|
||||
* The correct name of the .yaml file can be found with the command
|
||||
* Select and enable the input source you would like to change from the Region & Language section of the device settings. Perhaps use "A user-defined custom layout" listed under Other.
|
||||
* Find the correct name of the .yaml file associated with that input source. This can be found with the command
|
||||
|
||||
```
|
||||
gsettings get org.gnome.desktop.input-sources sources
|
||||
@ -29,12 +30,14 @@ gsettings get org.gnome.desktop.input-sources sources
|
||||
|
||||
The output should be something like this: `[('xkb', 'us'), ('xkb', 'de')]`
|
||||
So for example “de.yaml” would be the correct name for the German keyboard layout.
|
||||
|
||||
If the name of your layout is not translated correctly in the list, you can fix it by adding it and recompiling Squeekboard.
|
||||
|
||||
There is also associated files for that layout in landscape, terminal, number, emoji mode. They can be found at something analogous to `us_wide.yaml`, `terminal/us.yaml`, `number/us.yaml`, `emoji/us.yaml`, respectively.
|
||||
|
||||
### Testing the layout
|
||||
|
||||
Copy your yaml file to `~/.local/share/squeekboard/keyboards/` for testing purposes. From there it should get picked up by squeekboard automatically.
|
||||
The yaml file will overwrite the default settings for that layout. If you want to go back to default, simply remove the file.
|
||||
|
||||
You can also use the `test_layout` tool from the -devel package to check it for errors:
|
||||
|
||||
|
||||
@ -316,7 +316,6 @@ eek_gtk_keyboard_dispose (GObject *object)
|
||||
if (priv->renderer) {
|
||||
eek_renderer_free(priv->renderer);
|
||||
priv->renderer = NULL;
|
||||
priv->renderer = NULL;
|
||||
}
|
||||
|
||||
if (priv->keyboard) {
|
||||
|
||||
@ -18,6 +18,8 @@
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "squeekboard-eek-renderer"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
@ -54,6 +56,13 @@ render_outline (cairo_t *cr,
|
||||
position.x, position.y, position.width, position.height);
|
||||
}
|
||||
|
||||
float get_scale(cairo_t *cr) {
|
||||
double width = 1;
|
||||
double height = 1;
|
||||
cairo_user_to_device_distance (cr, &width, &height);
|
||||
return width;
|
||||
}
|
||||
|
||||
/// Rust interface
|
||||
void eek_render_button_in_context(uint32_t scale_factor,
|
||||
cairo_t *cr,
|
||||
@ -70,16 +79,17 @@ void eek_render_button_in_context(uint32_t scale_factor,
|
||||
|
||||
/* render icon (if any) */
|
||||
if (icon_name) {
|
||||
int context_scale = ceil (get_scale (cr));
|
||||
cairo_surface_t *icon_surface =
|
||||
eek_renderer_get_icon_surface (icon_name, 16, scale_factor);
|
||||
eek_renderer_get_icon_surface (icon_name, 16, scale_factor * context_scale);
|
||||
if (icon_surface) {
|
||||
gint width = cairo_image_surface_get_width (icon_surface);
|
||||
gint height = cairo_image_surface_get_height (icon_surface);
|
||||
double width = cairo_image_surface_get_width (icon_surface);
|
||||
double height = cairo_image_surface_get_height (icon_surface);
|
||||
|
||||
cairo_save (cr);
|
||||
cairo_translate (cr,
|
||||
(bounds.width - (double)width / scale_factor) / 2,
|
||||
(bounds.height - (double)height / scale_factor) / 2);
|
||||
(bounds.width - width / (scale_factor * context_scale)) / 2,
|
||||
(bounds.height - height / (scale_factor * context_scale)) / 2);
|
||||
cairo_rectangle (cr, 0, 0, width, height);
|
||||
cairo_clip (cr);
|
||||
/* Draw the shape of the icon using the foreground color */
|
||||
@ -112,7 +122,7 @@ eek_get_style_context_for_button (EekRenderer *self,
|
||||
const char *name,
|
||||
const char *outline_name,
|
||||
const char *locked_class,
|
||||
uint64_t pressed)
|
||||
uint64_t pressed)
|
||||
{
|
||||
GtkStyleContext *ctx = self->button_context;
|
||||
/* Set the name of the button on the widget path, using the name obtained
|
||||
@ -226,6 +236,8 @@ eek_renderer_free (EekRenderer *self)
|
||||
g_object_unref(self->css_provider);
|
||||
g_object_unref(self->view_context);
|
||||
g_object_unref(self->button_context);
|
||||
g_clear_signal_handler (&self->theme_name_id, gtk_settings_get_default());
|
||||
|
||||
// this is where renderer-specific surfaces would be released
|
||||
|
||||
free(self);
|
||||
@ -257,12 +269,49 @@ static GType button_type(void) {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_gtk_theme_name_changed (GtkSettings *settings, gpointer foo, EekRenderer *self)
|
||||
{
|
||||
g_autofree char *name = NULL;
|
||||
|
||||
g_object_get (settings, "gtk-theme-name", &name, NULL);
|
||||
g_debug ("GTK theme: %s", name);
|
||||
|
||||
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (self->css_provider));
|
||||
gtk_style_context_remove_provider (self->button_context,
|
||||
GTK_STYLE_PROVIDER(self->css_provider));
|
||||
gtk_style_context_remove_provider (self->view_context,
|
||||
GTK_STYLE_PROVIDER(self->css_provider));
|
||||
|
||||
g_set_object (&self->css_provider, squeek_load_style());
|
||||
|
||||
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (self->css_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
gtk_style_context_add_provider (self->button_context,
|
||||
GTK_STYLE_PROVIDER(self->css_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
gtk_style_context_add_provider (self->view_context,
|
||||
GTK_STYLE_PROVIDER(self->css_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
renderer_init (EekRenderer *self)
|
||||
{
|
||||
self->pcontext = NULL;
|
||||
self->scale_factor = 1;
|
||||
|
||||
GtkSettings *gtk_settings;
|
||||
|
||||
gtk_settings = gtk_settings_get_default ();
|
||||
|
||||
self->theme_name_id = g_signal_connect (gtk_settings, "notify::gtk-theme-name",
|
||||
G_CALLBACK (on_gtk_theme_name_changed), self);
|
||||
|
||||
self->css_provider = squeek_load_style();
|
||||
}
|
||||
|
||||
@ -274,6 +323,7 @@ eek_renderer_new (LevelKeyboard *keyboard,
|
||||
renderer_init(renderer);
|
||||
renderer->pcontext = pcontext;
|
||||
g_object_ref (renderer->pcontext);
|
||||
const char *purpose_class = "normal";
|
||||
|
||||
/* Create a style context for the layout */
|
||||
GtkWidgetPath *path = gtk_widget_path_new();
|
||||
@ -295,6 +345,55 @@ eek_renderer_new (LevelKeyboard *keyboard,
|
||||
if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
|
||||
gtk_widget_path_iter_add_class(path, -1, "wide");
|
||||
}
|
||||
/* Add style classes based on purpose */
|
||||
switch (squeek_layout_get_purpose (keyboard->layout)) {
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL:
|
||||
purpose_class = "normal";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ALPHA:
|
||||
purpose_class = "alpha";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DIGITS:
|
||||
purpose_class = "digits";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
|
||||
purpose_class = "number";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
|
||||
purpose_class = "phone";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_URL:
|
||||
purpose_class = "url";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL:
|
||||
purpose_class = "email";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME:
|
||||
purpose_class = "name";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD:
|
||||
purpose_class = "password";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN:
|
||||
purpose_class = "pin";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATE:
|
||||
purpose_class = "date";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TIME:
|
||||
purpose_class = "time";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATETIME:
|
||||
purpose_class = "datetime";
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
|
||||
purpose_class = "terminal";
|
||||
break;
|
||||
default:
|
||||
g_warning ("Unknown input purpose %d", squeek_layout_get_purpose(keyboard->layout));
|
||||
}
|
||||
gtk_widget_path_iter_add_class(path, -1, purpose_class);
|
||||
|
||||
gtk_widget_path_append_type(path, button_type());
|
||||
renderer->button_context = gtk_style_context_new ();
|
||||
gtk_style_context_set_path(renderer->button_context, path);
|
||||
|
||||
@ -39,6 +39,8 @@ typedef struct EekRenderer
|
||||
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
|
||||
/// Style class for rendering the view and button CSS.
|
||||
gchar *extra_style; // owned
|
||||
// Theme name change signal handler id
|
||||
gulong theme_name_id;
|
||||
|
||||
// Mutable state
|
||||
gint scale_factor; /* the outputs scale factor */
|
||||
|
||||
@ -312,7 +312,6 @@ on_phosh_layer_surface_unmapped (PhoshLayerSurface *self, gpointer unused)
|
||||
PhoshLayerSurfacePrivate *priv;
|
||||
|
||||
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self));
|
||||
priv = phosh_layer_surface_get_instance_private (self);
|
||||
|
||||
priv = phosh_layer_surface_get_instance_private (self);
|
||||
if (priv->layer_surface) {
|
||||
|
||||
@ -217,7 +217,7 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
|
||||
* Emitted when @context is destroyed.
|
||||
*/
|
||||
signals[DESTROYED] =
|
||||
g_signal_new (I_("destroyed"),
|
||||
g_signal_new ("destroyed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
|
||||
28
meson.build
28
meson.build
@ -1,7 +1,7 @@
|
||||
project(
|
||||
'squeekboard',
|
||||
'c', 'rust',
|
||||
version: '1.14.0',
|
||||
version: '1.17.1',
|
||||
license: 'GPLv3',
|
||||
meson_version: '>=0.51.0',
|
||||
default_options: [
|
||||
@ -36,8 +36,6 @@ add_project_arguments(
|
||||
|
||||
i18n = import('i18n')
|
||||
|
||||
conf_data = configuration_data()
|
||||
|
||||
if get_option('buildtype').startswith('debug')
|
||||
add_project_arguments('-DDEBUG=1', language : 'c')
|
||||
endif
|
||||
@ -63,6 +61,7 @@ endif
|
||||
prefix = get_option('prefix')
|
||||
bindir = join_paths(prefix, get_option('bindir'))
|
||||
datadir = join_paths(prefix, get_option('datadir'))
|
||||
localedir = join_paths(prefix, get_option('localedir'))
|
||||
desktopdir = join_paths(datadir, 'applications')
|
||||
pkgdatadir = join_paths(datadir, meson.project_name())
|
||||
if get_option('depdatadir') == ''
|
||||
@ -72,6 +71,10 @@ else
|
||||
endif
|
||||
dbusdir = join_paths(depdatadir, 'dbus-1/interfaces')
|
||||
|
||||
conf_data = configuration_data()
|
||||
conf_data.set_quoted('GETTEXT_PACKAGE', 'squeekboard')
|
||||
conf_data.set_quoted('LOCALEDIR', localedir)
|
||||
|
||||
summary = [
|
||||
'',
|
||||
'------------------',
|
||||
@ -93,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,
|
||||
)
|
||||
|
||||
@ -114,6 +121,7 @@ cargo_script = find_program('cargo.sh')
|
||||
cargo_build = find_program('cargo_build.py')
|
||||
|
||||
subdir('data')
|
||||
subdir('po')
|
||||
subdir('protocols')
|
||||
subdir('src')
|
||||
subdir('tools')
|
||||
|
||||
@ -7,10 +7,14 @@ 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,
|
||||
description: 'Turn more warnings into errors')
|
||||
|
||||
13
po/LINGUAS
Normal file
13
po/LINGUAS
Normal file
@ -0,0 +1,13 @@
|
||||
ca
|
||||
de
|
||||
fa
|
||||
fi
|
||||
fur
|
||||
gl
|
||||
he
|
||||
nl
|
||||
pt_BR
|
||||
ro
|
||||
sl
|
||||
uk
|
||||
sv
|
||||
2
po/POTFILES.in
Normal file
2
po/POTFILES.in
Normal file
@ -0,0 +1,2 @@
|
||||
data/popover.ui
|
||||
data/sm.puri.Squeekboard.desktop.in.in
|
||||
45
po/ca.po
Normal file
45
po/ca.po
Normal file
@ -0,0 +1,45 @@
|
||||
# Catalan translation for squeekboard.
|
||||
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# maite <maite.guix@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-01-11 14:31+0000\n"
|
||||
"PO-Revision-Date: 2022-01-20 10:53+0100\n"
|
||||
"Last-Translator: maite guix <maite.guix@me.com>\n"
|
||||
"Language-Team: Catalan <gnome@llistes.softcatala.org>\n"
|
||||
"Language: ca\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.0.1\n"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Emoji"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Terminal"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Configuració del teclat"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:3
|
||||
msgid "Squeekboard"
|
||||
msgstr "Teclat virtual"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:4
|
||||
msgid "On Screen Keyboard"
|
||||
msgstr "Teclat en pantalla"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "Un teclat virtual en pantalla"
|
||||
22
po/de.po
Normal file
22
po/de.po
Normal file
@ -0,0 +1,22 @@
|
||||
# German translations for squeekboard package.
|
||||
# Copyright (C) 2021 THE squeekboard'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# Automatically generated, 2021.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: squeekboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-12-03 18:41+0100\n"
|
||||
"PO-Revision-Date: 2021-12-03 18:41+0100\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ASCII\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: data/popup.ui:15
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Tastatureinstellungen"
|
||||
45
po/fa.po
Normal file
45
po/fa.po
Normal file
@ -0,0 +1,45 @@
|
||||
# Persian translation for squeekboard.
|
||||
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# Danial Behzadi <dani.behzi@ubuntu.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: 2021-12-26 15:15+0000\n"
|
||||
"PO-Revision-Date: 2022-01-11 18:01+0330\n"
|
||||
"Language-Team: Persian <fa@li.org>\n"
|
||||
"Language: fa\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Last-Translator: Danial Behzadi <dani.behzi@ubuntu.com>\n"
|
||||
"X-Generator: Poedit 3.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 "اسکوییکبرد"
|
||||
|
||||
#: 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 "یک صفحهٔ کلید لمسی مجازی"
|
||||
46
po/fi.po
Normal file
46
po/fi.po
Normal file
@ -0,0 +1,46 @@
|
||||
# Finnish translation for squeekboard.
|
||||
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# Jiri Grönroos <jiri.gronroos@iki.fi>, 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-25 13:55+0000\n"
|
||||
"PO-Revision-Date: 2021-12-26 17:15+0200\n"
|
||||
"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
|
||||
"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
|
||||
"Language: fi\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"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Emoji"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Pääte"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Näppäimistön asetukset"
|
||||
|
||||
#: 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 "Näyttönäppäimistö"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "Virtuaalinen näyttönäppäimistö"
|
||||
45
po/fur.po
Normal file
45
po/fur.po
Normal file
@ -0,0 +1,45 @@
|
||||
# Friulian translation for squeekboard.
|
||||
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# Fabio Tomat <f.t.public@gmail.com>, 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 13:33+0000\n"
|
||||
"PO-Revision-Date: 2021-12-22 15:06+0100\n"
|
||||
"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
|
||||
"Language-Team: Friulian <fur@li.org>\n"
|
||||
"Language: fur\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.0.1\n"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Emoji"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Terminâl"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Impostazions tastiere"
|
||||
|
||||
#: 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 "Tastiere a visôr"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "Une tastiere virtuâl a visôr"
|
||||
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
po/meson.build
Normal file
2
po/meson.build
Normal file
@ -0,0 +1,2 @@
|
||||
i18n = import('i18n')
|
||||
i18n.gettext('squeekboard', preset : 'glib')
|
||||
48
po/nl.po
Normal file
48
po/nl.po
Normal file
@ -0,0 +1,48 @@
|
||||
# Dutch translation for squeekboard.
|
||||
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# Jan Jasper de Kroon <jajadekroon@gmail.com>, 2021.
|
||||
# Nathan Follens <nfollens@gnome.org>, 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-23 15:18+0000\n"
|
||||
"PO-Revision-Date: 2021-12-25 14:04+0100\n"
|
||||
"Last-Translator: Nathan Follens <nfollens@gnome.org>\n"
|
||||
"Language-Team: Dutch <gnome-nl-list@gnome.org>\n"
|
||||
"Language: nl\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"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Emoji"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Terminal"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Toetsenbordinstellingen"
|
||||
|
||||
# Dit is de naam van de applicatie
|
||||
#: 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 "Schermtoetsenbord"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "Een virtueel schermtoetsenbord"
|
||||
47
po/pt_BR.po
Normal file
47
po/pt_BR.po
Normal file
@ -0,0 +1,47 @@
|
||||
# Brazilian Portuguese translation for squeekboard.
|
||||
# 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 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\n"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Emoji"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Terminal"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Configurações do 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 virtual"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "Um teclado virtual"
|
||||
47
po/ro.po
Normal file
47
po/ro.po
Normal file
@ -0,0 +1,47 @@
|
||||
# Romanian translation for squeekboard.
|
||||
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# libre <eposta1@pm.me>, 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 14:45+0000\n"
|
||||
"PO-Revision-Date: 2021-12-22 20:05+0100\n"
|
||||
"Last-Translator: libre <eposta1@pm.me>\n"
|
||||
"Language-Team: Romanian <gnomero-list@lists.sourceforge.net>\n"
|
||||
"Language: ro\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
|
||||
"20)) ? 1 : 2);;\n"
|
||||
"X-Generator: Gtranslator 3.30.1\n"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Emoji"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Terminal"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Opțiuni tastatură"
|
||||
|
||||
#: 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 "Tastatură pe ecran"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "O tastatură virtuală pe ecran"
|
||||
49
po/sl.po
Normal file
49
po/sl.po
Normal file
@ -0,0 +1,49 @@
|
||||
# Slovenian translation for squeekboard.
|
||||
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
#
|
||||
# Matej Urbančič <mateju@src.gnome.org>, 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 19:14+0000\n"
|
||||
"PO-Revision-Date: 2021-12-23 16:17+0100\n"
|
||||
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
|
||||
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
|
||||
"Language: sl_SI\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n"
|
||||
"%100==4 ? 3 : 0);\n"
|
||||
"X-Poedit-SourceCharset: utf-8\n"
|
||||
"X-Generator: Poedit 3.0.1\n"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Izrazne ikone"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Terminal"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Nastavitve tipkovnice"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:3
|
||||
msgid "Squeekboard"
|
||||
msgstr "Cvilkovnica"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:4
|
||||
msgid "On Screen Keyboard"
|
||||
msgstr "Zaslonska tipkovnica"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "Navidezna zaslonska tipkovnica"
|
||||
48
po/sv.po
Normal file
48
po/sv.po
Normal file
@ -0,0 +1,48 @@
|
||||
# Swedish translations for squeekboard package.
|
||||
# Copyright (C) 2021 THE squeekboard'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
# Automatically generated, 2021.
|
||||
#
|
||||
# Luna Jernberg <droidbittin@gmail.com>, 2021.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2021-12-22 12:47+0000\n"
|
||||
"PO-Revision-Date: 2021-12-22 14:15+0100\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.0\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: sv\n"
|
||||
|
||||
#. translators: This is a emmoji keyboard layout
|
||||
#: data/popover.ui:6
|
||||
msgid "Emoji"
|
||||
msgstr "Emoji"
|
||||
|
||||
#. translators: This is a terminal keyboard layout
|
||||
#: data/popover.ui:12
|
||||
msgid "Terminal"
|
||||
msgstr "Terminal"
|
||||
|
||||
#: data/popover.ui:18
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Tangentbordsinställningar"
|
||||
|
||||
#: 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 "Skärmtangentbord"
|
||||
|
||||
#: data/sm.puri.Squeekboard.desktop.in.in:5
|
||||
msgid "An on screen virtual keyboard"
|
||||
msgstr "Ett virtuellt skärmtangentbord"
|
||||
51
po/uk.po
Normal file
51
po/uk.po
Normal file
@ -0,0 +1,51 @@
|
||||
# Ukrainian translation for squeekboard.
|
||||
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the squeekboard package.
|
||||
#
|
||||
# Yuri Chornoivan <yurchor@ukr.net>, 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 14:46+0200\n"
|
||||
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
|
||||
"Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
|
||||
"Language: uk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Lokalize 20.12.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,6 +2,9 @@
|
||||
<protocol name="input_method_unstable_v2">
|
||||
|
||||
<copyright>
|
||||
Copyright © 2008-2011 Kristian Høgsberg
|
||||
Copyright © 2010-2011 Intel Corporation
|
||||
Copyright © 2012-2013 Collabora, Ltd.
|
||||
Copyright © 2012, 2013 Intel Corporation
|
||||
Copyright © 2015, 2016 Jan Arne Petersen
|
||||
Copyright © 2017, 2018 Red Hat, Inc.
|
||||
@ -29,14 +32,14 @@
|
||||
|
||||
<description summary="Protocol for creating input methods">
|
||||
This protocol allows applications to act as input methods for compositors.
|
||||
|
||||
|
||||
An input method context is used to manage the state of the input method.
|
||||
|
||||
Text strings are UTF-8 encoded, their indices and lengths are in bytes.
|
||||
|
||||
This document adheres to the RFC 2119 when using words like "must",
|
||||
"should", "may", etc.
|
||||
|
||||
|
||||
Warning! The protocol described in this file is experimental and
|
||||
backward incompatible changes may be made. Backward compatible changes
|
||||
may be added together with the corresponding interface version bump.
|
||||
@ -72,18 +75,19 @@
|
||||
Notification that a text input focused on this seat requested the input
|
||||
method to be activated.
|
||||
|
||||
This request must be issued every time a text input requests an input
|
||||
method.
|
||||
This event serves the purpose of providing the compositor with an
|
||||
active input method.
|
||||
|
||||
This request resets all state associated with previous enable, disable,
|
||||
set_surrounding_text, set_text_change_cause, set_content_type, and
|
||||
set_cursor_rectangle requests, as well as the state associated with
|
||||
preedit_string, commit_string, and delete_surrounding_text events. In
|
||||
addition, it marks the input method object as active.
|
||||
This event resets all state associated with previous enable, disable,
|
||||
surrounding_text, text_change_cause, and content_type events, as well
|
||||
as the state associated with set_preedit_string, commit_string, and
|
||||
delete_surrounding_text requests. In addition, it marks the
|
||||
zwp_input_method_v2 object as active, and makes any existing
|
||||
zwp_input_popup_surface_v2 objects visible.
|
||||
|
||||
The set_surrounding_text, set_content_type and set_cursor_rectangle
|
||||
requests must follow before the next done event if the text input
|
||||
supports the respective functionality.
|
||||
The surrounding_text, and content_type events must follow before the
|
||||
next done event if the text input supports the respective
|
||||
functionality.
|
||||
|
||||
State set with this event is double-buffered. It will get applied on
|
||||
the next zwp_input_method_v2.done event, and stay valid until changed.
|
||||
@ -92,13 +96,12 @@
|
||||
|
||||
<event name="deactivate">
|
||||
<description summary="deactivate event">
|
||||
Notification that this seat's current text input requested the input
|
||||
method to be deactivated.
|
||||
Notification that no focused text input currently needs an active
|
||||
input method on this seat.
|
||||
|
||||
This event mrks the zwp_input_method_v2 object as inactive.
|
||||
|
||||
When the seat has the keyboard capability the text-input focus follows
|
||||
the keyboard focus.
|
||||
This event marks the zwp_input_method_v2 object as inactive. The
|
||||
compositor must make all existing zwp_input_popup_surface_v2 objects
|
||||
invisible until the next activate event.
|
||||
|
||||
State set with this event is double-buffered. It will get applied on
|
||||
the next zwp_input_method_v2.done event, and stay valid until changed.
|
||||
@ -107,7 +110,7 @@
|
||||
|
||||
<event name="surrounding_text">
|
||||
<description summary="surrounding text event">
|
||||
Sets the surrounding plain text around the cursor, excluding the
|
||||
Updates the surrounding plain text around the cursor, excluding the
|
||||
preedit text.
|
||||
|
||||
If any preedit text is present, it is replaced with the cursor for the
|
||||
@ -125,7 +128,7 @@
|
||||
buffer. If there is no selected text, anchor must be the same as
|
||||
cursor.
|
||||
|
||||
If this request does not arrive before the first done event, the input
|
||||
If this event does not arrive before the first done event, the input
|
||||
method may assume that the text input does not support this
|
||||
functionality and ignore following surrounding_text events.
|
||||
|
||||
@ -166,7 +169,7 @@
|
||||
<event name="content_type">
|
||||
<description summary="content purpose and hint">
|
||||
Indicates the content type and hint for the current
|
||||
input_method_context instance.
|
||||
zwp_input_method_v2 instance.
|
||||
|
||||
Values set with this event are double-buffered. They will get applied
|
||||
on the next zwp_input_method_v2.done event.
|
||||
@ -213,14 +216,14 @@
|
||||
4000 bytes.
|
||||
|
||||
Values set with this event are double-buffered. They must be applied
|
||||
and reset to initial on the next zwp_text_input_v3.done event.
|
||||
and reset to initial on the next zwp_text_input_v3.commit request.
|
||||
|
||||
The initial value of text is an empty string.
|
||||
</description>
|
||||
<arg name="text" type="string"/>
|
||||
</request>
|
||||
|
||||
<request name="preedit_string">
|
||||
<request name="set_preedit_string">
|
||||
<description summary="pre-edit string">
|
||||
Send the pre-edit string text to the application text input.
|
||||
|
||||
@ -275,7 +278,7 @@
|
||||
|
||||
<request name="commit">
|
||||
<description summary="apply state">
|
||||
Apply state changes from commit_string, preedit_string and
|
||||
Apply state changes from commit_string, set_preedit_string and
|
||||
delete_surrounding_text requests.
|
||||
|
||||
The state relating to these events is double-buffered, and each one
|
||||
@ -294,11 +297,10 @@
|
||||
|
||||
The serial number reflects the last state of the zwp_input_method_v2
|
||||
object known to the client. The value of the serial argument must be
|
||||
equal to the number of done events already issued on that object.
|
||||
When the compositor receives a commit request with a serial different than
|
||||
the number of past commit requests, it must proceed as normal, except
|
||||
it should not change the current state of the zwp_input_method_v2
|
||||
object.
|
||||
equal to the number of done events already issued by that object. When
|
||||
the compositor receives a commit request with a serial different than
|
||||
the number of past done events, it must proceed as normal, except it
|
||||
should not change the current state of the zwp_input_method_v2 object.
|
||||
</description>
|
||||
<arg name="serial" type="uint"/>
|
||||
</request>
|
||||
@ -307,6 +309,10 @@
|
||||
<description summary="create popup surface">
|
||||
Creates a new zwp_input_popup_surface_v2 object wrapping a given
|
||||
surface.
|
||||
|
||||
The surface gets assigned the "input_popup" role. If the surface
|
||||
already has an assigned role, the compositor must issue a protocol
|
||||
error.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwp_input_popup_surface_v2"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
@ -327,9 +333,10 @@
|
||||
|
||||
Releasing the resulting wl_keyboard object releases the grab.
|
||||
</description>
|
||||
<arg name="keyboard" type="new_id" interface="wl_keyboard"/>
|
||||
<arg name="keyboard" type="new_id"
|
||||
interface="zwp_input_method_keyboard_grab_v2"/>
|
||||
</request>
|
||||
|
||||
|
||||
<event name="unavailable">
|
||||
<description summary="input method unavailable">
|
||||
The input method ceased to be available.
|
||||
@ -347,15 +354,25 @@
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor"/>
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the text input">
|
||||
Destroys the zwp_text_input_v2 object and any associated child
|
||||
objects, i.e. zwp_input_popup_surface_v2 and
|
||||
zwp_input_method_keyboard_grab_v2.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwp_input_popup_surface_v2" version="1">
|
||||
<description summary="popup surface">
|
||||
This surface is a popup for interacting with an input method.
|
||||
This interface marks a surface as a popup for interacting with an input
|
||||
method.
|
||||
|
||||
The compositor should place it near the active text input area. It must
|
||||
be visible if and only if the input method is in the active state.
|
||||
|
||||
The client must not destroy the underlying wl_surface while the
|
||||
zwp_input_popup_surface_v2 object exists.
|
||||
</description>
|
||||
|
||||
<event name="text_input_rectangle">
|
||||
@ -375,6 +392,75 @@
|
||||
<request name="destroy" type="destructor"/>
|
||||
</interface>
|
||||
|
||||
<interface name="zwp_input_method_keyboard_grab_v2" version="1">
|
||||
<!-- Closely follows wl_keyboard version 6 -->
|
||||
<description summary="keyboard grab">
|
||||
The zwp_input_method_keyboard_grab_v2 interface represents an exclusive
|
||||
grab of the wl_keyboard interface associated with the seat.
|
||||
</description>
|
||||
|
||||
<event name="keymap">
|
||||
<description summary="keyboard mapping">
|
||||
This event provides a file descriptor to the client which can be
|
||||
memory-mapped to provide a keyboard mapping description.
|
||||
</description>
|
||||
<arg name="format" type="uint" enum="wl_keyboard.keymap_format"
|
||||
summary="keymap format"/>
|
||||
<arg name="fd" type="fd" summary="keymap file descriptor"/>
|
||||
<arg name="size" type="uint" summary="keymap size, in bytes"/>
|
||||
</event>
|
||||
|
||||
<event name="key">
|
||||
<description summary="key event">
|
||||
A key was pressed or released.
|
||||
The time argument is a timestamp with millisecond granularity, with an
|
||||
undefined base.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="serial number of the key event"/>
|
||||
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
|
||||
<arg name="key" type="uint" summary="key that produced the event"/>
|
||||
<arg name="state" type="uint" enum="wl_keyboard.key_state"
|
||||
summary="physical state of the key"/>
|
||||
</event>
|
||||
|
||||
<event name="modifiers">
|
||||
<description summary="modifier and group state">
|
||||
Notifies clients that the modifier and/or group state has changed, and
|
||||
it should update its local state.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="serial number of the modifiers event"/>
|
||||
<arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
|
||||
<arg name="mods_latched" type="uint" summary="latched modifiers"/>
|
||||
<arg name="mods_locked" type="uint" summary="locked modifiers"/>
|
||||
<arg name="group" type="uint" summary="keyboard layout"/>
|
||||
</event>
|
||||
|
||||
<request name="release" type="destructor">
|
||||
<description summary="release the grab object"/>
|
||||
</request>
|
||||
|
||||
<event name="repeat_info">
|
||||
<description summary="repeat rate and delay">
|
||||
Informs the client about the keyboard's repeat rate and delay.
|
||||
|
||||
This event is sent as soon as the zwp_input_method_keyboard_grab_v2
|
||||
object has been created, and is guaranteed to be received by the
|
||||
client before any key press event.
|
||||
|
||||
Negative values for either rate or delay are illegal. A rate of zero
|
||||
will disable any repeating (regardless of the value of delay).
|
||||
|
||||
This event can be sent later on as well with a new value if necessary,
|
||||
so clients should continue listening for the event past the creation
|
||||
of zwp_input_method_keyboard_grab_v2.
|
||||
</description>
|
||||
<arg name="rate" type="int"
|
||||
summary="the rate of repeating keys in characters per second"/>
|
||||
<arg name="delay" type="int"
|
||||
summary="delay in milliseconds since key down until repeating starts"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwp_input_method_manager_v2" version="1">
|
||||
<description summary="input method manager">
|
||||
The input method manager allows the client to become the input method on
|
||||
|
||||
@ -94,6 +94,12 @@
|
||||
zwp_text_input_v3.disable when there is no longer any input focus on
|
||||
the current surface.
|
||||
|
||||
Clients must not enable more than one text input on the single seat
|
||||
and should disable the current text input before enabling the new one.
|
||||
At most one instance of text input may be in enabled state per instance,
|
||||
Requests to enable the another text input when some text input is active
|
||||
must be ignored by compositor.
|
||||
|
||||
This request resets all state associated with previous enable, disable,
|
||||
set_surrounding_text, set_text_change_cause, set_content_type, and
|
||||
set_cursor_rectangle requests, as well as the state associated with
|
||||
@ -307,6 +313,9 @@
|
||||
<description summary="enter event">
|
||||
Notification that this seat's text-input focus is on a certain surface.
|
||||
|
||||
If client has created multiple text input objects, compositor must send
|
||||
this event to all of them.
|
||||
|
||||
When the seat has the keyboard capability the text-input focus follows
|
||||
the keyboard focus. This event sets the current surface for the
|
||||
text-input object.
|
||||
@ -321,7 +330,9 @@
|
||||
set.
|
||||
|
||||
The leave notification clears the current surface. It is sent before
|
||||
the enter notification for the new focus.
|
||||
the enter notification for the new focus. After leave event, compositor
|
||||
must ignore requests from any text input instances until next enter
|
||||
event.
|
||||
|
||||
When the seat has the keyboard capability the text-input focus follows
|
||||
the keyboard focus.
|
||||
|
||||
23
src/animation.rs
Normal file
23
src/animation.rs
Normal file
@ -0,0 +1,23 @@
|
||||
/* Copyright (C) 2020 Purism SPC
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
|
||||
/*! Animation details */
|
||||
|
||||
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 {
|
||||
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"#);
|
||||
}
|
||||
}
|
||||
@ -2,3 +2,9 @@
|
||||
* Autogenerated by the Meson build system.
|
||||
* Do not edit, your changes will be lost.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#mesondefine GETTEXT_PACKAGE
|
||||
|
||||
#mesondefine LOCALEDIR
|
||||
|
||||
@ -61,7 +61,7 @@ pub mod c {
|
||||
};
|
||||
|
||||
let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str);
|
||||
let layout = ::layout::Layout::new(layout, kind);
|
||||
let layout = ::layout::Layout::new(layout, kind, variant);
|
||||
Box::into_raw(Box::new(layout))
|
||||
}
|
||||
}
|
||||
@ -166,10 +166,13 @@ fn get_directory_string(
|
||||
|
||||
let layout_purpose = match overlay {
|
||||
None => match content_purpose {
|
||||
ContentPurpose::Number => Special("number"),
|
||||
ContentPurpose::Email => Special("email"),
|
||||
ContentPurpose::Digits => Special("number"),
|
||||
ContentPurpose::Number => Special("number"),
|
||||
ContentPurpose::Phone => Special("number"),
|
||||
ContentPurpose::Pin => Special("pin"),
|
||||
ContentPurpose::Terminal => Special("terminal"),
|
||||
ContentPurpose::Url => Special("url"),
|
||||
_ => Default,
|
||||
},
|
||||
Some(overlay) => Special(overlay),
|
||||
|
||||
51
src/dbus.c
51
src/dbus.c
@ -19,7 +19,9 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "dbus.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
@ -44,11 +46,6 @@ dbus_handler_destroy(DBusHandler *service)
|
||||
service->introspection_data = NULL;
|
||||
}
|
||||
|
||||
if (service->context) {
|
||||
g_signal_handlers_disconnect_by_data (service->context, service);
|
||||
service->context = NULL;
|
||||
}
|
||||
|
||||
free(service);
|
||||
}
|
||||
|
||||
@ -57,38 +54,25 @@ handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
|
||||
gboolean arg_visible, gpointer user_data) {
|
||||
DBusHandler *service = user_data;
|
||||
|
||||
if (service->context) {
|
||||
if (arg_visible) {
|
||||
server_context_service_force_show_keyboard (service->context);
|
||||
} else {
|
||||
server_context_service_hide_keyboard (service->context);
|
||||
}
|
||||
if (arg_visible) {
|
||||
squeek_state_send_force_visible (service->state_manager);
|
||||
} else {
|
||||
squeek_state_send_force_hidden(service->state_manager);
|
||||
}
|
||||
|
||||
sm_puri_osk0_complete_set_visible(object, invocation);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void on_visible(DBusHandler *service,
|
||||
GParamSpec *pspec,
|
||||
ServerContextService *context)
|
||||
{
|
||||
(void)pspec;
|
||||
gboolean visible;
|
||||
|
||||
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (context));
|
||||
|
||||
g_object_get (context, "visible", &visible, NULL);
|
||||
|
||||
sm_puri_osk0_set_visible(service->dbus_interface, visible);
|
||||
}
|
||||
|
||||
DBusHandler *
|
||||
dbus_handler_new (GDBusConnection *connection,
|
||||
const gchar *object_path)
|
||||
const gchar *object_path,
|
||||
struct squeek_state_manager *state_manager)
|
||||
{
|
||||
DBusHandler *self = calloc(1, sizeof(DBusHandler));
|
||||
self->object_path = g_strdup(object_path);
|
||||
self->connection = connection;
|
||||
self->state_manager = state_manager;
|
||||
|
||||
self->dbus_interface = sm_puri_osk0_skeleton_new();
|
||||
g_signal_connect(self->dbus_interface, "handle-set-visible",
|
||||
@ -109,16 +93,9 @@ dbus_handler_new (GDBusConnection *connection,
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
dbus_handler_set_ui_context(DBusHandler *service,
|
||||
ServerContextService *context)
|
||||
// Exported to Rust
|
||||
void dbus_handler_set_visible(DBusHandler *service,
|
||||
uint8_t visible)
|
||||
{
|
||||
g_return_if_fail (!service->context);
|
||||
|
||||
service->context = context;
|
||||
|
||||
g_signal_connect_swapped (service->context,
|
||||
"notify::visible",
|
||||
G_CALLBACK(on_visible),
|
||||
service);
|
||||
sm_puri_osk0_set_visible(service->dbus_interface, visible);
|
||||
}
|
||||
|
||||
18
src/dbus.h
18
src/dbus.h
@ -19,15 +19,20 @@
|
||||
#ifndef DBUS_H_
|
||||
#define DBUS_H_ 1
|
||||
|
||||
#include "server-context-service.h"
|
||||
|
||||
#include "sm.puri.OSK0.h"
|
||||
|
||||
// From main.h
|
||||
struct squeek_state_manager;
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define DBUS_SERVICE_PATH "/sm/puri/OSK0"
|
||||
#define DBUS_SERVICE_INTERFACE "sm.puri.OSK0"
|
||||
|
||||
/// Two jobs: accept events, forwarding them to the visibility manager,
|
||||
/// and get updated from inside to show internal state.
|
||||
/// Updates are handled in the same loop as the UI.
|
||||
/// See main.rs
|
||||
typedef struct _DBusHandler
|
||||
{
|
||||
GDBusConnection *connection;
|
||||
@ -36,13 +41,14 @@ typedef struct _DBusHandler
|
||||
guint registration_id;
|
||||
char *object_path;
|
||||
|
||||
ServerContextService *context; // unowned reference
|
||||
/// Forward incoming events there
|
||||
struct squeek_state_manager *state_manager; // shared reference
|
||||
} DBusHandler;
|
||||
|
||||
DBusHandler * dbus_handler_new (GDBusConnection *connection,
|
||||
const gchar *object_path);
|
||||
void dbus_handler_set_ui_context(DBusHandler *service,
|
||||
ServerContextService *context);
|
||||
const gchar *object_path,
|
||||
struct squeek_state_manager *state_manager);
|
||||
|
||||
void dbus_handler_destroy(DBusHandler*);
|
||||
G_END_DECLS
|
||||
#endif /* DBUS_H_ */
|
||||
|
||||
@ -7,10 +7,10 @@ use ::action::{ Action, Modifier };
|
||||
use ::keyboard;
|
||||
use ::layout::{ Button, Label, LatchedState, Layout };
|
||||
use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
|
||||
use ::submission::Submission;
|
||||
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;
|
||||
@ -76,10 +76,11 @@ mod c {
|
||||
layout: *mut Layout,
|
||||
renderer: EekRenderer,
|
||||
cr: *mut cairo_sys::cairo_t,
|
||||
submission: *const Submission,
|
||||
submission: CSubmission,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
let submission = unsafe { &*submission };
|
||||
let submission = submission.clone_ref();
|
||||
let submission = submission.borrow();
|
||||
let cr = unsafe { cairo::Context::from_raw_none(cr) };
|
||||
let active_modifiers = submission.get_active_modifiers();
|
||||
|
||||
|
||||
143
src/event_loop/driver.rs
Normal file
143
src/event_loop/driver.rs
Normal file
@ -0,0 +1,143 @@
|
||||
/* Copyright (C) 2021 Purism SPC
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
|
||||
/*! This drives the loop from the `loop` module.
|
||||
*
|
||||
* The tracker loop needs to be driven somehow,
|
||||
* and connected to the external world,
|
||||
* both on the side of receiving and sending events.
|
||||
*
|
||||
* That's going to be implementation-dependent,
|
||||
* connecting to some external mechanisms
|
||||
* for time, messages, and threading/callbacks.
|
||||
*
|
||||
* This is the "imperative shell" part of the software,
|
||||
* and no longer unit-testable.
|
||||
*/
|
||||
|
||||
use crate::event_loop;
|
||||
use crate::logging;
|
||||
use crate::main::Commands;
|
||||
use crate::state::{ Application, Event };
|
||||
use glib;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::time::Instant;
|
||||
|
||||
// Traits
|
||||
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,
|
||||
/// in response to incoming events.
|
||||
/// It sends outcomes to the glib main loop using a channel.
|
||||
/// The outcomes are applied by the UI end of the channel in the `main` module.
|
||||
// This could still be reasonably tested,
|
||||
// by creating a glib::Sender and checking what messages it receives.
|
||||
#[derive(Clone)]
|
||||
pub struct Threaded {
|
||||
thread: Sender,
|
||||
}
|
||||
|
||||
impl Threaded {
|
||||
pub fn new(ui: UISender, initial_state: Application) -> Self {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let saved_sender = sender.clone();
|
||||
thread::spawn(move || {
|
||||
let mut state = event_loop::State::new(initial_state, Instant::now());
|
||||
loop {
|
||||
match receiver.recv() {
|
||||
Ok(event) => {
|
||||
state = Self::handle_loop_event(&sender, state, event, &ui);
|
||||
},
|
||||
Err(e) => {
|
||||
logging::print(logging::Level::Bug, &format!("Senders hung up, aborting: {}", e));
|
||||
return;
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
thread: saved_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&self, event: Event) -> Result<(), mpsc::SendError<Event>> {
|
||||
self.thread.send(event)
|
||||
}
|
||||
|
||||
fn handle_loop_event(loop_sender: &Sender, state: event_loop::State, event: Event, ui: &UISender)
|
||||
-> event_loop::State
|
||||
{
|
||||
let now = Instant::now();
|
||||
|
||||
let (new_state, commands) = event_loop::handle_event(state.clone(), event, now);
|
||||
|
||||
ui.send(commands)
|
||||
.or_warn(&mut logging::Print, logging::Problem::Bug, "Can't send to UI");
|
||||
|
||||
if new_state.scheduled_wakeup != state.scheduled_wakeup {
|
||||
if let Some(when) = new_state.scheduled_wakeup {
|
||||
Self::schedule_timeout_wake(loop_sender, when);
|
||||
}
|
||||
}
|
||||
|
||||
new_state
|
||||
}
|
||||
|
||||
fn schedule_timeout_wake(loop_sender: &Sender, when: Instant) {
|
||||
let sender = loop_sender.clone();
|
||||
thread::spawn(move || {
|
||||
let now = Instant::now();
|
||||
thread::sleep(when - now);
|
||||
sender.send(Event::TimeoutReached(when))
|
||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't wake visibility manager");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// For calling in only
|
||||
mod c {
|
||||
use super::*;
|
||||
|
||||
use crate::state::Presence;
|
||||
use crate::state::visibility;
|
||||
use crate::util::c::Wrapped;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_state_send_force_visible(mgr: Wrapped<Threaded>) {
|
||||
let sender = mgr.clone_ref();
|
||||
let sender = sender.borrow();
|
||||
sender.send(Event::Visibility(visibility::Event::ForceVisible))
|
||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_state_send_force_hidden(sender: Wrapped<Threaded>) {
|
||||
let sender = sender.clone_ref();
|
||||
let sender = sender.borrow();
|
||||
sender.send(Event::Visibility(visibility::Event::ForceHidden))
|
||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_state_send_keyboard_present(sender: Wrapped<Threaded>, present: u32) {
|
||||
let sender = sender.clone_ref();
|
||||
let sender = sender.borrow();
|
||||
let state =
|
||||
if present == 0 { Presence::Missing }
|
||||
else { Presence::Present };
|
||||
sender.send(Event::PhysicalKeyboard(state))
|
||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
|
||||
}
|
||||
}
|
||||
188
src/event_loop/mod.rs
Normal file
188
src/event_loop/mod.rs
Normal file
@ -0,0 +1,188 @@
|
||||
/* Copyright (C) 2021 Purism SPC
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
|
||||
/*! The loop abstraction for driving state changes.
|
||||
* It binds to the state tracker in `state::Application`,
|
||||
* and actually gets driven by a driver in the `driver` module.
|
||||
*
|
||||
* * * *
|
||||
*
|
||||
* If we performed updates in a tight loop,
|
||||
* the state tracker would have been all we need.
|
||||
*
|
||||
* ``
|
||||
* loop {
|
||||
* event = current_event()
|
||||
* outcome = update_state(event)
|
||||
* io.apply(outcome)
|
||||
* }
|
||||
* ``
|
||||
*
|
||||
* This is enough to process all events,
|
||||
* and keep the window always in sync with the current state.
|
||||
*
|
||||
* However, we're trying to be conservative,
|
||||
* and not waste time performing updates that don't change state,
|
||||
* so we have to react to events that end up influencing the state.
|
||||
*
|
||||
* One complication from that is that animation steps
|
||||
* are not a response to events coming from the owner of the loop,
|
||||
* but are needed by the loop itself.
|
||||
*
|
||||
* This is where the rest of bugs hide:
|
||||
* too few scheduled wakeups mean missed updates and wrong visible state.
|
||||
* Too many wakeups can slow down the process, or make animation jittery.
|
||||
* The loop iteration is kept as a pure function to stay testable.
|
||||
*/
|
||||
|
||||
pub mod driver;
|
||||
|
||||
// This module is tightly coupled to the shape of data passed around in this project.
|
||||
// That's not a problem as long as there's only one loop.
|
||||
// They can still be abstracted into Traits,
|
||||
// and the loop parametrized over them.
|
||||
use crate::main::Commands;
|
||||
use crate::state;
|
||||
use crate::state::Event;
|
||||
use std::cmp;
|
||||
use std::time::{ Duration, Instant };
|
||||
|
||||
|
||||
/// This keeps the state of the tracker loop between iterations
|
||||
#[derive(Clone)]
|
||||
struct State {
|
||||
state: state::Application,
|
||||
scheduled_wakeup: Option<Instant>,
|
||||
last_update: Instant,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new(initial_state: state::Application, now: Instant) -> Self {
|
||||
Self {
|
||||
state: initial_state,
|
||||
scheduled_wakeup: None,
|
||||
last_update: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single iteration of the loop, updating its persistent state.
|
||||
/// - updates tracker state,
|
||||
/// - determines outcome,
|
||||
/// - determines next scheduled animation wakeup,
|
||||
/// and because this is a pure function, it's easily testable.
|
||||
/// It returns the new state, and the message to send onwards.
|
||||
fn handle_event(
|
||||
mut loop_state: State,
|
||||
event: Event,
|
||||
now: Instant,
|
||||
) -> (State, Commands) {
|
||||
// Calculate changes to send to the consumer,
|
||||
// based on publicly visible state.
|
||||
// The internal state may change more often than the publicly visible one,
|
||||
// so the resulting changes may be no-ops.
|
||||
let old_state = loop_state.state.clone();
|
||||
let last_update = loop_state.last_update;
|
||||
loop_state.state = loop_state.state.apply_event(event.clone(), now);
|
||||
loop_state.last_update = now;
|
||||
|
||||
let new_outcome = loop_state.state.get_outcome(now);
|
||||
|
||||
let commands = old_state.get_outcome(last_update)
|
||||
.get_commands_to_reach(&new_outcome);
|
||||
|
||||
// Timeout events are special: they affect the scheduled timeout.
|
||||
loop_state.scheduled_wakeup = match event {
|
||||
Event::TimeoutReached(when) => {
|
||||
if when > now {
|
||||
// Special handling for scheduled events coming in early.
|
||||
// Wait at least 10 ms to avoid Zeno's paradox.
|
||||
// This is probably not needed though,
|
||||
// if the `now` contains the desired time of the event.
|
||||
// But then what about time "reversing"?
|
||||
Some(cmp::max(
|
||||
when,
|
||||
now + Duration::from_millis(10),
|
||||
))
|
||||
} else {
|
||||
// There's only one timeout in flight, and it's this one.
|
||||
// It's about to complete, and then the tracker can be cleared.
|
||||
// I'm not sure if this is strictly necessary.
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => loop_state.scheduled_wakeup.clone(),
|
||||
};
|
||||
|
||||
// Reschedule timeout if the new state calls for it.
|
||||
let scheduled = &loop_state.scheduled_wakeup;
|
||||
let desired = loop_state.state.get_next_wake(now);
|
||||
|
||||
loop_state.scheduled_wakeup = match (scheduled, desired) {
|
||||
(&Some(scheduled), Some(next)) => {
|
||||
if scheduled > next {
|
||||
// State wants a wake to happen before the one which is already scheduled.
|
||||
// The previous state is removed in order to only ever keep one in flight.
|
||||
// That hopefully avoids pileups,
|
||||
// e.g. because the system is busy
|
||||
// and the user keeps doing something that queues more events.
|
||||
Some(next)
|
||||
} else {
|
||||
// Not changing the case when the wanted wake is *after* scheduled,
|
||||
// because wakes are not expensive as long as they don't pile up,
|
||||
// and I can't see a pileup potential when it doesn't retrigger itself.
|
||||
// Skipping an expected event is much more dangerous.
|
||||
Some(scheduled)
|
||||
}
|
||||
},
|
||||
(None, Some(next)) => Some(next),
|
||||
// No need to change the unneeded wake - see above.
|
||||
// (Some(_), None) => ...
|
||||
(other, _) => other.clone(),
|
||||
};
|
||||
|
||||
(loop_state, commands)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::animation;
|
||||
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 {
|
||||
purpose: ContentPurpose::Normal,
|
||||
hint: ContentHint::NONE,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schedule_hide() {
|
||||
let start = Instant::now(); // doesn't matter when. It would be better to have a reproducible value though
|
||||
let mut now = start;
|
||||
|
||||
let state = Application {
|
||||
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_matches!(commands.panel_visibility, Some(PanelCommand::Show{..}));
|
||||
assert_eq!(l.scheduled_wakeup, Some(now + animation::HIDING_TIMEOUT));
|
||||
|
||||
now += animation::HIDING_TIMEOUT;
|
||||
|
||||
let (l, commands) = handle_event(l, Event::TimeoutReached(now), now);
|
||||
assert_eq!(commands.panel_visibility, Some(PanelCommand::Hide));
|
||||
assert_eq!(l.scheduled_wakeup, None);
|
||||
}
|
||||
}
|
||||
@ -23,22 +23,6 @@ static const struct zwp_input_method_v2_listener input_method_listener = {
|
||||
.unavailable = imservice_handle_unavailable,
|
||||
};
|
||||
|
||||
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
|
||||
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
|
||||
struct vis_manager *vis_manager,
|
||||
struct wl_seat *seat,
|
||||
EekboardContextService *state) {
|
||||
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 submission_new(im, vk, state, vis_manager);
|
||||
}
|
||||
|
||||
/// Un-inlined
|
||||
struct zwp_input_method_v2 *imservice_manager_get_input_method(struct zwp_input_method_manager_v2 *manager,
|
||||
struct wl_seat *seat) {
|
||||
|
||||
100
src/imservice.rs
100
src/imservice.rs
@ -8,7 +8,11 @@ use std::ffi::CString;
|
||||
use std::fmt;
|
||||
use std::num::Wrapping;
|
||||
use std::string::String;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::event_loop::driver;
|
||||
use crate::state;
|
||||
use crate::state::Event;
|
||||
use ::logging;
|
||||
use ::util::c::into_cstring;
|
||||
|
||||
@ -22,25 +26,32 @@ pub mod c {
|
||||
use super::*;
|
||||
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
pub use ::ui_manager::c::UIManager;
|
||||
pub use ::submission::c::StateManager;
|
||||
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);
|
||||
fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: 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,38 +149,20 @@ 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();
|
||||
let active_changed = imservice.current.active ^ imservice.pending.active;
|
||||
|
||||
imservice.current = imservice.pending.clone();
|
||||
imservice.pending = IMProtocolState {
|
||||
active: imservice.current.active,
|
||||
..IMProtocolState::default()
|
||||
};
|
||||
|
||||
imservice.serial += Wrapping(1u32);
|
||||
|
||||
if active_changed {
|
||||
(imservice.active_callback)(imservice.current.active);
|
||||
if imservice.current.active {
|
||||
unsafe {
|
||||
eekboard_context_service_set_hint_purpose(
|
||||
imservice.state_manager,
|
||||
imservice.current.content_hint.bits(),
|
||||
imservice.current.content_purpose.clone() as u32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
imservice.send_event();
|
||||
}
|
||||
|
||||
// TODO: this is really untested
|
||||
#[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); }
|
||||
@ -178,7 +171,7 @@ pub mod c {
|
||||
// the keyboard is already decommissioned
|
||||
imservice.current.active = false;
|
||||
|
||||
(imservice.active_callback)(imservice.current.active);
|
||||
imservice.send_event();
|
||||
}
|
||||
|
||||
// FIXME: destroy and deallocate
|
||||
@ -194,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() {
|
||||
@ -233,7 +226,7 @@ bitflags!{
|
||||
/// use rs::imservice::ContentPurpose;
|
||||
/// assert_eq!(ContentPurpose::Alpha as u32, 1);
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ContentPurpose {
|
||||
Normal = 0,
|
||||
Alpha = 1,
|
||||
@ -328,10 +321,8 @@ impl Default for IMProtocolState {
|
||||
|
||||
pub struct IMService {
|
||||
/// Owned reference (still created and destroyed in C)
|
||||
pub im: *mut c::InputMethod,
|
||||
/// Unowned reference. Be careful, it's shared with C at large
|
||||
state_manager: *const c::StateManager,
|
||||
active_callback: Box<dyn Fn(bool)>,
|
||||
pub im: c::InputMethod,
|
||||
sender: driver::Threaded,
|
||||
|
||||
pending: IMProtocolState,
|
||||
current: IMProtocolState, // turn current into an idiomatic representation?
|
||||
@ -346,16 +337,14 @@ pub enum SubmitError {
|
||||
|
||||
impl IMService {
|
||||
pub fn new(
|
||||
im: *mut c::InputMethod,
|
||||
state_manager: *const c::StateManager,
|
||||
active_callback: Box<dyn Fn(bool)>,
|
||||
im: c::InputMethod,
|
||||
sender: driver::Threaded,
|
||||
) -> Box<IMService> {
|
||||
// IMService will be referenced to by C,
|
||||
// so it needs to stay in the same place in memory via Box
|
||||
let imservice = Box::new(IMService {
|
||||
im,
|
||||
active_callback,
|
||||
state_manager,
|
||||
sender,
|
||||
pending: IMProtocolState::default(),
|
||||
current: IMProtocolState::default(),
|
||||
preedit_string: String::new(),
|
||||
@ -415,4 +404,21 @@ impl IMService {
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.current.active
|
||||
}
|
||||
|
||||
fn send_event(&self) {
|
||||
let state = &self.current;
|
||||
let timestamp = Instant::now();
|
||||
let message = if state.active {
|
||||
state::InputMethod::Active(
|
||||
state::InputMethodDetails {
|
||||
hint: state.content_hint,
|
||||
purpose: state.content_purpose,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
state::InputMethod::InactiveSince(timestamp)
|
||||
};
|
||||
self.sender.send(Event::InputMethod(message))
|
||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ struct transformation squeek_layout_calculate_transformation(
|
||||
|
||||
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type, uint32_t variant_type, const char *overlay_name);
|
||||
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
|
||||
uint32_t squeek_layout_get_purpose(const struct squeek_layout *);
|
||||
void squeek_layout_free(struct squeek_layout*);
|
||||
|
||||
void squeek_layout_release(struct squeek_layout *layout,
|
||||
|
||||
106
src/layout.rs
106
src/layout.rs
@ -1,17 +1,17 @@
|
||||
/*!
|
||||
* Layout-related data.
|
||||
*
|
||||
*
|
||||
* The `View` contains `Row`s and each `Row` contains `Button`s.
|
||||
* They carry data relevant to their positioning only,
|
||||
* except the Button, which also carries some data
|
||||
* about its appearance and function.
|
||||
*
|
||||
*
|
||||
* The layout is determined bottom-up, by measuring `Button` sizes,
|
||||
* deriving `Row` sizes from them, and then centering them within the `View`.
|
||||
*
|
||||
*
|
||||
* That makes the `View` position immutable,
|
||||
* and therefore different than the other positions.
|
||||
*
|
||||
*
|
||||
* Note that it might be a better idea
|
||||
* to make `View` position depend on its contents,
|
||||
* and let the renderer scale and center it within the widget.
|
||||
@ -32,6 +32,8 @@ use ::manager;
|
||||
use ::submission::{ Submission, SubmitData, Timestamp };
|
||||
use ::util::find_max_double;
|
||||
|
||||
use ::imservice::ContentPurpose;
|
||||
|
||||
// Traits
|
||||
use std::borrow::Borrow;
|
||||
use ::logging::Warn;
|
||||
@ -42,6 +44,7 @@ pub mod c {
|
||||
|
||||
use gtk_sys;
|
||||
use std::os::raw::c_void;
|
||||
use crate::submission::c::Submission as CSubmission;
|
||||
|
||||
use std::ops::{ Add, Sub };
|
||||
|
||||
@ -64,7 +67,7 @@ pub mod c {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
|
||||
impl Add for Point {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
@ -81,7 +84,7 @@ pub mod c {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Sub<&Point> for Point {
|
||||
type Output = Point;
|
||||
fn sub(self, other: &Point) -> Point {
|
||||
@ -152,14 +155,14 @@ pub mod c {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is constructed only in C, no need for warnings
|
||||
#[allow(dead_code)]
|
||||
#[repr(transparent)]
|
||||
pub struct LevelKeyboard(*const c_void);
|
||||
|
||||
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
|
||||
|
||||
|
||||
/// Positions the layout contents within the available space.
|
||||
/// The origin of the transformation is the point inside the margins.
|
||||
#[no_mangle]
|
||||
@ -183,6 +186,13 @@ pub mod c {
|
||||
layout.kind.clone() as u32
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_layout_get_purpose(layout: *const Layout) -> u32 {
|
||||
let layout = unsafe { &*layout };
|
||||
layout.purpose.clone() as u32
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_layout_free(layout: *mut Layout) {
|
||||
@ -198,7 +208,7 @@ pub mod c {
|
||||
pub extern "C"
|
||||
fn squeek_layout_release(
|
||||
layout: *mut Layout,
|
||||
submission: *mut Submission,
|
||||
submission: CSubmission,
|
||||
widget_to_layout: Transformation,
|
||||
time: u32,
|
||||
manager: manager::c::Manager,
|
||||
@ -206,7 +216,8 @@ pub mod c {
|
||||
) {
|
||||
let time = Timestamp(time);
|
||||
let layout = unsafe { &mut *layout };
|
||||
let submission = unsafe { &mut *submission };
|
||||
let submission = submission.clone_ref();
|
||||
let mut submission = submission.borrow_mut();
|
||||
let ui_backend = UIBackend {
|
||||
widget_to_layout,
|
||||
keyboard: ui_keyboard,
|
||||
@ -218,7 +229,7 @@ pub mod c {
|
||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||
seat::handle_release_key(
|
||||
layout,
|
||||
submission,
|
||||
&mut submission,
|
||||
Some(&ui_backend),
|
||||
time,
|
||||
Some(manager),
|
||||
@ -233,18 +244,19 @@ pub mod c {
|
||||
pub extern "C"
|
||||
fn squeek_layout_release_all_only(
|
||||
layout: *mut Layout,
|
||||
submission: *mut Submission,
|
||||
submission: CSubmission,
|
||||
time: u32,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
let submission = unsafe { &mut *submission };
|
||||
let submission = submission.clone_ref();
|
||||
let mut submission = submission.borrow_mut();
|
||||
// The list must be copied,
|
||||
// because it will be mutated in the loop
|
||||
for key in layout.pressed_keys.clone() {
|
||||
let key: &Rc<RefCell<KeyState>> = key.borrow();
|
||||
seat::handle_release_key(
|
||||
layout,
|
||||
submission,
|
||||
&mut submission,
|
||||
None, // don't update UI
|
||||
Timestamp(time),
|
||||
None, // don't switch layouts
|
||||
@ -257,25 +269,26 @@ pub mod c {
|
||||
pub extern "C"
|
||||
fn squeek_layout_depress(
|
||||
layout: *mut Layout,
|
||||
submission: *mut Submission,
|
||||
submission: CSubmission,
|
||||
x_widget: f64, y_widget: f64,
|
||||
widget_to_layout: Transformation,
|
||||
time: u32,
|
||||
ui_keyboard: EekGtkKeyboard,
|
||||
) {
|
||||
let layout = unsafe { &mut *layout };
|
||||
let submission = unsafe { &mut *submission };
|
||||
let submission = submission.clone_ref();
|
||||
let mut submission = submission.borrow_mut();
|
||||
let point = widget_to_layout.forward(
|
||||
Point { x: x_widget, y: y_widget }
|
||||
);
|
||||
|
||||
let state = layout.find_button_by_position(point)
|
||||
.map(|place| place.button.state.clone());
|
||||
|
||||
|
||||
if let Some(state) = state {
|
||||
seat::handle_press_key(
|
||||
layout,
|
||||
submission,
|
||||
&mut submission,
|
||||
Timestamp(time),
|
||||
&state,
|
||||
);
|
||||
@ -294,7 +307,7 @@ pub mod c {
|
||||
pub extern "C"
|
||||
fn squeek_layout_drag(
|
||||
layout: *mut Layout,
|
||||
submission: *mut Submission,
|
||||
submission: CSubmission,
|
||||
x_widget: f64, y_widget: f64,
|
||||
widget_to_layout: Transformation,
|
||||
time: u32,
|
||||
@ -303,7 +316,8 @@ pub mod c {
|
||||
) {
|
||||
let time = Timestamp(time);
|
||||
let layout = unsafe { &mut *layout };
|
||||
let submission = unsafe { &mut *submission };
|
||||
let submission = submission.clone_ref();
|
||||
let mut submission = submission.borrow_mut();
|
||||
let ui_backend = UIBackend {
|
||||
widget_to_layout,
|
||||
keyboard: ui_keyboard,
|
||||
@ -311,7 +325,7 @@ pub mod c {
|
||||
let point = ui_backend.widget_to_layout.forward(
|
||||
Point { x: x_widget, y: y_widget }
|
||||
);
|
||||
|
||||
|
||||
let pressed = layout.pressed_keys.clone();
|
||||
let button_info = {
|
||||
let place = layout.find_button_by_position(point);
|
||||
@ -331,7 +345,7 @@ pub mod c {
|
||||
} else {
|
||||
seat::handle_release_key(
|
||||
layout,
|
||||
submission,
|
||||
&mut submission,
|
||||
Some(&ui_backend),
|
||||
time,
|
||||
Some(manager),
|
||||
@ -342,7 +356,7 @@ pub mod c {
|
||||
if !found {
|
||||
seat::handle_press_key(
|
||||
layout,
|
||||
submission,
|
||||
&mut submission,
|
||||
time,
|
||||
&state,
|
||||
);
|
||||
@ -356,7 +370,7 @@ pub mod c {
|
||||
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
|
||||
seat::handle_release_key(
|
||||
layout,
|
||||
submission,
|
||||
&mut submission,
|
||||
Some(&ui_backend),
|
||||
time,
|
||||
Some(manager),
|
||||
@ -370,11 +384,11 @@ pub mod c {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
||||
fn near(a: f64, b: f64) -> bool {
|
||||
(a - b).abs() < ((a + b) * 0.001f64).abs()
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn transform_back() {
|
||||
let transform = Transformation {
|
||||
@ -413,7 +427,7 @@ pub enum Label {
|
||||
/// The graphical representation of a button
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Button {
|
||||
/// ID string, e.g. for CSS
|
||||
/// ID string, e.g. for CSS
|
||||
pub name: CString,
|
||||
/// Label to display to the user
|
||||
pub label: Label,
|
||||
@ -573,11 +587,11 @@ impl View {
|
||||
offset: &row.0 + c::Point { x: button.0, y: 0.0 },
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn get_size(&self) -> Size {
|
||||
self.size.clone()
|
||||
}
|
||||
|
||||
|
||||
/// Returns positioned rows, with appropriate x offsets (centered)
|
||||
pub fn get_rows(&self) -> &Vec<(c::Point, Row)> {
|
||||
&self.rows
|
||||
@ -627,6 +641,7 @@ pub enum LatchedState {
|
||||
pub struct Layout {
|
||||
pub margins: Margins,
|
||||
pub kind: ArrangementKind,
|
||||
pub purpose: ContentPurpose,
|
||||
pub current_view: String,
|
||||
|
||||
// If current view is latched,
|
||||
@ -676,7 +691,7 @@ impl fmt::Display for NoSuchView {
|
||||
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
|
||||
// Cloning could also be used.
|
||||
impl Layout {
|
||||
pub fn new(data: LayoutData, kind: ArrangementKind) -> Layout {
|
||||
pub fn new(data: LayoutData, kind: ArrangementKind, purpose: ContentPurpose) -> Layout {
|
||||
Layout {
|
||||
kind,
|
||||
current_view: "base".to_owned(),
|
||||
@ -685,6 +700,7 @@ impl Layout {
|
||||
keymaps: data.keymaps,
|
||||
pressed_keys: HashSet::new(),
|
||||
margins: data.margins,
|
||||
purpose,
|
||||
}
|
||||
}
|
||||
|
||||
@ -731,7 +747,7 @@ impl Layout {
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn calculate_transformation(
|
||||
&self,
|
||||
available: Size,
|
||||
@ -770,7 +786,7 @@ impl Layout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn apply_view_transition(
|
||||
&mut self,
|
||||
action: &Action,
|
||||
@ -812,7 +828,7 @@ impl Layout {
|
||||
///
|
||||
/// Although the state is not defined at the keys
|
||||
/// (it's in the relationship between view and action),
|
||||
/// keys go through the following stages when clicked repeatedly:
|
||||
/// keys go through the following stages when clicked repeatedly:
|
||||
/// unlocked+unlatched -> locked+latched -> locked+unlatched
|
||||
/// -> unlocked+unlatched
|
||||
fn process_action_for_view<'a>(
|
||||
@ -906,7 +922,7 @@ mod procedures {
|
||||
})
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@ -1113,7 +1129,7 @@ mod test {
|
||||
state: state,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn latch_lock_unlock() {
|
||||
let action = Action::LockView {
|
||||
@ -1152,7 +1168,7 @@ mod test {
|
||||
latches: true,
|
||||
looks_locked_from: vec![],
|
||||
};
|
||||
|
||||
|
||||
let submit = Action::Erase;
|
||||
|
||||
let view = View::new(vec![(
|
||||
@ -1194,7 +1210,8 @@ mod test {
|
||||
"base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
|
||||
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
|
||||
},
|
||||
};
|
||||
purpose: ContentPurpose::Normal,
|
||||
};
|
||||
|
||||
// Basic cycle
|
||||
layout.apply_view_transition(&switch);
|
||||
@ -1220,14 +1237,14 @@ mod test {
|
||||
latches: true,
|
||||
looks_locked_from: vec![],
|
||||
};
|
||||
|
||||
|
||||
let unswitch = Action::LockView {
|
||||
lock: "locked".into(),
|
||||
unlock: "unlocked".into(),
|
||||
latches: false,
|
||||
looks_locked_from: vec![],
|
||||
};
|
||||
|
||||
|
||||
let submit = Action::Erase;
|
||||
|
||||
let view = View::new(vec![(
|
||||
@ -1270,7 +1287,8 @@ mod test {
|
||||
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
|
||||
"unlocked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
|
||||
},
|
||||
};
|
||||
purpose: ContentPurpose::Normal,
|
||||
};
|
||||
|
||||
layout.apply_view_transition(&switch);
|
||||
assert_eq!(&layout.current_view, "locked");
|
||||
@ -1286,14 +1304,14 @@ mod test {
|
||||
latches: true,
|
||||
looks_locked_from: vec![],
|
||||
};
|
||||
|
||||
|
||||
let switch_again = Action::LockView {
|
||||
lock: "ĄĘ".into(),
|
||||
unlock: "locked".into(),
|
||||
latches: true,
|
||||
looks_locked_from: vec![],
|
||||
};
|
||||
|
||||
|
||||
let submit = Action::Erase;
|
||||
|
||||
let view = View::new(vec![(
|
||||
@ -1336,7 +1354,8 @@ mod test {
|
||||
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
|
||||
"ĄĘ".into() => (c::Point { x: 0.0, y: 0.0 }, view),
|
||||
},
|
||||
};
|
||||
purpose: ContentPurpose::Normal,
|
||||
};
|
||||
|
||||
// Latch twice, then Ąto-unlatch across 2 levels
|
||||
layout.apply_view_transition(&switch);
|
||||
@ -1436,6 +1455,7 @@ mod test {
|
||||
views: hashmap! {
|
||||
String::new() => (c::Point { x: 0.0, y: 0.0 }, view),
|
||||
},
|
||||
purpose: ContentPurpose::Normal,
|
||||
};
|
||||
assert_eq!(
|
||||
layout.calculate_inner_size(),
|
||||
|
||||
10
src/lib.rs
10
src/lib.rs
@ -11,30 +11,34 @@ extern crate gtk_sys;
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use] // only for tests
|
||||
extern crate maplit;
|
||||
extern crate regex;
|
||||
extern crate serde;
|
||||
extern crate xkbcommon;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod assert_matches;
|
||||
#[macro_use]
|
||||
mod logging;
|
||||
|
||||
mod action;
|
||||
mod animation;
|
||||
pub mod data;
|
||||
mod drawing;
|
||||
mod event_loop;
|
||||
pub mod float_ord;
|
||||
pub mod imservice;
|
||||
mod keyboard;
|
||||
mod layout;
|
||||
mod locale;
|
||||
mod locale_config;
|
||||
mod main;
|
||||
mod manager;
|
||||
mod outputs;
|
||||
mod popover;
|
||||
mod resources;
|
||||
mod state;
|
||||
mod style;
|
||||
mod submission;
|
||||
pub mod tests;
|
||||
pub mod util;
|
||||
mod ui_manager;
|
||||
mod vkeyboard;
|
||||
mod xdg;
|
||||
|
||||
@ -88,15 +88,6 @@ impl Drop for XkbInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Translation<'a>(pub &'a str);
|
||||
|
||||
impl<'a> Translation<'a> {
|
||||
pub fn to_owned(&'a self) -> OwnedTranslation {
|
||||
OwnedTranslation(self.0.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct OwnedTranslation(pub String);
|
||||
|
||||
|
||||
@ -1,535 +0,0 @@
|
||||
/*! Locale detection and management.
|
||||
* Based on https://github.com/rust-locale/locale_config
|
||||
*
|
||||
* Ready for deletion/replacement once Debian starts packaging this,
|
||||
* although this version doesn't need lazy_static.
|
||||
*
|
||||
* Copyright (c) 2016–2019 Jan Hudec <bulb@ucw.cz>
|
||||
Copyright (c) 2016 A.J. Gardner <aaron.j.gardner@gmail.com>
|
||||
Copyright (c) 2019, Bastien Orivel <eijebong@bananium.fr>
|
||||
Copyright (c) 2019, Igor Gnatenko <i.gnatenko.brain@gmail.com>
|
||||
Copyright (c) 2019, Sophie Tauchert <999eagle@999eagle.moe>
|
||||
*/
|
||||
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
|
||||
/// Errors that may be returned by `locale_config`.
|
||||
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
|
||||
pub enum Error {
|
||||
/// Provided definition was not well formed.
|
||||
///
|
||||
/// This is returned when provided configuration string does not match even the rather loose
|
||||
/// definition for language range from [RFC4647] or the composition format used by `Locale`.
|
||||
///
|
||||
/// [RFC4647]: https://www.rfc-editor.org/rfc/rfc4647.txt
|
||||
NotWellFormed,
|
||||
/// Placeholder for adding more errors in future. **Do not match!**.
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Error {
|
||||
fn fmt(&self, out: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
out.write_str(match self {
|
||||
&Error::NotWellFormed => "Language tag is not well-formed.",
|
||||
// this is exception: here we do want exhaustive match so we don't publish version with
|
||||
// missing descriptions by mistake.
|
||||
&Error::__NonExhaustive => panic!("Placeholder error must not be instantiated!"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Convenience Result alias.
|
||||
type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// Iterator over `LanguageRange`s for specific category in a `Locale`
|
||||
///
|
||||
/// Returns `LanguageRange`s in the `Locale` that are applicable to provided category. The tags
|
||||
/// are returned in order of preference, which means the category-specific ones first and then
|
||||
/// the generic ones.
|
||||
///
|
||||
/// The iterator is guaranteed to return at least one value.
|
||||
pub struct TagsFor<'a, 'c> {
|
||||
src: &'a str,
|
||||
tags: std::str::Split<'a, &'static str>,
|
||||
category: Option<&'c str>,
|
||||
}
|
||||
|
||||
impl<'a, 'c> Iterator for TagsFor<'a, 'c> {
|
||||
type Item = LanguageRange<'a>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(cat) = self.category {
|
||||
while let Some(s) = self.tags.next() {
|
||||
if s.starts_with(cat) && s[cat.len()..].starts_with("=") {
|
||||
return Some(
|
||||
LanguageRange { language: Cow::Borrowed(&s[cat.len()+1..]) });
|
||||
}
|
||||
}
|
||||
self.category = None;
|
||||
self.tags = self.src.split(",");
|
||||
}
|
||||
while let Some(s) = self.tags.next() {
|
||||
if s.find('=').is_none() {
|
||||
return Some(
|
||||
LanguageRange{ language: Cow::Borrowed(s) });
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Language and culture identifier.
|
||||
///
|
||||
/// This object holds a [RFC4647] extended language range.
|
||||
///
|
||||
/// The internal data may be owned or shared from object with lifetime `'a`. The lifetime can be
|
||||
/// extended using the `into_static()` method, which internally clones the data as needed.
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// The range is composed of `-`-separated alphanumeric subtags, possibly replaced by `*`s. It
|
||||
/// might be empty.
|
||||
///
|
||||
/// In agreement with [RFC4647], this object only requires that the tag matches:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// language_tag = (alpha{1,8} | "*")
|
||||
/// ("-" (alphanum{1,8} | "*"))*
|
||||
/// ```
|
||||
///
|
||||
/// The exact interpretation is up to the downstream localization provider, but it expected that
|
||||
/// it will be matched against a normalized [RFC5646] language tag, which has the structure:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// language_tag = language
|
||||
/// ("-" script)?
|
||||
/// ("-" region)?
|
||||
/// ("-" variant)*
|
||||
/// ("-" extension)*
|
||||
/// ("-" private)?
|
||||
///
|
||||
/// language = alpha{2,3} ("-" alpha{3}){0,3}
|
||||
///
|
||||
/// script = aplha{4}
|
||||
///
|
||||
/// region = alpha{2}
|
||||
/// | digit{3}
|
||||
///
|
||||
/// variant = alphanum{5,8}
|
||||
/// | digit alphanum{3}
|
||||
///
|
||||
/// extension = [0-9a-wyz] ("-" alphanum{2,8})+
|
||||
///
|
||||
/// private = "x" ("-" alphanum{1,8})+
|
||||
/// ```
|
||||
///
|
||||
/// * `language` is an [ISO639] 2-letter or, where not defined, 3-letter code. A code for
|
||||
/// macro-language might be followed by code of specific dialect.
|
||||
/// * `script` is an [ISO15924] 4-letter code.
|
||||
/// * `region` is either an [ISO3166] 2-letter code or, for areas other than countries, [UN M.49]
|
||||
/// 3-digit numeric code.
|
||||
/// * `variant` is a string indicating variant of the language.
|
||||
/// * `extension` and `private` define additional options. The private part has same structure as
|
||||
/// the Unicode [`-u-` extension][u_ext]. Available options are documented for the facets that
|
||||
/// use them.
|
||||
///
|
||||
/// The values obtained by inspecting the system are normalized according to those rules.
|
||||
///
|
||||
/// The content will be case-normalized as recommended in [RFC5646] §2.1.1, namely:
|
||||
///
|
||||
/// * `language` is written in lowercase,
|
||||
/// * `script` is written with first capital,
|
||||
/// * `country` is written in uppercase and
|
||||
/// * all other subtags are written in lowercase.
|
||||
///
|
||||
/// When detecting system configuration, additional options that may be generated under the
|
||||
/// [`-u-` extension][u_ext] currently are:
|
||||
///
|
||||
/// * `cf` — Currency format (`account` for parenthesized negative values, `standard` for minus
|
||||
/// sign).
|
||||
/// * `fw` — First day of week (`mon` to `sun`).
|
||||
/// * `hc` — Hour cycle (`h12` for 1–12, `h23` for 0–23).
|
||||
/// * `ms` — Measurement system (`metric` or `ussystem`).
|
||||
/// * `nu` — Numbering system—only decimal systems are currently used.
|
||||
/// * `va` — Variant when locale is specified in Unix format and the tag after `@` does not
|
||||
/// correspond to any variant defined in [Language subtag registry].
|
||||
///
|
||||
/// And under the `-x-` extension, following options are defined:
|
||||
///
|
||||
/// * `df` — Date format:
|
||||
///
|
||||
/// * `iso`: Short date should be in ISO format of `yyyy-MM-dd`.
|
||||
///
|
||||
/// For example `-df-iso`.
|
||||
///
|
||||
/// * `dm` — Decimal separator for monetary:
|
||||
///
|
||||
/// Followed by one or more Unicode codepoints in hexadecimal. For example `-dm-002d` means to
|
||||
/// use comma.
|
||||
///
|
||||
/// * `ds` — Decimal separator for numbers:
|
||||
///
|
||||
/// Followed by one or more Unicode codepoints in hexadecimal. For example `-ds-002d` means to
|
||||
/// use comma.
|
||||
///
|
||||
/// * `gm` — Group (thousand) separator for monetary:
|
||||
///
|
||||
/// Followed by one or more Unicode codepoints in hexadecimal. For example `-dm-00a0` means to
|
||||
/// use non-breaking space.
|
||||
///
|
||||
/// * `gs` — Group (thousand) separator for numbers:
|
||||
///
|
||||
/// Followed by one or more Unicode codepoints in hexadecimal. For example `-ds-00a0` means to
|
||||
/// use non-breaking space.
|
||||
///
|
||||
/// * `ls` — List separator:
|
||||
///
|
||||
/// Followed by one or more Unicode codepoints in hexadecimal. For example, `-ds-003b` means to
|
||||
/// use a semicolon.
|
||||
///
|
||||
/// [RFC5646]: https://www.rfc-editor.org/rfc/rfc5646.txt
|
||||
/// [RFC4647]: https://www.rfc-editor.org/rfc/rfc4647.txt
|
||||
/// [ISO639]: https://en.wikipedia.org/wiki/ISO_639
|
||||
/// [ISO15924]: https://en.wikipedia.org/wiki/ISO_15924
|
||||
/// [ISO3166]: https://en.wikipedia.org/wiki/ISO_3166
|
||||
/// [UN M.49]: https://en.wikipedia.org/wiki/UN_M.49
|
||||
/// [u_ext]: http://www.unicode.org/reports/tr35/#u_Extension
|
||||
/// [Language subtag registry]: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
||||
#[derive(Clone,Debug,Eq,Hash,PartialEq)]
|
||||
pub struct LanguageRange<'a> {
|
||||
language: Cow<'a, str>
|
||||
}
|
||||
|
||||
impl<'a> LanguageRange<'a> {
|
||||
/// Return LanguageRange for the invariant locale.
|
||||
///
|
||||
/// Invariant language is identified simply by empty string.
|
||||
pub fn invariant() -> LanguageRange<'static> {
|
||||
LanguageRange { language: Cow::Borrowed("") }
|
||||
}
|
||||
|
||||
/// Create language tag from Unix/Linux/GNU locale tag.
|
||||
///
|
||||
/// Unix locale tags have the form
|
||||
///
|
||||
/// > *language* [ `_` *region* ] [ `.` *encoding* ] [ `@` *variant* ]
|
||||
///
|
||||
/// The *language* and *region* have the same format as RFC5646. *Encoding* is not relevant
|
||||
/// here, since Rust always uses Utf-8. That leaves *variant*, which is unfortunately rather
|
||||
/// free-form. So this function will translate known variants to corresponding RFC5646 subtags
|
||||
/// and represent anything else with Unicode POSIX variant (`-u-va-`) extension.
|
||||
///
|
||||
/// Note: This function is public here for benefit of applications that may come across this
|
||||
/// kind of tags from other sources than system configuration.
|
||||
pub fn from_unix(s: &str) -> Result<LanguageRange<'static>> {
|
||||
let unix_tag_regex = Regex::new(r"(?ix) ^
|
||||
(?P<language> [[:alpha:]]{2,3} )
|
||||
(?: _ (?P<region> [[:alpha:]]{2} | [[:digit:]]{3} ))?
|
||||
(?: \. (?P<encoding> [0-9a-zA-Z-]{1,20} ))?
|
||||
(?: @ (?P<variant> [[:alnum:]]{1,20} ))?
|
||||
$ ").unwrap();
|
||||
|
||||
let unix_invariant_regex = Regex::new(r"(?ix) ^
|
||||
(?: c | posix )
|
||||
(?: \. (?: [0-9a-zA-Z-]{1,20} ))?
|
||||
$ ").unwrap();
|
||||
|
||||
if let Some(caps) = unix_tag_regex.captures(s) {
|
||||
let src_variant = caps.name("variant").map(|m| m.as_str()).unwrap_or("").to_ascii_lowercase();
|
||||
let mut res = caps.name("language").map(|m| m.as_str()).unwrap().to_ascii_lowercase();
|
||||
let region = caps.name("region").map(|m| m.as_str()).unwrap_or("");
|
||||
let mut script = "";
|
||||
let mut variant = "";
|
||||
let mut uvariant = "";
|
||||
match src_variant.as_ref() {
|
||||
// Variants seen in the wild in GNU LibC (via http://lh.2xlibre.net/) or in Debian
|
||||
// GNU/Linux Stretch system. Treatment of things not found in RFC5646 subtag registry
|
||||
// (http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry)
|
||||
// or CLDR according to notes at https://wiki.openoffice.org/wiki/LocaleMapping.
|
||||
// Dialects:
|
||||
// aa_ER@saaho - NOTE: Can't be found under that name in RFC5646 subtag registry,
|
||||
// but there is language Saho with code ssy, which is likely that thing.
|
||||
"saaho" if res == "aa" => res = String::from("ssy"),
|
||||
// Scripts:
|
||||
// @arabic
|
||||
"arabic" => script = "Arab",
|
||||
// @cyrillic
|
||||
"cyrl" => script = "Cyrl",
|
||||
"cyrillic" => script = "Cyrl",
|
||||
// @devanagari
|
||||
"devanagari" => script = "Deva",
|
||||
// @hebrew
|
||||
"hebrew" => script = "Hebr",
|
||||
// tt@iqtelif
|
||||
// Neither RFC5646 subtag registry nor CLDR knows anything about this, but as best
|
||||
// as I can tell it is Tatar name for Latin (default is Cyrillic).
|
||||
"iqtelif" => script = "Latn",
|
||||
// @Latn
|
||||
"latn" => script = "Latn",
|
||||
// @latin
|
||||
"latin" => script = "Latn",
|
||||
// en@shaw
|
||||
"shaw" => script = "Shaw",
|
||||
// Variants:
|
||||
// sr@ijekavianlatin
|
||||
"ijekavianlatin" => {
|
||||
script = "Latn";
|
||||
variant = "ijekavsk";
|
||||
},
|
||||
// sr@ije
|
||||
"ije" => variant = "ijekavsk",
|
||||
// sr@ijekavian
|
||||
"ijekavian" => variant = "ijekavsk",
|
||||
// ca@valencia
|
||||
"valencia" => variant = "valencia",
|
||||
// Currencies:
|
||||
// @euro - NOTE: We follow suite of Java and Openoffice and ignore it, because it
|
||||
// is default for all locales where it sometimes appears now, and because we use
|
||||
// explicit currency in monetary formatting anyway.
|
||||
"euro" => {},
|
||||
// Collation:
|
||||
// gez@abegede - NOTE: This is collation, but CLDR does not have any code for it,
|
||||
// so we for the moment leave it fall through as -u-va- instead of -u-co-.
|
||||
// Anything else:
|
||||
// en@boldquot, en@quot, en@piglatin - just randomish stuff
|
||||
// @cjknarrow - beware, it's gonna end up as -u-va-cjknarro due to lenght limit
|
||||
s if s.len() <= 8 => uvariant = &*s,
|
||||
s => uvariant = &s[0..8], // the subtags are limited to 8 chars, but some are longer
|
||||
};
|
||||
if script != "" {
|
||||
res.push('-');
|
||||
res.push_str(script);
|
||||
}
|
||||
if region != "" {
|
||||
res.push('-');
|
||||
res.push_str(&*region.to_ascii_uppercase());
|
||||
}
|
||||
if variant != "" {
|
||||
res.push('-');
|
||||
res.push_str(variant);
|
||||
}
|
||||
if uvariant != "" {
|
||||
res.push_str("-u-va-");
|
||||
res.push_str(uvariant);
|
||||
}
|
||||
return Ok(LanguageRange {
|
||||
language: Cow::Owned(res)
|
||||
});
|
||||
} else if unix_invariant_regex.is_match(s) {
|
||||
return Ok(LanguageRange::invariant())
|
||||
} else {
|
||||
return Err(Error::NotWellFormed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<str> for LanguageRange<'a> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.language.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Locale configuration.
|
||||
///
|
||||
/// Users may accept several languages in some order of preference and may want to use rules from
|
||||
/// different culture for some particular aspect of the program behaviour, and operating systems
|
||||
/// allow them to specify this (to various extent).
|
||||
///
|
||||
/// The `Locale` objects represent the user configuration. They contain:
|
||||
///
|
||||
/// - The primary `LanguageRange`.
|
||||
/// - Optional category-specific overrides.
|
||||
/// - Optional fallbacks in case data (usually translations) for the primary language are not
|
||||
/// available.
|
||||
///
|
||||
/// The set of categories is open-ended. The `locale` crate uses five well-known categories
|
||||
/// `messages`, `numeric`, `time`, `collate` and `monetary`, but some systems define additional
|
||||
/// ones (GNU Linux has additionally `paper`, `name`, `address`, `telephone` and `measurement`) and
|
||||
/// these are provided in the user default `Locale` and other libraries can use them.
|
||||
///
|
||||
/// `Locale` is represented by a `,`-separated sequence of tags in `LanguageRange` syntax, where
|
||||
/// all except the first one may be preceded by category name and `=` sign.
|
||||
///
|
||||
/// The first tag indicates the default locale, the tags prefixed by category names indicate
|
||||
/// _overrides_ for those categories and the remaining tags indicate fallbacks.
|
||||
///
|
||||
/// Note that a syntactically valid value of HTTP `Accept-Language` header is a valid `Locale`. Not
|
||||
/// the other way around though due to the presence of category selectors.
|
||||
// TODO: Interning
|
||||
#[derive(Clone,Debug,Eq,Hash,PartialEq)]
|
||||
pub struct Locale {
|
||||
// TODO: Intern the string for performance reasons
|
||||
// XXX: Store pre-split to LanguageTags?
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl Locale {
|
||||
/// Construct invariant locale.
|
||||
///
|
||||
/// Invariant locale is represented simply with empty string.
|
||||
pub fn invariant() -> Locale {
|
||||
Locale::from(LanguageRange::invariant())
|
||||
}
|
||||
|
||||
/// Append fallback language tag.
|
||||
///
|
||||
/// Adds fallback to the end of the list.
|
||||
pub fn add(&mut self, tag: &LanguageRange) {
|
||||
for i in self.inner.split(',') {
|
||||
if i == tag.as_ref() {
|
||||
return; // don't add duplicates
|
||||
}
|
||||
}
|
||||
self.inner.push_str(",");
|
||||
self.inner.push_str(tag.as_ref());
|
||||
}
|
||||
|
||||
/// Append category override.
|
||||
///
|
||||
/// Appending new override for a category that already has one will not replace the existing
|
||||
/// override. This might change in future.
|
||||
pub fn add_category(&mut self, category: &str, tag: &LanguageRange) {
|
||||
if self.inner.split(',').next().unwrap() == tag.as_ref() {
|
||||
return; // don't add useless override equal to the primary tag
|
||||
}
|
||||
for i in self.inner.split(',') {
|
||||
if i.starts_with(category) &&
|
||||
i[category.len()..].starts_with("=") &&
|
||||
&i[category.len() + 1..] == tag.as_ref() {
|
||||
return; // don't add duplicates
|
||||
}
|
||||
}
|
||||
self.inner.push_str(",");
|
||||
self.inner.push_str(category);
|
||||
self.inner.push_str("=");
|
||||
self.inner.push_str(tag.as_ref());
|
||||
}
|
||||
|
||||
/// Iterate over `LanguageRange`s in this `Locale` applicable to given category.
|
||||
///
|
||||
/// Returns `LanguageRange`s in the `Locale` that are applicable to provided category. The tags
|
||||
/// are returned in order of preference, which means the category-specific ones first and then
|
||||
/// the generic ones.
|
||||
///
|
||||
/// The iterator is guaranteed to return at least one value.
|
||||
pub fn tags_for<'a, 'c>(&'a self, category: &'c str) -> TagsFor<'a, 'c> {
|
||||
let mut tags = self.inner.split(",");
|
||||
while let Some(s) = tags.clone().next() {
|
||||
if s.starts_with(category) && s[category.len()..].starts_with("=") {
|
||||
return TagsFor {
|
||||
src: self.inner.as_ref(),
|
||||
tags: tags,
|
||||
category: Some(category),
|
||||
};
|
||||
}
|
||||
tags.next();
|
||||
}
|
||||
return TagsFor {
|
||||
src: self.inner.as_ref(),
|
||||
tags: self.inner.split(","),
|
||||
category: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Locale is specified by a string tag. This is the way to access it.
|
||||
// FIXME: Do we want to provide the full string representation? We would have it as single string
|
||||
// then.
|
||||
impl AsRef<str> for Locale {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<LanguageRange<'a>> for Locale {
|
||||
fn from(t: LanguageRange<'a>) -> Locale {
|
||||
Locale {
|
||||
inner: t.language.into_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tag(s: &str) -> Result<LanguageRange> {
|
||||
LanguageRange::from_unix(s)
|
||||
}
|
||||
|
||||
// TODO: Read /etc/locale.alias
|
||||
fn tag_inv(s: &str) -> LanguageRange {
|
||||
tag(s).unwrap_or(LanguageRange::invariant())
|
||||
}
|
||||
|
||||
pub fn system_locale() -> Option<Locale> {
|
||||
// LC_ALL overrides everything
|
||||
if let Ok(all) = env::var("LC_ALL") {
|
||||
if let Ok(t) = tag(all.as_ref()) {
|
||||
return Some(Locale::from(t));
|
||||
}
|
||||
}
|
||||
// LANG is default
|
||||
let mut loc =
|
||||
if let Ok(lang) = env::var("LANG") {
|
||||
Locale::from(tag_inv(lang.as_ref()))
|
||||
} else {
|
||||
Locale::invariant()
|
||||
};
|
||||
// category overrides
|
||||
for &(cat, var) in [
|
||||
("ctype", "LC_CTYPE"),
|
||||
("numeric", "LC_NUMERIC"),
|
||||
("time", "LC_TIME"),
|
||||
("collate", "LC_COLLATE"),
|
||||
("monetary", "LC_MONETARY"),
|
||||
("messages", "LC_MESSAGES"),
|
||||
("paper", "LC_PAPER"),
|
||||
("name", "LC_NAME"),
|
||||
("address", "LC_ADDRESS"),
|
||||
("telephone", "LC_TELEPHONE"),
|
||||
("measurement", "LC_MEASUREMENT"),
|
||||
].iter() {
|
||||
if let Ok(val) = env::var(var) {
|
||||
if let Ok(tag) = tag(val.as_ref())
|
||||
{
|
||||
loc.add_category(cat, &tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
// LANGUAGE defines fallbacks
|
||||
if let Ok(langs) = env::var("LANGUAGE") {
|
||||
for i in langs.split(':') {
|
||||
if i != "" {
|
||||
if let Ok(tag) = tag(i) {
|
||||
loc.add(&tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if loc.as_ref() != "" {
|
||||
return Some(loc);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::LanguageRange;
|
||||
|
||||
#[test]
|
||||
fn unix_tags() {
|
||||
assert_eq!("cs-CZ", LanguageRange::from_unix("cs_CZ.UTF-8").unwrap().as_ref());
|
||||
assert_eq!("sr-RS-ijekavsk", LanguageRange::from_unix("sr_RS@ijekavian").unwrap().as_ref());
|
||||
assert_eq!("sr-Latn-ijekavsk", LanguageRange::from_unix("sr.UTF-8@ijekavianlatin").unwrap().as_ref());
|
||||
assert_eq!("en-Arab", LanguageRange::from_unix("en@arabic").unwrap().as_ref());
|
||||
assert_eq!("en-Arab", LanguageRange::from_unix("en.UTF-8@arabic").unwrap().as_ref());
|
||||
assert_eq!("de-DE", LanguageRange::from_unix("DE_de.UTF-8@euro").unwrap().as_ref());
|
||||
assert_eq!("ssy-ER", LanguageRange::from_unix("aa_ER@saaho").unwrap().as_ref());
|
||||
assert!(LanguageRange::from_unix("foo_BAR").is_err());
|
||||
assert!(LanguageRange::from_unix("en@arabic.UTF-8").is_err());
|
||||
assert_eq!("", LanguageRange::from_unix("C").unwrap().as_ref());
|
||||
assert_eq!("", LanguageRange::from_unix("C.UTF-8").unwrap().as_ref());
|
||||
assert_eq!("", LanguageRange::from_unix("C.ISO-8859-1").unwrap().as_ref());
|
||||
assert_eq!("", LanguageRange::from_unix("POSIX").unwrap().as_ref());
|
||||
}
|
||||
}
|
||||
34
src/main.h
Normal file
34
src/main.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
/// This all wraps https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/struct.MainContext.html#method.channel
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "input-method-unstable-v2-client-protocol.h"
|
||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
||||
|
||||
#include "eek/eek-types.h"
|
||||
#include "dbus.h"
|
||||
|
||||
|
||||
struct receiver;
|
||||
|
||||
/// Wrapped<event_loop::driver::Threaded>
|
||||
struct squeek_state_manager;
|
||||
|
||||
struct submission;
|
||||
|
||||
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_init(void);
|
||||
|
||||
void squeek_state_send_force_visible(struct squeek_state_manager *state);
|
||||
void squeek_state_send_force_hidden(struct squeek_state_manager *state);
|
||||
|
||||
void squeek_state_send_keyboard_present(struct squeek_state_manager *state, uint32_t keyboard_present);
|
||||
218
src/main.rs
Normal file
218
src/main.rs
Normal file
@ -0,0 +1,218 @@
|
||||
/* 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};
|
||||
|
||||
|
||||
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;
|
||||
use crate::vkeyboard::c::ZwpVirtualKeyboardV1;
|
||||
|
||||
/// ServerContextService*
|
||||
#[repr(transparent)]
|
||||
pub struct UIManager(*const c_void);
|
||||
|
||||
/// DbusHandler*
|
||||
#[repr(transparent)]
|
||||
pub struct DBusHandler(*const c_void);
|
||||
|
||||
/// 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" {
|
||||
#[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,
|
||||
// given that dbus handler is using glib.
|
||||
fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8);
|
||||
}
|
||||
|
||||
/// Creates what's possible in Rust to eliminate as many FFI calls as possible,
|
||||
/// because types aren't getting checked across their boundaries,
|
||||
/// and that leads to suffering.
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
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 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(wayland.input_method, state_manager.clone()))
|
||||
};
|
||||
let submission = Submission::new(vk, imservice);
|
||||
|
||||
RsObjects {
|
||||
submission: Wrapped::new(submission),
|
||||
state_manager: Wrapped::new(state_manager),
|
||||
receiver: Wrapped::new(receiver),
|
||||
wayland: Box::into_raw(wayland),
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the UI loop callback in the glib main loop.
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn register_ui_loop_handler(
|
||||
receiver: Wrapped<Receiver<Commands>>,
|
||||
ui_manager: *const UIManager,
|
||||
dbus_handler: *const DBusHandler,
|
||||
) {
|
||||
let receiver = unsafe { receiver.unwrap() };
|
||||
let receiver = Rc::try_unwrap(receiver).expect("References still present");
|
||||
let receiver = receiver.into_inner();
|
||||
let ctx = MainContext::default();
|
||||
let _acqu = ctx.acquire();
|
||||
receiver.attach(
|
||||
Some(&ctx),
|
||||
move |msg| {
|
||||
main_loop_handle_message(msg, ui_manager, dbus_handler);
|
||||
Continue(true)
|
||||
},
|
||||
);
|
||||
#[cfg(not(feature = "glib_v0_14"))]
|
||||
ctx.release();
|
||||
}
|
||||
|
||||
/// A single iteration of the UI loop.
|
||||
/// Applies state outcomes to external portions of the program.
|
||||
/// This is the outest layer of the imperative shell,
|
||||
/// and doesn't lend itself to testing other than integration.
|
||||
fn main_loop_handle_message(
|
||||
msg: Commands,
|
||||
ui_manager: *const UIManager,
|
||||
dbus_handler: *const DBusHandler,
|
||||
) {
|
||||
match msg.panel_visibility {
|
||||
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);
|
||||
},
|
||||
None => {},
|
||||
};
|
||||
|
||||
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 {
|
||||
server_context_service_set_hint_purpose(
|
||||
ui_manager,
|
||||
hints.hint.bits(),
|
||||
hints.purpose.clone() as u32,
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
output: OutputId,
|
||||
height: PixelSize,
|
||||
},
|
||||
Hide,
|
||||
}
|
||||
|
||||
/// The commands consumed by the main loop,
|
||||
/// to be sent out to external components.
|
||||
#[derive(Clone)]
|
||||
pub struct Commands {
|
||||
pub panel_visibility: Option<PanelCommand>,
|
||||
pub layout_hint_set: Option<state::InputMethodDetails>,
|
||||
pub dbus_visible_set: Option<bool>,
|
||||
}
|
||||
@ -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
|
||||
|
||||
223
src/outputs.rs
223
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!(
|
||||
logging::Level::Warning,
|
||||
"Got done on unknown output",
|
||||
),
|
||||
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 {
|
||||
output: output.clone(),
|
||||
pending: OutputState::uninitialized(),
|
||||
current: OutputState::uninitialized(),
|
||||
});
|
||||
collection.outputs.push((
|
||||
Output {
|
||||
output: output.clone(),
|
||||
pending: OutputState::uninitialized(),
|
||||
current: OutputState::uninitialized(),
|
||||
},
|
||||
id,
|
||||
));
|
||||
|
||||
unsafe { squeek_output_add_listener(
|
||||
output,
|
||||
@ -255,41 +267,23 @@ pub mod c {
|
||||
raw_collection,
|
||||
)};
|
||||
}
|
||||
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
229
src/popover.rs
229
src/popover.rs
@ -5,25 +5,17 @@ use gtk;
|
||||
use std::ffi::CString;
|
||||
use std::cmp::Ordering;
|
||||
use ::layout::c::{ Bounds, EekGtkKeyboard };
|
||||
use ::locale;
|
||||
use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
|
||||
use ::locale_config::system_locale;
|
||||
use ::locale::{ OwnedTranslation, compare_current_locale };
|
||||
use ::logging;
|
||||
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::BuilderExtManual;
|
||||
use gtk::PopoverExt;
|
||||
use gtk::WidgetExt;
|
||||
use std::io::Write;
|
||||
use gtk::prelude::*;
|
||||
use ::logging::Warn;
|
||||
|
||||
mod c {
|
||||
@ -111,50 +103,15 @@ mod variants {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
|
||||
let mut xml: Vec<u8> = Vec::new();
|
||||
writeln!(
|
||||
xml,
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
<interface>
|
||||
<menu id=\"app-menu\">
|
||||
<section>"
|
||||
).unwrap();
|
||||
for (input_name, translation) in inputs {
|
||||
writeln!(
|
||||
xml,
|
||||
"
|
||||
<item>
|
||||
<attribute name=\"label\" translatable=\"yes\">{}</attribute>
|
||||
<attribute name=\"action\">layout</attribute>
|
||||
<attribute name=\"target\">{}</attribute>
|
||||
</item>",
|
||||
translation.0,
|
||||
input_name,
|
||||
).unwrap();
|
||||
}
|
||||
writeln!(
|
||||
xml,
|
||||
"
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name=\"label\" translatable=\"yes\">Keyboard Settings</attribute>
|
||||
<attribute name=\"action\">settings</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
</interface>"
|
||||
).unwrap();
|
||||
gtk::Builder::new_from_string(
|
||||
&String::from_utf8(xml).expect("Bad menu definition")
|
||||
)
|
||||
}
|
||||
|
||||
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.",
|
||||
@ -173,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);
|
||||
@ -250,13 +211,8 @@ fn get_current_layout(
|
||||
/// Translates all provided layout names according to current locale,
|
||||
/// for the purpose of display (i.e. errors will be caught and reported)
|
||||
fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
|
||||
// This procedure is rather ugly...
|
||||
// Xkb lookup *must not* be applied to non-system layouts,
|
||||
// so both translators can't be merged into one lookup table,
|
||||
// therefore must be done in two steps.
|
||||
// `XkbInfo` being temporary also means
|
||||
// that its return values must be copied,
|
||||
// forcing the use of `OwnedTranslation`.
|
||||
// `XkbInfo` being temporary means that its return values must be
|
||||
// copied, forcing the use of `OwnedTranslation`.
|
||||
enum Status {
|
||||
/// xkb names should get all translated here
|
||||
Translated(OwnedTranslation),
|
||||
@ -265,7 +221,7 @@ fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
|
||||
}
|
||||
|
||||
// Attempt to take all xkb names from gnome-desktop's xkb info.
|
||||
let xkb_translator = locale::XkbInfo::new();
|
||||
let xkb_translator = ::locale::XkbInfo::new();
|
||||
|
||||
let translated_names = layouts.iter()
|
||||
.map(|id| match id {
|
||||
@ -277,49 +233,15 @@ fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
|
||||
&format!("No display name for xkb layout {}", name),
|
||||
).unwrap_or_else(|| Status::Remaining(name.clone()))
|
||||
},
|
||||
LayoutId::Local(name) => Status::Remaining(name.clone()),
|
||||
LayoutId::Local (_) => unreachable!(),
|
||||
});
|
||||
|
||||
// Non-xkb layouts and weird xkb layouts
|
||||
// still need to be looked up in the internal database.
|
||||
let builtin_translations = system_locale()
|
||||
.map(|locale|
|
||||
locale.tags_for("messages")
|
||||
.next().unwrap() // guaranteed to exist
|
||||
.as_ref()
|
||||
.to_owned()
|
||||
)
|
||||
.or_print(logging::Problem::Surprise, "No locale detected")
|
||||
.and_then(|lang| {
|
||||
resources::get_layout_names(lang.as_str())
|
||||
.or_print(
|
||||
logging::Problem::Surprise,
|
||||
&format!("No translations for locale {}", lang),
|
||||
)
|
||||
});
|
||||
|
||||
match builtin_translations {
|
||||
Some(translations) => {
|
||||
translated_names
|
||||
.map(|status| match status {
|
||||
Status::Remaining(name) => {
|
||||
translations.get(name.as_str())
|
||||
.unwrap_or(&Translation(name.as_str()))
|
||||
.to_owned()
|
||||
},
|
||||
Status::Translated(t) => t,
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
None => {
|
||||
translated_names
|
||||
.map(|status| match status {
|
||||
Status::Remaining(name) => OwnedTranslation(name),
|
||||
Status::Translated(t) => t,
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
}
|
||||
translated_names
|
||||
.map(|status| match status {
|
||||
Status::Remaining(name) => OwnedTranslation(name),
|
||||
Status::Translated(t) => t,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn show(
|
||||
@ -336,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());
|
||||
@ -350,12 +276,12 @@ pub fn show(
|
||||
.chain(overlay_layouts)
|
||||
.collect();
|
||||
|
||||
let translated_names = translate_layout_names(&all_layouts);
|
||||
|
||||
// sorted collection of human and machine names
|
||||
let translated_names = translate_layout_names(&system_layouts);
|
||||
|
||||
// sorted collection of language layouts
|
||||
let mut human_names: Vec<(OwnedTranslation, LayoutId)> = translated_names
|
||||
.into_iter()
|
||||
.zip(all_layouts.clone().into_iter())
|
||||
.zip(system_layouts.clone().into_iter())
|
||||
.collect();
|
||||
|
||||
human_names.sort_unstable_by(|(tr_a, layout_a), (tr_b, layout_b)| {
|
||||
@ -367,34 +293,30 @@ pub fn show(
|
||||
}
|
||||
});
|
||||
|
||||
// GVariant doesn't natively support `enum`s,
|
||||
// so the `choices` vector will serve as a lookup table.
|
||||
let choices_with_translations: Vec<(String, (OwnedTranslation, LayoutId))>
|
||||
= human_names.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, human_entry)| {(
|
||||
format!("{}_{}", i, human_entry.1.get_name()),
|
||||
human_entry,
|
||||
)}).collect();
|
||||
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");
|
||||
builder.get_object("app-menu").unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
for (tr, l) in human_names.iter().rev() {
|
||||
let detailed_action = format!("layout::{}", l.get_name());
|
||||
let item = gio::MenuItem::new(Some(&tr.0), Some(detailed_action.as_str()));
|
||||
model.prepend_item (&item);
|
||||
}
|
||||
|
||||
let builder = make_menu_builder(
|
||||
choices_with_translations.iter()
|
||||
.map(|(id, (translation, _))| (id.as_str(), (*translation).clone()))
|
||||
.collect()
|
||||
);
|
||||
|
||||
let choices: Vec<(String, LayoutId)>
|
||||
= choices_with_translations.into_iter()
|
||||
.map(|(id, (_tr, layout))| (id, layout))
|
||||
.collect();
|
||||
|
||||
// Much more debuggable to populate the model & menu
|
||||
// from a string representation
|
||||
// than add items imperatively
|
||||
let model: gio::MenuModel = builder.get_object("app-menu").unwrap();
|
||||
|
||||
#[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,
|
||||
@ -403,32 +325,36 @@ pub fn show(
|
||||
});
|
||||
menu.set_constrain_to(gtk::PopoverConstraint::None);
|
||||
|
||||
let action_group = gio::SimpleActionGroup::new();
|
||||
|
||||
if let Some(current_layout) = get_current_layout(manager, &system_layouts) {
|
||||
let current_name_variant = choices.iter()
|
||||
let current_layout_name = all_layouts.iter()
|
||||
.find(
|
||||
|(_id, layout)| layout == ¤t_layout
|
||||
|l| l.get_name() == current_layout.get_name()
|
||||
).unwrap()
|
||||
.0.to_variant();
|
||||
.get_name();
|
||||
log_print!(logging::Level::Debug, "Current Layout {}", current_layout_name);
|
||||
|
||||
let layout_action = gio::SimpleAction::new_stateful(
|
||||
"layout",
|
||||
Some(current_name_variant.type_()),
|
||||
¤t_name_variant,
|
||||
Some(current_layout_name.to_variant().type_()),
|
||||
¤t_layout_name.to_variant()
|
||||
);
|
||||
|
||||
let menu_inner = menu.clone();
|
||||
layout_action.connect_change_state(move |_action, state| {
|
||||
match state {
|
||||
Some(v) => {
|
||||
log_print!(logging::Level::Debug, "Selected layout {}", v);
|
||||
v.get::<String>()
|
||||
.or_print(
|
||||
logging::Problem::Bug,
|
||||
&format!("Variant is not string: {:?}", v)
|
||||
)
|
||||
.map(|state| {
|
||||
let (_id, layout) = choices.iter()
|
||||
let layout = all_layouts.iter()
|
||||
.find(
|
||||
|choices| state == choices.0
|
||||
|choices| state == choices.get_name()
|
||||
).unwrap();
|
||||
set_visible_layout(
|
||||
manager,
|
||||
@ -443,20 +369,21 @@ pub fn show(
|
||||
};
|
||||
menu_inner.popdown();
|
||||
});
|
||||
|
||||
let settings_action = gio::SimpleAction::new("settings", None);
|
||||
settings_action.connect_activate(move |_, _| {
|
||||
let s = CString::new("region").unwrap();
|
||||
unsafe { c::popover_open_settings_panel(s.as_ptr()) };
|
||||
});
|
||||
|
||||
let action_group = gio::SimpleActionGroup::new();
|
||||
action_group.add_action(&layout_action);
|
||||
action_group.add_action(&settings_action);
|
||||
|
||||
menu.insert_action_group("popup", Some(&action_group));
|
||||
};
|
||||
|
||||
let settings_action = gio::SimpleAction::new("settings", None);
|
||||
settings_action.connect_activate(move |_, _| {
|
||||
let s = CString::new("region").unwrap();
|
||||
unsafe { c::popover_open_settings_panel(s.as_ptr()) };
|
||||
});
|
||||
action_group.add_action(&settings_action);
|
||||
|
||||
menu.insert_action_group("popup", Some(&action_group));
|
||||
|
||||
menu.bind_model(Some(&model), Some("popup"));
|
||||
menu.popup();
|
||||
glib::idle_add_local(move || {
|
||||
menu.popup();
|
||||
Continue(false)
|
||||
});
|
||||
}
|
||||
|
||||
@ -2,11 +2,6 @@
|
||||
* This could be done using GResource, but that would need additional work.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
use ::locale::Translation;
|
||||
|
||||
use std::iter::FromIterator;
|
||||
|
||||
// TODO: keep a list of what is a language layout,
|
||||
// and what a convenience layout. "_wide" is not a layout,
|
||||
// neither is "number"
|
||||
@ -18,12 +13,24 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
|
||||
("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
|
||||
|
||||
// Language layouts: keep alphabetical.
|
||||
("am", include_str!("../data/keyboards/am.yaml")),
|
||||
("am+phonetic", include_str!("../data/keyboards/am+phonetic.yaml")),
|
||||
|
||||
("ara", include_str!("../data/keyboards/ara.yaml")),
|
||||
("ara_wide", include_str!("../data/keyboards/ara_wide.yaml")),
|
||||
|
||||
("be", include_str!("../data/keyboards/be.yaml")),
|
||||
("be_wide", include_str!("../data/keyboards/be_wide.yaml")),
|
||||
|
||||
("bg", include_str!("../data/keyboards/bg.yaml")),
|
||||
("bg+phonetic", include_str!("../data/keyboards/bg+phonetic.yaml")),
|
||||
|
||||
("br", include_str!("../data/keyboards/br.yaml")),
|
||||
|
||||
("ch+fr", include_str!("../data/keyboards/ch+fr.yaml")),
|
||||
("ch+de", include_str!("../data/keyboards/ch+de.yaml")),
|
||||
("ch", include_str!("../data/keyboards/ch.yaml")),
|
||||
("ch_wide", include_str!("../data/keyboards/ch_wide.yaml")),
|
||||
|
||||
("de", include_str!("../data/keyboards/de.yaml")),
|
||||
("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
|
||||
@ -64,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")),
|
||||
@ -79,11 +89,19 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
|
||||
("us+dvorak", include_str!("../data/keyboards/us+dvorak.yaml")),
|
||||
("us+dvorak_wide", include_str!("../data/keyboards/us+dvorak_wide.yaml")),
|
||||
|
||||
// Email
|
||||
("email/us", include_str!("../data/keyboards/email/us.yaml")),
|
||||
|
||||
// URL
|
||||
("url/us", include_str!("../data/keyboards/url/us.yaml")),
|
||||
|
||||
// Others
|
||||
("number/us", include_str!("../data/keyboards/number/us.yaml")),
|
||||
("pin/us", include_str!("../data/keyboards/pin/us.yaml")),
|
||||
|
||||
// Terminal
|
||||
("terminal/fr", include_str!("../data/keyboards/terminal/fr.yaml")),
|
||||
("terminal/fr_wide", include_str!("../data/keyboards/terminal/fr_wide.yaml")),
|
||||
|
||||
("terminal/us", include_str!("../data/keyboards/terminal/us.yaml")),
|
||||
("terminal/us_wide", include_str!("../data/keyboards/terminal/us_wide.yaml")),
|
||||
@ -105,46 +123,6 @@ pub fn get_overlays() -> Vec<&'static str> {
|
||||
OVERLAY_NAMES.to_vec()
|
||||
}
|
||||
|
||||
/// Translations of the layout identifier strings
|
||||
static LAYOUT_NAMES: &[(&'static str, &'static str)] = &[
|
||||
("de-DE", include_str!("../data/langs/de-DE.txt")),
|
||||
("en-US", include_str!("../data/langs/en-US.txt")),
|
||||
("es-ES", include_str!("../data/langs/es-ES.txt")),
|
||||
("fur-IT", include_str!("../data/langs/fur-IT.txt")),
|
||||
("he-IL", include_str!("../data/langs/he-IL.txt")),
|
||||
("ja-JP", include_str!("../data/langs/ja-JP.txt")),
|
||||
("pl-PL", include_str!("../data/langs/pl-PL.txt")),
|
||||
("ru-RU", include_str!("../data/langs/ru-RU.txt")),
|
||||
];
|
||||
|
||||
pub fn get_layout_names(lang: &str)
|
||||
-> Option<HashMap<&'static str, Translation<'static>>>
|
||||
{
|
||||
let translations = LAYOUT_NAMES.iter()
|
||||
.find(|(name, _data)| *name == lang)
|
||||
.map(|(_name, data)| *data);
|
||||
translations.map(make_mapping)
|
||||
}
|
||||
|
||||
fn parse_line(line: &str) -> Option<(&str, Translation)> {
|
||||
let comment = line.trim().starts_with("#");
|
||||
if comment {
|
||||
None
|
||||
} else {
|
||||
let mut iter = line.splitn(2, " ");
|
||||
let name = iter.next().unwrap();
|
||||
// will skip empty and unfinished lines
|
||||
iter.next().map(|tr| (name, Translation(tr.trim())))
|
||||
}
|
||||
}
|
||||
|
||||
fn make_mapping(data: &str) -> HashMap<&str, Translation> {
|
||||
HashMap::from_iter(
|
||||
data.split("\n")
|
||||
.filter_map(parse_line)
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@ -155,32 +133,4 @@ mod test {
|
||||
assert!(get_keyboard(&format!("{}/us", name)).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_line() {
|
||||
assert_eq!(
|
||||
Some(("name", Translation("translation"))),
|
||||
parse_line("name translation")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_bad() {
|
||||
assert_eq!(None, parse_line("bad"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_empty() {
|
||||
assert_eq!(None, parse_line(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_comment() {
|
||||
assert_eq!(None, parse_line("# comment"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_comment_offset() {
|
||||
assert_eq!(None, parse_line(" # comment"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,10 +27,10 @@
|
||||
#include "submission.h"
|
||||
#include "wayland.h"
|
||||
#include "server-context-service.h"
|
||||
#include "wayland-client-protocol.h"
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_VISIBLE,
|
||||
PROP_ENABLED,
|
||||
PROP_LAST
|
||||
};
|
||||
@ -42,13 +42,12 @@ struct _ServerContextService {
|
||||
/// Needed for instantiating the widget
|
||||
struct submission *submission; // unowned
|
||||
struct squeek_layout_state *layout;
|
||||
struct ui_manager *manager; // unowned
|
||||
struct vis_manager *vis_manager; // owned
|
||||
struct squeek_state_manager *state_manager; // shared reference
|
||||
|
||||
gboolean visible;
|
||||
PhoshLayerSurface *window;
|
||||
GtkWidget *widget; // nullable
|
||||
guint hiding;
|
||||
|
||||
struct wl_output *current_output;
|
||||
guint last_requested_height;
|
||||
};
|
||||
|
||||
@ -68,112 +67,16 @@ on_destroy (ServerContextService *self, GtkWidget *widget)
|
||||
}
|
||||
|
||||
static void
|
||||
on_notify_map (ServerContextService *self, GtkWidget *widget)
|
||||
{
|
||||
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
|
||||
|
||||
g_object_set (self, "visible", TRUE, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_notify_unmap (ServerContextService *self, GtkWidget *widget)
|
||||
{
|
||||
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
|
||||
|
||||
g_object_set (self, "visible", FALSE, NULL);
|
||||
}
|
||||
|
||||
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
|
||||
@ -187,9 +90,7 @@ make_window (ServerContextService *self)
|
||||
|
||||
g_object_connect (self->window,
|
||||
"swapped-signal::destroy", G_CALLBACK(on_destroy), self,
|
||||
"swapped-signal::map", G_CALLBACK(on_notify_map), self,
|
||||
"swapped-signal::unmap", G_CALLBACK(on_notify_unmap), 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.
|
||||
@ -199,8 +100,7 @@ make_window (ServerContextService *self)
|
||||
// or for hacks with regular windows.
|
||||
gtk_widget_set_can_focus (GTK_WIDGET(self->window), FALSE);
|
||||
g_object_set (G_OBJECT(self->window), "accept_focus", FALSE, NULL);
|
||||
gtk_window_set_title (GTK_WINDOW(self->window),
|
||||
_("Squeekboard"));
|
||||
gtk_window_set_title (GTK_WINDOW(self->window), "Squeekboard");
|
||||
gtk_window_set_icon_name (GTK_WINDOW(self->window), "squeekboard");
|
||||
gtk_window_set_keep_above (GTK_WINDOW(self->window), TRUE);
|
||||
}
|
||||
@ -226,109 +126,58 @@ make_widget (ServerContextService *self)
|
||||
gtk_widget_show_all(self->widget);
|
||||
}
|
||||
|
||||
static void
|
||||
server_context_service_real_show_keyboard (ServerContextService *self)
|
||||
// Called from rust
|
||||
/// Updates the type of hiddenness
|
||||
void
|
||||
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);
|
||||
}
|
||||
self->visible = TRUE;
|
||||
gtk_widget_show (GTK_WIDGET(self->window));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
show_keyboard_source_func(ServerContextService *context)
|
||||
{
|
||||
server_context_service_real_show_keyboard(context);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
server_context_service_real_hide_keyboard (ServerContextService *self)
|
||||
{
|
||||
gtk_widget_hide (GTK_WIDGET(self->window));
|
||||
self->visible = FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
hide_keyboard_source_func(ServerContextService *context)
|
||||
{
|
||||
server_context_service_real_hide_keyboard(context);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_hide (ServerContextService *self)
|
||||
{
|
||||
server_context_service_real_hide_keyboard(self);
|
||||
self->hiding = 0;
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
server_context_service_show_keyboard (ServerContextService *self)
|
||||
{
|
||||
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
|
||||
|
||||
if (self->hiding) {
|
||||
g_source_remove (self->hiding);
|
||||
self->hiding = 0;
|
||||
}
|
||||
|
||||
if (!self->visible) {
|
||||
g_idle_add((GSourceFunc)show_keyboard_source_func, self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
server_context_service_force_show_keyboard (ServerContextService *self)
|
||||
{
|
||||
if (!submission_hint_available(self->submission)) {
|
||||
eekboard_context_service_set_hint_purpose(
|
||||
self->state,
|
||||
ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE,
|
||||
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL
|
||||
);
|
||||
}
|
||||
server_context_service_show_keyboard(self);
|
||||
}
|
||||
|
||||
void
|
||||
server_context_service_hide_keyboard (ServerContextService *self)
|
||||
{
|
||||
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
|
||||
|
||||
if (self->visible) {
|
||||
g_idle_add((GSourceFunc)hide_keyboard_source_func, self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Meant for use by the input-method handler:
|
||||
/// the visible keyboard is no longer needed.
|
||||
/// The implementation will delay it slightly,
|
||||
/// because the release may be due to switching from one text field to another.
|
||||
/// In this case, the user doesn't really need the keyboard surface
|
||||
/// to disappear completely.
|
||||
void
|
||||
server_context_service_release_visibility (ServerContextService *self)
|
||||
{
|
||||
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
|
||||
|
||||
if (!self->hiding && self->visible) {
|
||||
self->hiding = g_timeout_add (200, (GSourceFunc) on_hide, self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
server_context_service_set_physical_keyboard_present (ServerContextService *self, gboolean physical_keyboard_present)
|
||||
{
|
||||
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
|
||||
squeek_visman_set_keyboard_present(self->vis_manager, physical_keyboard_present);
|
||||
}
|
||||
|
||||
static void
|
||||
server_context_service_set_property (GObject *object,
|
||||
@ -339,11 +188,8 @@ server_context_service_set_property (GObject *object,
|
||||
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_VISIBLE:
|
||||
self->visible = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_ENABLED:
|
||||
server_context_service_set_physical_keyboard_present (self, !g_value_get_boolean (value));
|
||||
squeek_state_send_keyboard_present(self->state_manager, !g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
@ -357,11 +203,7 @@ server_context_service_get_property (GObject *object,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
|
||||
switch (prop_id) {
|
||||
case PROP_VISIBLE:
|
||||
g_value_set_boolean (value, self->visible);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@ -389,18 +231,6 @@ server_context_service_class_init (ServerContextServiceClass *klass)
|
||||
gobject_class->get_property = server_context_service_get_property;
|
||||
gobject_class->dispose = server_context_service_dispose;
|
||||
|
||||
/**
|
||||
* Flag to indicate if keyboard is visible or not.
|
||||
*/
|
||||
pspec = g_param_spec_boolean ("visible",
|
||||
"Visible",
|
||||
"Visible",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_VISIBLE,
|
||||
pspec);
|
||||
|
||||
/**
|
||||
* ServerContextServie:keyboard:
|
||||
*
|
||||
@ -442,24 +272,19 @@ init (ServerContextService *self) {
|
||||
}
|
||||
|
||||
ServerContextService *
|
||||
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct vis_manager *visman)
|
||||
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->vis_manager = visman;
|
||||
ui->state_manager = state_manager;
|
||||
init(ui);
|
||||
return ui;
|
||||
}
|
||||
|
||||
void
|
||||
server_context_service_update_visible (ServerContextService *self, gboolean visible) {
|
||||
if (visible) {
|
||||
server_context_service_show_keyboard(self);
|
||||
} else {
|
||||
server_context_service_hide_keyboard(self);
|
||||
}
|
||||
// Used from Rust
|
||||
void server_context_service_set_hint_purpose(ServerContextService *self, uint32_t hint,
|
||||
uint32_t purpose) {
|
||||
eekboard_context_service_set_hint_purpose(self->state, hint, purpose);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user