Compare commits

..

1 Commits

Author SHA1 Message Date
1488aa97f3 Release 1.8.1 "Corona"
- Landscape layout doesn't crash
- CSS font is actually taken into account
- Failed start due to dbus is now communicated
- Better log messages
- Fixed Enter in numbers layout
- More consistent terminal layout
- Proper font sizes in terminal layout
2020-01-31 10:08:59 +00:00
96 changed files with 1613 additions and 4628 deletions

View File

@ -1,4 +1,4 @@
image: debian:bullseye image: debian:buster
stages: stages:
- build - build
@ -11,7 +11,7 @@ stages:
before_script: before_script:
- apt-get -y update - apt-get -y update
- apt-get -y install wget ca-certificates gnupg - 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 - echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- wget -O- https://ci.puri.sm/ci-repo.key | apt-key add - - wget -O- https://ci.puri.sm/ci-repo.key | apt-key add -
- apt-get -y update - apt-get -y update
@ -27,8 +27,7 @@ build_docs:
- ./doc/build.sh _build - ./doc/build.sh _build
build_meson: build_meson:
tags: <<: *tags
- librem5
stage: build stage: build
artifacts: artifacts:
paths: paths:
@ -36,7 +35,7 @@ build_meson:
expire_in: 3h expire_in: 3h
script: script:
- apt-get -y build-dep . - apt-get -y build-dep .
- meson . _build/ -Ddepdatadir=/usr/share --werror - meson . _build/ -Ddepdatadir=/usr/share
- ninja -C _build install - ninja -C _build install
build_deb: build_deb:
@ -53,44 +52,10 @@ build_deb:
- debuild -i -us -uc -b - debuild -i -us -uc -b
- cp ../*.deb . - 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 .
build_deb:arm64: build_deb:arm64:
tags: tags:
- librem5:arm64 - librem5:arm64
allow_failure: true
stage: build stage: build
artifacts: artifacts:
paths: paths:
@ -102,23 +67,6 @@ build_deb:arm64:
- debuild -i -us -uc -b - debuild -i -us -uc -b
- cp ../*.deb . - 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 .
test_lintian: test_lintian:
<<: *tags <<: *tags
stage: test stage: test
@ -129,24 +77,10 @@ test_lintian:
- lintian *.deb - lintian *.deb
test: test:
tags: <<: *tags
- librem5
stage: test stage: test
needs: needs:
- build_meson - build_meson
script: script:
- apt-get -y build-dep . - apt-get -y build-dep .
- apt-get -y install clang-tidy
- ninja -C _build test - 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
check_release:
<<: *tags
stage: test
only:
refs:
- master
script:
- apt-get -y install git python3
- (head -n 1 ./debian/changelog && git tag) | ./debian/check_release.py

View File

@ -1,22 +0,0 @@
# 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.gdk]
version = "0.11.*"
[dependencies.gio]
version = "0.7.*"
features = ["v2_44"]
[dependencies.glib]
version = "0.8.*"
features = ["v2_44"]
[dependencies.gtk]
version = "0.7.*"
features = ["v3_22"]

View File

@ -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"]

492
Cargo.lock generated
View File

@ -1,484 +1,506 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "atk" name = "aho-corasick"
version = "0.7.0" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b7499272acf036bb5820c6e346bbfb5acc5dceb104bc2c4fd7e6e33dfcde6a"
dependencies = [ dependencies = [
"atk-sys", "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags",
"glib",
"glib-sys",
"gobject-sys",
"libc",
] ]
[[package]] [[package]]
name = "atk-sys" name = "atk-sys"
version = "0.9.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e552c1776737a4c80110d06b36d099f47c727335f9aaa5d942a72b6863a8ec6f"
dependencies = [ dependencies = [
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
version = "0.7.1" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05db47de3b0f09a222fa4bba2eab957d920d4243962a86b2d77ab401e4a359c"
dependencies = [ dependencies = [
"bitflags", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
] ]
[[package]] [[package]]
name = "cairo-sys-rs" name = "cairo-sys-rs"
version = "0.9.2" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff65ba02cac715be836f63429ab00a767d48336efc5497c5637afb53b4f14d63"
dependencies = [ dependencies = [
"glib-sys", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.62" version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.3" version = "2.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap", "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width", "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "0.4.6" version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
[[package]] [[package]]
name = "fragile" name = "fragile"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
[[package]] [[package]]
name = "gdk" name = "gdk"
version = "0.11.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6243e995f41f3a61a31847e54cc719edce93dd9140c89dca3b9919be1cfe22d5"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs", "cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf", "gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys", "gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango", "pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gdk-pixbuf" name = "gdk-pixbuf"
version = "0.7.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9726408ee1bbada83094326a99b9c68fea275f9dbb515de242a69e72051f4fcc"
dependencies = [ dependencies = [
"gdk-pixbuf-sys", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gdk-pixbuf-sys" name = "gdk-pixbuf-sys"
version = "0.9.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8991b060a9e9161bafd09bf4a202e6fd404f5b4dd1a08d53a1e84256fb34ab0"
dependencies = [ dependencies = [
"gio-sys", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gdk-sys" name = "gdk-sys"
version = "0.9.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6adf679e91d1bff0c06860287f80403e7db54c2d2424dce0a470023b56c88fbb"
dependencies = [ dependencies = [
"cairo-sys-rs", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys", "pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.7.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6261b5d34c30c2d59f879e643704cf54cb44731f3a2038000b68790c03e360e3"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fragile", "fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gio-sys" name = "gio-sys"
version = "0.9.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fad225242b9eae7ec8a063bb86974aca56885014672375e5775dc0ea3533911"
dependencies = [ dependencies = [
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "glib" name = "glib"
version = "0.8.2" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be27232841baa43e0fd5ae003f7941925735b2f733a336dc75f07b9eff415e7b"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "glib-sys" name = "glib-sys"
version = "0.9.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2"
dependencies = [ dependencies = [
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gobject-sys" name = "gobject-sys"
version = "0.9.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9"
dependencies = [ dependencies = [
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gtk" name = "gtk"
version = "0.7.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709f1074259d4685b96133f92b75c7f35b504715b0fcdc96ec95de2607296a60"
dependencies = [ dependencies = [
"atk", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags", "cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs", "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"cc", "gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk", "gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys", "gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pango",
"pango-sys",
] ]
[[package]] [[package]]
name = "gtk-sys" name = "gtk-sys"
version = "0.9.2" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53def660c7b48b00b510c81ef2d2fbd3c570f1527081d8d7947f471513e1a4c1"
dependencies = [ dependencies = [
"atk-sys", "atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys", "gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys", "pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.80" version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.3" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]] [[package]]
name = "maplit" name = "maplit"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "memchr"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "memmap" name = "memmap"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
dependencies = [ dependencies = [
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "pango" name = "pango"
version = "0.7.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "393fa071b144f8ffb83ede273758983cf414ca3c0b1d2a5a9ce325b3ba3dd786"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"glib", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys", "pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "pango-sys" name = "pango-sys"
version = "0.9.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b93d84907b3cf0819bff8f13598ba72843bee579d5ebc2502e4b0367b4be7d"
dependencies = [ dependencies = [
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.19" version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.24" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.7" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.3.9" version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
dependencies = [ dependencies = [
"regex-syntax", "aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.21" version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
[[package]] [[package]]
name = "rs" name = "rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs", "cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk", "gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk", "gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys", "gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit", "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml", "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
"xkbcommon", "xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.117" version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
dependencies = [ dependencies = [
"serde_derive", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.117" version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn", "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.8.14" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7"
dependencies = [ dependencies = [
"dtoa", "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.48" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [ dependencies = [
"unicode-width", "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.8" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.1" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [ dependencies = [
"winapi-i686-pc-windows-gnu", "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "xkbcommon" name = "xkbcommon"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fda0ea5f7ddabd51deeeda7799bee06274112f577da7dd3d954b8eda731b2fce"
dependencies = [ dependencies = [
"libc", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap", "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.4" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
dependencies = [ dependencies = [
"linked-hash-map", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[metadata]
"checksum aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5f56c476256dc249def911d6f7580b5fc7e875895b5d7ee88f5d602208035744"
"checksum atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7017e53393e713212aed7aea336b6553be4927f58c37070a56c2fe3d107e489"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd940f0d609699e343ef71c4af5f66423afbf30d666f796dabd8fd15229cf5b6"
"checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2"
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96"
"checksum gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc3aa730cb4df3de5d9fed59f43afdf9e5fb2d3d10bfcbd04cec031435ce87f5"
"checksum gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08284f16ce4d909b10d785a763ba190e222d2c1557b29908bf0a661e27a8ac3b"
"checksum gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "108548ebf5329b551f2b97ab356908d14627905abb74b936c3372de1535aee81"
"checksum gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "29a44b051990573448edc80b1995237f8b97b5734d2aec05105b9242aa10af11"
"checksum gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6975ada29f7924dc1c90b30ed3b32d777805a275556c05e420da4fbdc22eb250"
"checksum glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a333edf5b9f1411c246ef14e7881b087255f04c56dbef48c64a0cb039b4b340"
"checksum glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3573351e846caed9f11207b275cd67bc07f0c2c94fb628e5d7c92ca056c7882d"
"checksum gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08475e4a08f27e6e2287005950114735ed61cec2cb8c1187682a5aec8c69b715"
"checksum gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a6b30f194f09a17bb7ffa95c3ecdb405abd3b75ff981f831b1f6d18fe115ff"
"checksum gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d487d333a4b87072e6bf9f2e55befa0ebef01b9496c2e263c0f4a1ff3d6c04b1"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2cb169402a3eb1ba034a7cc7d95b8b1c106e9be5ba4be79a5a93dc1a2795f4"
"checksum pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6eb49268e69dd0c1da5d3001a61aac08e2e9d2bfbe4ae4b19b9963c998f6453"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad"
"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fda0ea5f7ddabd51deeeda7799bee06274112f577da7dd3d954b8eda731b2fce"
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"

View File

@ -2,40 +2,46 @@
name = "rs" name = "rs"
version = "0.1.0" version = "0.1.0"
[lib] [dependencies]
name = "rs" bitflags = "1.0.*"
path = "@path@/src/lib.rs" clap = { version = "2.32.*", default-features = false }
crate-type = ["staticlib", "rlib"] maplit = "1.0.*"
regex = "1.1.*"
serde = { version = "1.0.*", features = ["derive"] }
serde_yaml = "0.8.*"
xkbcommon = { version = "0.4.*", features = ["wayland"] }
# Cargo can't do autodiscovery if Cargo.toml is not in the root. [dependencies.cairo-rs]
[[bin]] version = "0.5.*"
name = "test_layout"
path = "@path@/src/bin/test_layout.rs"
[[example]]
name = "test_layout"
path = "@path@/examples/test_layout.rs"
[features]
gio_v0_5 = []
gtk_v0_5 = []
rustc_less_1_36 = []
# Dependencies which don't change based on build flags
[dependencies.cairo-sys-rs] [dependencies.cairo-sys-rs]
version = "" version = ""
[dependencies.gdk]
version = ""
[dependencies.gio]
version = ""
features = ["v2_44"]
[dependencies.glib]
version = ""
features = ["v2_44"]
[dependencies.glib-sys] [dependencies.glib-sys]
version = "" version = ""
features = ["v2_44"] features = ["v2_44"]
[dependencies.gtk]
version = "0.5.*"
features = ["v3_22"]
[dependencies.gtk-sys] [dependencies.gtk-sys]
version = "" version = ""
features = ["v3_22"] features = ["v3_22"]
[dependencies]
maplit = "1.0.*" [lib]
serde = { version = "1.0.*", features = ["derive"] } name = "rs"
serde_yaml = "0.8.*" path = "src/lib.rs"
xkbcommon = { version = "0.4.*", features = ["wayland"] } crate-type = ["staticlib", "rlib"]
# Here is inserted the Cargo.deps file

View File

@ -3,47 +3,15 @@ Hacking
This document describes the standards for modifying and maintaining the *squeekboard* project. This document describes the standards for modifying and maintaining the *squeekboard* project.
Principles
----------
The project was built upon some guiding principles, which should be respected primarily by the maintainers, but also by contributors to avoid needlessly rejected changes.
The overarching principle of *squeekboard* is to empower users.
Software is primarily meant to solve problems of its users. Often in the quest to make software better, a hard distinction is made between the developer, who becomes the creator, and the user, who takes the role of the consumer, without direct influence on the software they use.
This project aims to give users the power to make the software work for them by blurring the lines between users and developers.
Notwithstanding its current state, *squeekboard* must be structured in a way that provides users a gradual way to gain more experience and power to adjust it. It must be easy, in order of importance:
- to use the software,
- to modify its resources,
- to change its behavior,
- to contribute upstream.
To give an idea of what it means in practice, those are some examples of what has been important for *squeekboard* so far:
- being quick and usable,
- allowing local overrides of resources and config,
- storing resources and config as editable, standard files,
- having complete, up to date documentation of interfaces,
- having an easy process of sending contributions,
- adapting to to user's settings and constrains without overriding them,
- avoiding compiling whenever possible,
- making it easy to build,
- having code that is [simple and obvious](https://www.python.org/dev/peps/pep-0020/),
- having an easy process of testing and accepting contributions.
You may notice that they are ordered roughly from "user-focused" to "maintainer-focused". While good properties are desired, sometimes they conflict, and maintainers should give additional weight to those benefiting the user compared to those benefiting regular contributors.
Sending patches Sending patches
--------------- ---------------
By submitting a change to this project, you agree to license it under the [GPL license version 3](https://source.puri.sm/Librem5/squeekboard/blob/master/COPYING), or any later version. You also certify that your contribution fulfills the [Developer's Certificate of Origin 1.1](https://source.puri.sm/Librem5/squeekboard/blob/master/dco.txt). By submitting a change to this project, you agree to license it under the [GPL license version 3](./COPYING), or any later version. You also certify that your contribution fulfills the [Developer's Certificate of Origin 1.1](./dco.txt).
Development environment Development environment
----------------------- -----------------------
*Squeekboard* is regularly built and tested on [the development environment](https://developer.puri.sm/Librem5/Development_Environment.html). *Squeekboard* is regularly built and tested on [the develpment environment](https://developer.puri.sm/Librem5/Development_Environment.html).
Recent Fedora releases are likely to be tested as well. Recent Fedora releases are likely to be tested as well.
@ -56,7 +24,8 @@ sudo apt-get -y install build-essential
sudo apt-get -y build-dep . sudo apt-get -y build-dep .
``` ```
For an explicit list of dependencies check the `Build-Depends` entry in the [`debian/control`](https://source.puri.sm/Librem5/squeekboard/blob/master/debian/control) file. For an explicit list of dependencies check the `Build-Depends` entry in the
[`debian/control`](./debian/control) file.
Testing Testing
------- -------
@ -113,7 +82,7 @@ User interface modules should:
Code submitted should roughly match the style of surrounding code. Things that will *not* be accepted are ones that often lead to errors: 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)) - skipping brackets `{}` after every `if()`, `else`, and similar
Bad example: Bad example:
@ -162,7 +131,7 @@ Maintenance
Squeekboard uses Rust & Cargo for some of its dependencies. Squeekboard uses Rust & Cargo for some of its dependencies.
Use the `cargo.sh` script for maintaining the Cargo part of the build. The script takes the usual Cargo commands, after the first 2 positional arguments: source directory, and output artifact. So, `cargo test` becomes: Use the `cargo.sh` script for maintaining the Cargo part of the build. The script takes the usual Cargo commands, after the first 2 positionsl arguments: source directory, and output artifact. So, `cargo test` becomes:
``` ```
cd build_dir cd build_dir
@ -175,11 +144,10 @@ 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. 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: `Cargo.lock` is used for remembering the revisions of all Rust dependencies. It should be updated often, preferably with each bugfix revision, and in a commit on its own:
``` ```
cd build_dir cd build_dir
ninja build src/Cargo.toml
sh /source_path/cargo.sh update sh /source_path/cargo.sh update
ninja test ninja test
``` ```

View File

@ -30,43 +30,30 @@ Building
### Dependencies ### Dependencies
See `.gitlab-ci.yml` or run `apt-get build-dep .` See `.gitlab-ci.yml`.
### Build from git repo ### Build from git repo
```bash ```
$ git clone https://source.puri.sm/Librem5/squeekboard.git $ git clone https://source.puri.sm/Librem5/squeekboard.git
$ cd squeekboard $ cd squeekboard
$ mkdir _build $ mkdir ../build
$ meson _build/ $ meson ../build/
$ cd _build $ cd ../build
$ ninja $ ninja test
$ ninja install
``` ```
To run tests use `ninja test`. To install squeekboard run `ninja install`.
Running Running
------- -------
```bash ```
$ phoc # if no compatible Wayland compositor is running yet $ phoc # if no compatible Wayland compositor is running yet
$ cd ../build/ $ cd ../build/
$ src/squeekboard $ src/squeekboard
``` ```
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:
```bash
busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
```
Developing Developing
---------- ----------
See [`doc/hacking.md`](doc/hacking.md) for this copy, or the [official documentation](https://developer.puri.sm/projects/squeekboard/) for the current release. See `HACKING.md`

View File

@ -13,10 +13,5 @@ CARGO_TARGET_DIR="$(pwd)"
export CARGO_TARGET_DIR export CARGO_TARGET_DIR
cd "$SOURCE_DIR" cd "$SOURCE_DIR"
cargo "$@"
# the 'run" command takes arguments at the end,
# so --manifest-path must not be last
CMD="$1"
shift
cargo "$CMD" --manifest-path "$CARGO_TARGET_DIR"/Cargo.toml "$@"

View File

@ -1,89 +0,0 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 52 }
spaceline: { width: 140, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "a z e r t y u i o p"
- "q s d f g h j k l m"
- "Shift_L w x c v b n . BackSpace"
- "show_numbers preferences space show_eschars Return"
upper:
- "A Z E R T Y U I O P"
- "Q S D F G H J K L M"
- "Shift_L W X C V B N , BackSpace"
- "show_numbers preferences space show_eschars Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars 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: "*/="
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"

View File

@ -1,89 +0,0 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 100, height: 42 }
spaceline: { width: 205, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "a z e r t y u i o p"
- "q s d f g h j k l m"
- "Shift_L w x c v b n . BackSpace"
- "show_numbers preferences space show_eschars Return"
upper:
- "A Z E R T Y U I O P"
- "Q S D F G H J K L M"
- "Shift_L W X C V B N , BackSpace"
- "show_numbers preferences space show_eschars Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars 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: "*/="
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"

View File

@ -1,78 +0,0 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 62, height: 52 }
spaceline: { width: 142, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "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 , 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 period Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "à À á Á ã Ã â Â é É"
- "show_symbols ê Ê í Í ó Ó ô Ô"
- "show_letters õ Õ ú Ú ü Ü period BackSpace"
symbols:
- "@ # $ % - + ÷ × = ≠"
- "( ) § & < > / * { }"
- "show_numbers_from_symbols º \" ' colon ; ! ? 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: "1ã"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "1ã"
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: ":"

View File

@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -1,98 +0,0 @@
---
outlines:
default: { width: 32, height: 52 }
altline: { width: 48.39024, height: 52 }
wide: { width: 62, height: 52 }
outline7: { width: 88.97561, height: 52 }
spaceline: { width: 150.5853, 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 . 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 . Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' : ; ! ? BackSpace"
- "show_letters preferences space . Return"
symbols:
- "~ ` | U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree * { }"
- "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: "altline"
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: "*/="
".":
outline: altline
space:
outline: spaceline
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
U00B7:
text: "·"
squareroot:
text: "√"
Greek_pi:
text: "π"
division:
text: "÷"
multiply:
text: "×"
paragraph:
text: "¶"
Greek_tau:
text: "τ"
copyright:
text: "©"
U00AE:
text: "®"
U00A3:
text: "£"
EuroSign:
text: "€"
U00A5:
text: "¥"
asciicircum:
text: "^"
degree:
text: "°"

View File

@ -46,7 +46,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -1,80 +1,16 @@
--- ---
outlines: outlines:
default: { width: 52, height: 52 } default: { width: 52, height: 52 }
altline: { width: 40, height: 52 } altline: { width: 52, height: 52 }
narrow: { width: 22, height: 52 }
views: views:
base: base:
- "😀 😁 😅 😂 😊 😇 🙃" - "😀 😁 😅 😂 😊 😇 🙃"
- "😍 😘 😋 😜 😎 🥳 😔" - "😍 😘 😋 😜 😎 🥳 😔"
- "😢 😭 😡 😱 🤔 😬 🙄" - "😢 😭 😡 😱 🤔 😬 🙄"
- "preferences blank 1 2 3 4 5 6 blank BackSpace" - "preferences 🤨 🤓 😴 🤢 🤮 😈"
two:
- "🤩 🤨 🤓 😴 🤢 🤮 😈"
- "💩 🙌 👏 👍 👎 👌 👋"
- "💪 🖕 🙏 💋 🤦‍♀️ 🤷‍♀️ 💃"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
three:
- "🐶 🐱 🐯 🙈 🐴 🦄 🌳"
- "🍀 🌹 💫 ⭐️ ✨ 💥 🔥"
- "🌈 ☀️ 🌤 🌧 ⛄️ ☂️ 🌊"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
four:
- "🍎 🍓 🍑 🍍 🍆 🥑 🥦"
- "🍕 🎂 🍫 🍿 🍻 🍾 🍽"
- "⚽️ 🏀 🏓 🏆 🎹 🎸 🎯"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
five:
- "🚗 🚌 🚲 🚄 🚂 ✈️ 🛰"
- "🚀 🛸 🚁 🚦 🏝 🏔 ⛺️"
- "🏠 🏢 🏥 🏛 🛤 🌅 🎇"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
six:
- "⌚️ 📱 💻 🖥 🖨 🕹 ✉️"
- "📞 ☎️ ⏰ ⏳ 📈 📉 📌"
- "🎁 ❤️ 💕 💯 ✅ ❎ 📢"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
buttons: buttons:
1:
action:
set_view: "base"
outline: "altline"
label: "1"
2:
action:
set_view: "two"
outline: "altline"
label: "2"
3:
action:
set_view: "three"
outline: "altline"
label: "3"
4:
action:
set_view: "four"
outline: "altline"
label: "4"
5:
action:
set_view: "five"
outline: "altline"
label: "5"
6:
action:
set_view: "six"
outline: "altline"
label: "6"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
keysym: BackSpace
blank:
outline: "narrow"
text: ""

View File

@ -44,7 +44,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -1,89 +0,0 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 52 }
spaceline: { width: 140, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "a z e r t y u i o p"
- "q s d f g h j k l m"
- "Shift_L w x c v b n . BackSpace"
- "show_numbers preferences space show_eschars Return"
upper:
- "A Z E R T Y U I O P"
- "Q S D F G H J K L M"
- "Shift_L W X C V B N , BackSpace"
- "show_numbers preferences space show_eschars Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars 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: "*/="
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"

View File

@ -1,89 +0,0 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 100, height: 42 }
spaceline: { width: 205, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "a z e r t y u i o p"
- "q s d f g h j k l m"
- "Shift_L w x c v b n . BackSpace"
- "show_numbers preferences space show_eschars Return"
upper:
- "A Z E R T Y U I O P"
- "Q S D F G H J K L M"
- "Shift_L W X C V B N , BackSpace"
- "show_numbers preferences space show_eschars Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars 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: "*/="
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"

View File

@ -1,92 +0,0 @@
# Friulian layout created by Fabio Tomat
# 14 october 2020
---
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 \\ / < > = [ ] BackSpace"
- "show_letters show_eschars preferences space ? . Return"
eschars:
- "â ê î ô û Â Ê Î Ô Û"
- "à è ì ò ù À È Ì Ò Ù"
- "show_numbers ç Ç ᶜ ᵐ ⁿ ᵉ ᵗ BackSpace"
- "show_letters show_eschars preferences space ᶠ . Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: "erase"
preferences:
action: "show_prefs"
outline: "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: "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: "àê"
space:
outline: "spaceline"
label: " "
text: " "
Return:
outline: "altline"
icon: "key-enter"
keysym: "Return"
colon:
label: ":"
"\"":
keysym: "quotedbl"

View File

@ -13,22 +13,22 @@ views:
- "q w e r t y u i o p" - "q w e r t y u i o p"
- "a s d f g h j k l" - "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace" - "Shift_L z x c v b n m BackSpace"
- "show_numbers show_eschars preferences space , . Return" - "show_numbers show_eschars preferences space , period Return"
upper: upper:
- "Q W E R T Y U I O P" - "Q W E R T Y U I O P"
- "A S D F G H J K L" - "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace" - "Shift_L Z X C V B N M BackSpace"
- "show_numbers show_eschars preferences space ? . Return" - "show_numbers show_eschars preferences space ? period Return"
numbers: numbers:
- "1 2 3 4 5 6 7 8 9 0" - "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )" - "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace" - "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters show_eschars preferences space ? . Return" - "show_letters show_eschars preferences space ? period Return"
symbols: symbols:
- "~ ` | · √ π τ ÷ × ¶" - "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }" - "© ® £ $ ¥ ^ ° * { }"
- "show_numbers \\ / < > = [ ] BackSpace" - "show_numbers \\ / < > = [ ] BackSpace"
- "show_letters show_eschars preferences space ? . Return" - "show_letters show_eschars preferences space ? period Return"
eschars: eschars:
- "á é í ó ú Á É Í Ó Ú" - "á é í ó ú Á É Í Ó Ú"
- "à è ì ò « » ù ! { }" - "à è ì ò « » ù ! { }"
@ -46,7 +46,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"
@ -76,10 +76,12 @@ buttons:
set_view: "eschars" set_view: "eschars"
outline: "altline" outline: "altline"
label: "àè" label: "àè"
period:
outline: "default"
label: "."
space: space:
outline: "spaceline" outline: "spaceline"
label: " " label: " "
text: " "
Return: Return:
outline: "altline" outline: "altline"
icon: "key-enter" icon: "key-enter"

View File

@ -195,7 +195,7 @@ buttons:
BackSpace: BackSpace:
outline: "wide" outline: "wide"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"

View File

@ -195,7 +195,7 @@ buttons:
BackSpace: BackSpace:
outline: "wide" outline: "wide"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -16,7 +16,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
space: space:
outline: spaceline outline: spaceline
text: " " text: " "

View File

@ -1,110 +0,0 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 52 }
spaceline: { width: 140, 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 show_accents 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 show_upper_accents Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space period Return"
accents:
- "q w ę r t y u i ó p"
- "ą ś d f g h j k ł"
- "accents_show_upper ż ź ć v b ń m BackSpace"
- "show_numbers preferences space show_accents Return"
upper_accents:
- "Q W Ę R T Y U I Ó P"
- "Ą Ś D F G H J K Ł"
- "accents_show_upper Ż Ź Ć V B Ń M BackSpace"
- "show_numbers preferences space show_upper_accents Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
accents_show_upper:
action:
locking:
lock_view: "upper_accents"
unlock_view: "accents"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "ABC"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_accents:
action:
locking:
lock_view: "accents"
unlock_view: "base"
outline: "altline"
label: "ąę"
show_upper_accents:
action:
locking:
lock_view: "upper_accents"
unlock_view: "upper"
outline: "altline"
label: "ĄĘ"
period:
outline: "altline"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

View File

@ -1,102 +0,0 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 100, height: 42 }
spaceline: { width: 206, 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 preferences space show_accents 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 show_upper_accents Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space period Return"
accents:
- "q w ę r t y u i ó p"
- "ą ś d f g h j k ł"
- "Shift_L ż ź ć v b ń m BackSpace"
- "show_numbers preferences space show_accents Return"
upper_accents:
- "Q W Ę R T Y U I Ó P"
- "Ą Ś D F G H J K Ł"
- "Shift_L Ż Ź Ć V B Ń M BackSpace"
- "show_numbers preferences space show_upper_accents Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "ABC"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_accents:
action:
locking:
lock_view: "accents"
unlock_view: "base"
outline: "altline"
label: "ąę"
show_upper_accents:
action:
locking:
lock_view: "upper_accents"
unlock_view: "upper"
outline: "altline"
label: "ĄĘ"
period:
outline: "altline"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

View File

@ -1,94 +0,0 @@
---
outlines:
default: { width: 32, height: 52 }
altline: { width: 32, height: 52 }
wide: { width: 57, height: 52 }
narrow: { width: 26, height: 52 }
spaceline: { width: 107, height: 52 }
fill: { width: 159, height: 52 }
special: { width: 42, height: 52 }
views:
base:
- "й ц у к е н г ш щ з х"
- "ф ы в а п р о л д ж э"
- "Shift_L я ч с м и т ь б ю BackSpace"
- "show_numbers preferences ё space ъ period Return"
upper:
- "Й Ц У К Е Н Г Ш Щ З Х"
- "Ф Ы В А П Р О Л Д Ж Э"
- "Shift_L Я Ч С М И Т Ь Б Ю BackSpace"
- "show_numbers preferences Ё space Ъ comma Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space_fill period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space_fill 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: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "АБВ"
show_symbols:
action:
set_view: "symbols"
outline: "wide"
label: "*/="
period:
outline: "special"
text: "."
comma:
outline: "special"
text: ","
space:
outline: "spaceline"
text: " "
space_fill:
outline: "fill"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
ё:
outline: "narrow"
Ё:
outline: "narrow"
ъ:
outline: "narrow"
Ъ:
outline: "narrow"

View File

@ -2,10 +2,9 @@
outlines: outlines:
default: { width: 32, height: 52 } default: { width: 32, height: 52 }
altline: { width: 48.39024, height: 52 } altline: { width: 48.39024, height: 52 }
wide: { width: 64, height: 52 } wide: { width: 62, height: 52 }
spaceline: { width: 142, height: 52 } outline7: { width: 88.97561, height: 52 }
special: { width: 44, height: 52 } spaceline: { width: 150.5853, height: 52 }
views: views:
base: base:
@ -26,7 +25,7 @@ views:
symbols: symbols:
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph" - "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright" - "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
- "show_numbers_from_symbols backslash slash less greater equal bracketleft bracketright BackSpace" - "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
- "show_letters preferences space . Return" - "show_letters preferences space . Return"
buttons: buttons:
@ -38,27 +37,22 @@ buttons:
outline: "altline" outline: "altline"
icon: "key-shift" icon: "key-shift"
BackSpace: BackSpace:
outline: altline outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "altline"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
show_numbers: show_numbers:
outline: "wide"
action: action:
set_view: "numbers" set_view: "numbers"
label: "123" outline: "altline"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: altline
label: "123" label: "123"
show_letters: show_letters:
outline: "wide"
action: action:
set_view: "base" set_view: "base"
outline: "altline"
label: "ABC" label: "ABC"
show_symbols: show_symbols:
action: action:
@ -66,7 +60,7 @@ buttons:
outline: "altline" outline: "altline"
label: "*/=" label: "*/="
".": ".":
outline: "special" outline: altline
space: space:
outline: spaceline outline: spaceline
text: " " text: " "
@ -162,3 +156,4 @@ buttons:
text: "[" text: "["
bracketright: bracketright:
text: "]" text: "]"

View File

@ -1,44 +1,38 @@
--- ---
outlines: outlines:
default: { width: 35.33, height: 46 } default: { width: 35.33, height: 52 }
action: { width: 59, height: 46 } action: { width: 59, height: 52 }
altline: { width: 52.67, height: 46 } altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 46 } wide: { width: 59, height: 52 }
spaceline: { width: 140, height: 46 } spaceline: { width: 140, height: 52 }
special: { width: 44, height: 46 } special: { width: 44, height: 52 }
small: { width: 59, height: 22 }
views: views:
base: base:
- "Ctrl Alt ↑ ↓ ← →"
- "q w e r t y u i o p" - "q w e r t y u i o p"
- "a s d f g h j k l" - "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace" - "Shift_L z x c v b n m BackSpace"
- "show_numbers preferences space show_actions Return" - "show_numbers preferences space show_actions Return"
upper: upper:
- "Ctrl Alt PgUp PgDn Home End"
- "Q W E R T Y U I O P" - "Q W E R T Y U I O P"
- "A S D F G H J K L" - "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace" - "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space show_actions Return" - "show_numbers preferences space show_actions Return"
numbers: numbers:
- "Ctrl Alt ↑ ↓ ← →"
- "1 2 3 4 5 6 7 8 9 0" - "1 2 3 4 5 6 7 8 9 0"
- "* # $ / & - _ + ( )" - "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace" - "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return" - "show_letters preferences space period Return"
symbols: symbols:
- "Ctrl Alt ↑ ↓ ← →"
- "~ ` | · √ π τ ÷ × ¶" - "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° @ { }" - "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace" - "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space period Return" - "show_letters preferences space period Return"
actions: actions:
- "Ctrl Alt PgUp PgDn Home End"
- "F1 F2 F3 F4 F5 F6" - "F1 F2 F3 F4 F5 F6"
- "F7 F8 F9 F10 F11 F12" - "F7 F8 F9 F10 F11 F12"
- "Esc Tab Pause Insert Up Del" - "Esc Tab Del PgUp ↑ PgDn"
- "show_letters Menu Break Left Down Right" - "show_letters Home End ← ↓ →"
buttons: buttons:
Shift_L: Shift_L:
@ -51,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"
@ -75,14 +69,14 @@ buttons:
action: action:
set_view: "symbols" set_view: "symbols"
outline: "altline" outline: "altline"
label: "τ=\\" label: "*/="
show_actions: show_actions:
action: action:
set_view: "actions" set_view: "actions"
outline: "altline" outline: "altline"
label: ">_" label: ">_"
period: period:
outline: "altline" outline: "special"
text: "." text: "."
space: space:
outline: "spaceline" outline: "spaceline"
@ -138,67 +132,28 @@ buttons:
Del: Del:
outline: "action" outline: "action"
keysym: "Delete" keysym: "Delete"
Insert:
outline: "action"
keysym: "Insert"
Menu:
outline: "action"
keysym: "Menu"
Pause:
outline: "action"
keysym: "Pause"
Menu:
outline: "action"
keysym: "Menu"
Break:
outline: "action"
keysym: "Break"
Home: Home:
outline: "small" outline: "action"
keysym: "Home" keysym: "Home"
End: End:
outline: "small" outline: "action"
keysym: "End" keysym: "End"
PgUp: PgUp:
outline: "small" outline: "action"
keysym: "Page_Up" keysym: "Page_Up"
PgDn: PgDn:
outline: "small" outline: "action"
keysym: "Page_Down" keysym: "Page_Down"
"↑": "↑":
outline: "small" outline: "action"
keysym: "Up" keysym: "Up"
"↓": "↓":
outline: "small" outline: "action"
keysym: "Down" keysym: "Down"
"←": "←":
outline: "small" outline: "action"
keysym: "Left" keysym: "Left"
"→": "→":
outline: "small"
keysym: "Right"
Up:
label: "↑"
outline: "action"
keysym: "Up"
Left:
label: "←"
outline: "action"
keysym: "Left"
Down:
label: "↓"
outline: "action"
keysym: "Down"
Right:
label: "→"
outline: "action" outline: "action"
keysym: "Right" keysym: "Right"
Ctrl:
modifier: "Control"
outline: "small"
label: "Ctrl"
Alt:
modifier: "Alt"
outline: "small"
label: "Alt"

View File

@ -1,211 +0,0 @@
---
outlines:
default: { width: 54, height: 37 }
action: { width: 90, height: 37 }
altline: { width: 81, height: 37 }
wide: { width: 90, height: 37 }
spaceline: { width: 225, height: 37 }
special: { width: 54, height: 37 }
small: { width: 67.4, height: 22 }
views:
base:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "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 show_actions Return"
upper:
- "EscSmall TabSmall Ctrl Alt PgUp PgDn Home End"
- "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 show_actions Return"
numbers:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "1 2 3 4 5 6 7 8 9 0"
- "* # $ / & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° @ { }"
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace"
- "show_letters preferences space period Return"
actions:
- "EscSmall TabSmall Ctrl Alt PgUp PgDn Home End"
- "F1 F2 F3 F4 F5 F6"
- "F7 F8 F9 F10 F11 F12"
- "Esc Tab Pause Insert Up Del"
- "show_letters Menu Break Left Down Right"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "ABC"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "τ=\\"
show_actions:
action:
set_view: "actions"
outline: "altline"
label: ">_"
period:
outline: "altline"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
F1:
outline: "action"
keysym: "F1"
F2:
outline: "action"
keysym: "F2"
F3:
outline: "action"
keysym: "F3"
F4:
outline: "action"
keysym: "F4"
F5:
outline: "action"
keysym: "F5"
F6:
outline: "action"
keysym: "F6"
F7:
outline: "action"
keysym: "F7"
F8:
outline: "action"
keysym: "F8"
F9:
outline: "action"
keysym: "F9"
F10:
outline: "action"
keysym: "F10"
F11:
outline: "action"
keysym: "F11"
F12:
outline: "action"
keysym: "F12"
Esc:
outline: "action"
keysym: "Escape"
EscSmall:
outline: "small"
keysym: "Escape"
label: "Esc"
Tab:
outline: "action"
keysym: "Tab"
TabSmall:
outline: "small"
keysym: "Tab"
label: "Tab"
Del:
outline: "action"
keysym: "Delete"
Insert:
outline: "action"
keysym: "Insert"
Menu:
outline: "action"
keysym: "Menu"
Pause:
outline: "action"
keysym: "Pause"
Menu:
outline: "action"
keysym: "Menu"
Break:
outline: "action"
keysym: "Break"
Home:
outline: "small"
keysym: "Home"
End:
outline: "small"
keysym: "End"
PgUp:
outline: "small"
keysym: "Page_Up"
PgDn:
outline: "small"
keysym: "Page_Down"
"↑":
outline: "small"
keysym: "Up"
"↓":
outline: "small"
keysym: "Down"
"←":
outline: "small"
keysym: "Left"
"→":
outline: "small"
keysym: "Right"
Up:
label: "↑"
outline: "action"
keysym: "Up"
Left:
label: "←"
outline: "action"
keysym: "Left"
Down:
label: "↓"
outline: "action"
keysym: "Down"
Right:
label: "→"
outline: "action"
keysym: "Right"
Ctrl:
modifier: "Control"
outline: "small"
label: "Ctrl"
Alt:
modifier: "Alt"
outline: "small"
label: "Alt"

View File

@ -1,94 +0,0 @@
---
outlines:
default: { width: 32, height: 52 }
altline: { width: 32, height: 52 }
wide: { width: 57, height: 52 }
narrow: { width: 26, height: 52 }
spaceline: { width: 107, height: 52 }
fill: { width: 159, height: 52 }
special: { width: 42, height: 52 }
views:
base:
- "й ц у к е н г ш щ з х"
- і в а п р о л д ж є"
- "Shift_L я ч с м и т ь б ю BackSpace"
- "show_numbers preferences ґ space ї period Return"
upper:
- "Й Ц У К Е Н Г Ш Щ З Х"
- І В А П Р О Л Д Ж Є"
- "Shift_L Я Ч С М И Т Ь Б Ю BackSpace"
- "show_numbers preferences Ґ space Ї comma Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space_fill period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space_fill 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: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "АБВ"
show_symbols:
action:
set_view: "symbols"
outline: "wide"
label: "*/="
period:
outline: "special"
text: "."
comma:
outline: "special"
text: ","
space:
outline: "spaceline"
text: " "
space_fill:
outline: "fill"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
ґ:
outline: "narrow"
Ґ:
outline: "narrow"
ї:
outline: "narrow"
Ї:
outline: "narrow"

View File

@ -39,9 +39,9 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: show_prefs action: "show_prefs"
outline: "special" outline: "special"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
show_numbers: show_numbers:

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -0,0 +1,8 @@
us Englisch (US)
de Deutsch
el Griechisch
es Spanisch
it Italienisch
jp+kana Japanisch (Kana)
no Norwegisch

View File

@ -1,2 +1,10 @@
emoji Emoji us English (US)
terminal Terminal de German
el Greek
es Spanish
fi Finnish
it Italian
jp+kana Japanese (kana)
no Norwegian
se Swedish

View File

@ -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)

View File

@ -0,0 +1,8 @@
us 英語 (US)
de ドイツ語
el ギリシャ語
es スペイン語
it イタリア語
jp+kana 日本語 (かな)
nb ノルウェー語

View File

@ -1,2 +1,9 @@
emoji emoji us angielski (USA)
terminal terminal de niemiecki
el grecki
es hiszpański
fi fiński
it włoski
jp+kana japoński (kana)
no norweski
se szwedzki

View File

@ -1,11 +0,0 @@
de Немецкий
es Испанский
fi Финский
gr Греческий
it Итальянский
no Норвежский
pl Польский
ru Русский
se Шведский
terminal Терминал
us Английский (США)

View File

@ -7,20 +7,13 @@ squeekboard_resources = gnome.compile_resources(
c_name: 'squeekboard', c_name: 'squeekboard',
) )
desktopconf = configuration_data()
desktopconf.set('bindir', bindir)
desktop_file = 'sm.puri.Squeekboard.desktop' desktop_file = 'sm.puri.Squeekboard.desktop'
i18n.merge_file('desktop', i18n.merge_file('desktop',
input: configure_file( input: desktop_file + '.in',
input: desktop_file + '.in.in',
output: desktop_file + '.in',
configuration: desktopconf
),
output: desktop_file, output: desktop_file,
po_dir: '../po', po_dir: '../po',
install: true, install: true,
install_dir: desktopdir, install_dir: join_paths(datadir, 'applications'),
type: 'desktop' type: 'desktop'
) )

View File

@ -2,7 +2,7 @@
Name=Squeekboard Name=Squeekboard
GenericName=Squeekboard Virtual Keyboard GenericName=Squeekboard Virtual Keyboard
Comment=Virtual Keyboard Comment=Virtual Keyboard
Exec=@bindir@/squeekboard Exec=squeekboard
Terminal=false Terminal=false
Type=Application Type=Application
NoDisplay=true NoDisplay=true

View File

@ -40,10 +40,6 @@ sq_button.action {
font-size: 0.75em; font-size: 0.75em;
} }
sq_button.small {
font-size: 0.5em;
}
#Return { #Return {
background: #1c71d8; background: #1c71d8;
border-color: #1a5fb4; border-color: #1a5fb4;

View File

@ -43,10 +43,6 @@ sq_button.action {
font-size: 0.75em; font-size: 0.75em;
} }
sq_button.small {
font-size: 0.5em;
}
#Return { #Return {
background: @theme_selected_bg_color; /* #1c71d8; */ background: @theme_selected_bg_color; /* #1c71d8; */
border-color: @borders; /*#1a5fb4;*/ border-color: @borders; /*#1a5fb4;*/

227
debian/changelog vendored
View File

@ -1,230 +1,3 @@
squeekboard (1.11.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* UI: Delay hiding only when leaving a text field
* ui: Cancel hiding delay when activity requested again
* Update dependencies
[ Fabio Tomat ]
* Update fur-IT.txt fix typo for Spanish
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Sat, 14 Nov 2020 06:46:28 +0000
squeekboard (1.10.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* virtual_keyboard: Fix desynced modifiers state
* rust: Fix deprecation warnings
* docs: Tutorial syntax cleanups
* docs: Reorganize tutorial
* build: Error on repeating declarations
* keymap: Generate from symbol map, not layout
* data: Restore testability of action->keysym conversion
* syntax: Let older rustc understand symbolmap's lifetime
* debian: Insert a "breaks" for librem5-base < 24
* keymap: Keep keymap fd management in one place
* vkeyboard: Use a generic slice instead of a vector
* tests: Check for missing return in builtin layouts except emoji
* keymap: Concentrate special handling of BackSpace, which is implicit in Erase action
* keymaps: Use multiple key maps, each within the limit of what Xorg can accept.
* build: Avoid MaybeUninit on older Debian
* tests: Fix bad field access
* cargo: Update dependencies
[ Guido Günther ]
* eekboard-context-service: Return early if schema is unavailable
* treewide: Use new style function definitions
* build: Enable '-Wold-style-definition' '-Wstrict-prototypes'
* build: Enable '-Wunused-function'
* eekboard-context-service: Drop EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE
* keyboard: Fix warning
* layout: Fix warning
* gitlab-ci: Enable --Werror
* eek-keyboard: Don't ignore return value
* build: Enable -Winit-self
* build: Enable -Wformat-security
* build: Enable -Wmaybe-uninitialized
* treewide: Drop redundant declarations
* build: Enable -Wredundant-declarations
* ServerContextService: Drop GObject boilerplate
* build: Enable '-Wformat-nonliteral'
* eekboad-context-service: Drop signal class handler
* eekboard-context-service: Drop docstrings for inexistent functions
* eekboard-context-service: Drop the GObject boilerplate
* eekboard-context-service: Drop private struct
* server-context-service: Consistenty name self argument 'self'
* server-context-service: swap signal arguments
* server-context-service: Don't show keyboard when disabled (Closes: #222)
[ Nazarii Kretovych ]
* Add Ukrainian keyboard layout.
[ Benjamin Schaaf ]
* Fix spelling mistakes in doc/hacking.md
* Expand the development documentation in the readme
* Expand key press detection to the edges of the view's bounding box
* Sort layouts by type before sorting by name
* Fix leak in level_keyboard_new
* Fix leak endlessly adding a resource path to the default theme
* Add settings option to popover
[ Al ]
* proposal for belgian layout (copy of fr)
* alphabetical order for src/resources.rs tests/meson.build
[ Arnaud Ferraris ]
* eek-gtk-keyboard: use virtual resolution to check arrangement kind
* server-context-service: optimize height calculation
* keyboards: add wide French layout
* keyboards: add wide Belgian layout
* keyboards: add wide terminal layout
[ Fabio Tomat ]
* Revert "Add friulian keyboard"
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 19 Oct 2020 14:07:01 +0000
squeekboard (1.9.3) amber-phone; urgency=medium
[ Björn Tantau ]
* Show more useful keys at the same time.
* Add Ctrl and Alt modifier keys.
* Add missing Ê key.
* Make f-keys slightly wider.
* Add Menu key.
[ Guido Günther ]
* d/rules: Only remove Cargo.lock if it exists
* eek: Drop libcanberra usage
* debian: Build-depend on libfeedback
* eek-gtk-keyboard: Trigger event feedback on button press (Closes: #166)
[ Dorota Czaplejewicz ]
* build: Add missing gio-unix dependency
* build: Make compatible with Debian Bullseye
* debian: Add amber to legacy distro list
* ci: Add amber job
* debian: Require lsb-release
* size: Hardcode size to work around screen rotation
* ci: Re-add x64 Buster build
* italian: Fix space and period
[ Sebastian Krzyszkowiak ]
* Revert "Merge branch 'btantau-master-patch-76686' into 'master'"
* Terminal layout: another approach
[ Luís Fernando Stürmer da Rosa ]
* Brazilian Portuguese Keyboard Layout.
-- Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm> Wed, 05 Aug 2020 16:16:08 +0200
squeekboard (1.9.2) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* keyboard: Remove unused code
* gsettings: Don't crash when unavailable
* dbus: Don't crash if can't make a connection
* gsettings: Don't crash on switching when unavailable
* layout: Split out choice to a struct on its own
* renderer: Simplify by dropping gobjectness
* levelkeyboard: Rearrange to make future conversion easier
* layout: Minor generalizations
* Remove unused code
* sizing: Create a standalone UI shape manager
* sizing: Ignore scaling factor for layout selection
* CI: Fix typo
* Update rust deps for release
[ Andreas Rönnquist ]
* Swedish keyboard, wide button switching between numbers, symbols and base
* More fixes of button sizes
* Folder is doc, not docs
[ uzanto ]
* Add new file
* Replace duplicated show_symbols by show_eschars and removed "Delete" button that it's doing nothing
[ Arnaud Ferraris ]
* keyboards: fr: fix keyboard layout
* keyboards: fr: make sure the layout fits the screen
* resources: include French keyboard layout
* keyboards: fr: improve consistency with other layouts
* keyboards: fr: improve diacritics layout
* tests: add french layout
[ Vlad ]
* Fresh Russian layout
[ Jordi Masip ]
* Removed unused dependency 'libcroco'
[ Florian Klink ]
* sm.puri.Squeekboard.desktop: make path to Exec= absolute
[ Ole Guldberg ]
* Danish keyboard layout
* Danish keyboard layout
* add test for danish layout
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 01 Jun 2020 09:39:12 +0000
squeekboard (1.9.1) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* layout: Improve press handling
* settings: Handle empty settings
* Variant: Use proper pointer conversion between C and Rust
* meta: Add doap file
* modifiers: Support Control and Alt
* CI: Test that any bump to changelog has a corresponding tag
* docs: Add the guiding principle
* hacking: Move into docs/
[ &t ]
* Fix minor comment typos
[ Dorota Czaplejewicz ]
* cargo: Bump package versions before release
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Sun, 08 Mar 2020 10:04:29 +0000
squeekboard (1.9.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* imservice: Add commit_string method
* submission: Handle submitting strings
* input_method: Use for erasing
* logging: Use in merged functions
* translations: Remove redundant ones
* translations: Translate builtin layouts
* greek: Rename to gr which is used by gnome settings
[ Sebastian Krzyszkowiak ]
* layouts: Add Polish layouts
[ Dorota Czaplejewicz ]
* locks: Draw based on current view
* locking: Lock keys statelessly
* layouts: Better accented uppercase in PL
* emoji: Add more choices
* row: Eliminate angle
* layout: Center views relative to each other and the layout bounds
* drawing: Generalized foreach_visible_button
* variant: Fix double-free
* variant: Fix leak
* keyboard_layout: Fix leak
* layout: Improve scoping of locked variable
* terminal: Make */ easier to reach
[ Sebastian Krzyszkowiak ]
* layouts: terminal: Use altline outline for dot key
[ Dorota Czaplejewicz ]
* text input: Disable erasing
* cargo: Update deps
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Wed, 19 Feb 2020 14:32:39 +0000
squeekboard (1.8.1) amber-phone; urgency=medium squeekboard (1.8.1) amber-phone; urgency=medium
[ Dorota Czaplejewicz ] [ Dorota Czaplejewicz ]

View File

@ -1,10 +0,0 @@
#!/usr/bin/env python3
"""Checks tag before release.
Feed it the first changelog line, and then all available tags.
"""
import re, sys
tag = "v" + re.findall("\\((.*)\\)", input())[0]
if tag not in map(str.strip, sys.stdin.readlines()):
raise Exception("Changelog's current version doesn't have a tag. Push the tag!")

5
debian/control vendored
View File

@ -11,7 +11,7 @@ Build-Depends:
libglib2.0-dev, libglib2.0-dev,
libgnome-desktop-3-dev, libgnome-desktop-3-dev,
libgtk-3-dev, libgtk-3-dev,
libfeedback-dev, libcroco3-dev,
librust-bitflags-1-dev (>= 1.0), librust-bitflags-1-dev (>= 1.0),
librust-clap-2+default-dev (>= 2.32), librust-clap-2+default-dev (>= 2.32),
librust-gio+v2-44-dev, librust-gio+v2-44-dev,
@ -25,7 +25,6 @@ Build-Depends:
librust-serde-yaml-0.8-dev (>= 0.8), librust-serde-yaml-0.8-dev (>= 0.8),
librust-xkbcommon-0.4+wayland-dev (>= 0.4), librust-xkbcommon-0.4+wayland-dev (>= 0.4),
libwayland-dev (>= 1.16), libwayland-dev (>= 1.16),
lsb-release,
rustc, rustc,
wayland-protocols (>= 1.14), wayland-protocols (>= 1.14),
Standards-Version: 4.1.3 Standards-Version: 4.1.3
@ -38,8 +37,6 @@ Depends:
gnome-themes-extra-data, gnome-themes-extra-data,
${shlibs:Depends}, ${shlibs:Depends},
${misc:Depends}, ${misc:Depends},
Breaks:
librem5-base (<< 24),
Description: On-screen keyboard for Wayland Description: On-screen keyboard for Wayland
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone. Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.

13
debian/rules vendored
View File

@ -3,20 +3,13 @@
export CARGO_HOME = $(CURDIR)/debian/cargo export CARGO_HOME = $(CURDIR)/debian/cargo
export DEB_BUILD_MAINT_OPTIONS = hardening=+all export DEB_BUILD_MAINT_OPTIONS = hardening=+all
distrel := $(shell lsb_release --codename --short)
ifneq (,$(filter $(distrel),buster amber))
legacy = true
else
legacy = false
endif
%: %:
dh $@ --builddirectory=_build --buildsystem=meson dh $@ --builddirectory=_build --buildsystem=meson
# The Debian version of linked-hash-map doesn't provide any hash, # The Debian version of linked-hash-map doesn't provide any hash,
# causing Cargo to refuse to build with a crates.io copy # causing Cargo to refuse to build with a crates.io copy
override_dh_auto_configure: build-arch:
[ ! -f Cargo.lock ] || rm Cargo.lock rm Cargo.lock
dh_auto_configure -- -Dlegacy=$(legacy) dh $@ --builddirectory=_build --buildsystem=meson
override_dh_autoreconf: override_dh_autoreconf:

View File

@ -5,23 +5,17 @@ Contents
-------- --------
* [Tutorial](tutorial.md) * [Tutorial](tutorial.md)
* [Contributing](hacking.md)
Introduction Introduction
------------ ------------
Squeekboard is the on-screen keyboard for the Librem 5 phone. For information about building, look at the [README](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md). Squeekboard is the on-screen keyboard for the Librem 5 phone. For more information, look at the [README](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md).
Layouts Layouts
------- -------
Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The [tutorial](tutorial.md) explains the process in detail. Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The [tutorial](/tutorial.md) explains the process in detail.
Layouts are created using a text-based format, based on YAML. Layouts are created using a text-based format, based on YAML.
TODO: Provide a description of the format. TODO: Provide a description of the format.
Contributions
-------------
Anyone is free to modify *squeekboard*. See the [contributing document](hacking.md).

View File

@ -1,92 +1,55 @@
A guide to creating layouts Kareema's guide to creating layouts
=========================== ===================================
This guide is based on the original Kareema's [forum post](https://forums.puri.sm/t/translations-and-virtual-touch-keyboards-tracking-localization/7669/48). Its long overdue to write a comprehensive guide how to add a keyboard layout from start. But unfortunately, I dont have much time left ATM. A lot of information can be found in [this ](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5) thread.
Its long overdue to write a comprehensive guide how to add a keyboard layout from start. But unfortunately, I dont have much time left ATM. A lot of information can be found in [this](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5) thread. So at least I will try to start writing a short how-to here and edit this post as I find the time. Hope this helps a bit - comments and corrections welcome.
So at least I will try to start writing a short how-to here and edit this post as I find the time. Hope this helps a bit - comments and corrections [welcome](https://source.puri.sm/Librem5/squeekboard/-/merge_requests/) **Get one of the existing keyboard layouts**
## Creating a new layout * You can get one of the keyboards from the squeekboard git repository : [https://source.puri.sm/Librem5/squeekboard ](https://source.puri.sm/Librem5/squeekboard)
* The keyboard layouts are located in the subdirectory `data/keyboard/` in the `.yaml` files
Creating a layout is easy. You don't need to recompile things, just edit and test. It's easiest to start with an existing layout.
### Get one of the existing keyboard layouts
* You can get one of the keyboards from the squeekboard git repository : [https://source.puri.sm/Librem5/squeekboard](https://source.puri.sm/Librem5/squeekboard)
* The keyboard layouts are located in the subdirectory [`data/keyboards/`](https://source.puri.sm/Librem5/squeekboard/-/tree/master/data/keyboards) in the `.yaml` files
* Take a look and try to understand them :slight_smile: * Take a look and try to understand them :slight_smile:
**Fork your own copy of squeekboard**
### 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
```
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.
### 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.
You can also use the `test_layout` tool from the -devel package to check it for errors:
```
# squeekboard_test_layout ./mylayout.yaml
Test result: OK
```
## Contributing your changes
If you want to share your layout with the world, the best way is to submit it to the Squeekboard project. The workflow is similar to any other Gitlab-based project.
Above all, your layout should be working, be tested, not break anything, and make sense.
### Fork your own copy of squeekboard
* Best way would be to start with a fork of the squeekboard repository: Create a user account at https://source.puri.sm/, go the the squeekboard git repository, press “Fork” in the web interface. You can find further instructions [here](https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html#creating-a-fork). * Best way would be to start with a fork of the squeekboard repository: Create a user account at https://source.puri.sm/, go the the squeekboard git repository, press “Fork” in the web interface. You can find further instructions [here](https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html#creating-a-fork).
* Clone your fork locally with `git clone` and use the uri of your forked repo there * Clone your fork locally with `git clone` and use the uri of your forked repo there
### Edit your keyboard and get it merged **Workflow to edit your keyboard and get it merged**
* It may be useful to check out the [generic guide how the workflow to contribute works](https://developer.puri.sm/Librem5/Contact/Contributing.html) * A generic guide how the workflow to contribute works, can be found at https://developer.puri.sm/Librem5/Contact/Contributing.html
* Create a branch: Name it “keyboard-layout-mylanguage” or whatever * Create a branch: Name it “keyboard-layout-mylanguage” or whatever
* Checkout your branch, edit your keyboard layout and commit your changes * Checkout your branch, edit your keyboard layout and commit your changes
* Your layout **must** be correctly named, and in `data/keyboards/`.
* Your layout **must** pass the `test_layout` tool with zero problems.
* Your translation **must** be correctly named, and in `data/langs/`.
* Your layout or translation **must** be added to automatic tests. **Dont forget to add it** to `src/resources.rs` and the layout to `tests/meson.build` (thats for me, because I always forget it).
### Get it merged
It's always recommended to **compile and run** squeekboard before submitting your changes. This serves as a test that all is working. See instructions in the [compiling section](#compiling-and-running-squeekboard).
* Push the local changes (to the branch of your fork of squeekboard) * Push the local changes (to the branch of your fork of squeekboard)
* Create a merge request for the branch to get your changes merged to the official squeekboard git repository * Create a merge request for the branch to get your changes merged to the official squeekboard git repository
If your changes pass automated tests (CI), then the merge request will be reviewed by the maintainers, and you might be asked to change a thing or two. **Compile squeekboard**
## Compiling and running Squeekboard * Follow the instructions found in “Building” section of the squeekboards README: Running squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building ](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building)
If you want your change to become part of official Squeekboard, or if you want to add a translation of your layout name, you will have to recompile Squeekboard and test your changes there. **Running squeekboard**
### Compile squeekboard * Follow these instructions to run squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running ](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running)
* Additionally take a look at https://source.puri.sm/Librem5/squeekboard/blob/master/HACKING.md#testing
* Follow the instructions found in “Building” section of the squeekboards README: Running squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building) * You can either test it locally on your Linux system or use the [QEMU Librem 5 image ](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html)
### Run squeekboard
* Follow these instructions to run squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running)
* Additionally take a look at the contribution document for [testing info](HACKING.md#testing)
* You can either test it locally on your Linux system or use the [QEMU Librem 5 image](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html)
* To test squeekboard locally, you need phoc. Either compile that from the sources as well or use the CI repository ci.puri.sm for Debian based systems: * To test squeekboard locally, you need phoc. Either compile that from the sources as well or use the CI repository ci.puri.sm for Debian based systems:
`deb [arch=amd64] http://ci.puri.sm/ scratch librem5` `deb [arch=amd64] http://ci.puri.sm/ scratch librem5`
Squeekboard can be installed from there as a Debian package, too (thats what I often do). But beware - there be dragons! You could bork your system with these packages and you should probably disable this repository again after installing what you need - these packages are not meant for production systems (or so I heard :wink: ) Squeekboard can be installed from there as a Debian package, too (thats what I often do). But beware - there be dragons! You could bork your system with these packages and you should probably disable this repository again after installing what you need - these packages are not meant for production systems (or so I heard :wink: )
**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 `gsettings get org.gnome.desktop.input-sources sources`
The output should be something like this: `[('xkb', 'us'), ('xkb', 'de')]`
So f.ex. “de.yaml” would be the correct name for the German keyboard layout.
* The translations for the keyboard layout names in the different languages can be found at `data/langs/`
* Dont forget to add your newly created layout or translation to `src/resources.rs` and the layout to `tests/meson.build` (thats for me, because I always forget it)
**Testing the layout**
* Copy your yaml file to `~/.local/share/squeekboard/keyboards/` for testing purposes. From there it should get picked up by squeekboard
* To test the translations in `data/langs/` , you have to compile squeekboard

View File

@ -38,6 +38,8 @@ struct _EekElementClass
GObjectClass parent_class; GObjectClass parent_class;
}; };
GType eek_element_get_type (void) G_GNUC_CONST;
void eek_element_set_name (EekElement *element, void eek_element_set_name (EekElement *element,
const gchar *name); const gchar *name);

View File

@ -25,6 +25,10 @@
#include "config.h" #include "config.h"
#ifdef HAVE_LIBCANBERRA
#include <canberra-gtk.h>
#endif
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
@ -37,22 +41,25 @@
#include "src/layout.h" #include "src/layout.h"
#include "src/submission.h" #include "src/submission.h"
#define LIBFEEDBACK_USE_UNSTABLE_API enum {
#include <libfeedback.h> PROP_0,
PROP_LAST
};
#define SQUEEKBOARD_APP_ID "sm.puri.squeekboard" /* since 2.91.5 GDK_DRAWABLE was removed and gdk_cairo_create takes
GdkWindow as the argument */
#ifndef GDK_DRAWABLE
#define GDK_DRAWABLE(x) (x)
#endif
typedef struct _EekGtkKeyboardPrivate typedef struct _EekGtkKeyboardPrivate
{ {
EekRenderer *renderer; // owned, nullable EekRenderer *renderer;
EekboardContextService *eekboard_context; // unowned reference EekboardContextService *eekboard_context; // unowned reference
struct submission *submission; // unowned reference struct submission *submission; // unowned reference
LevelKeyboard *keyboard; // unowned reference; it's kept in server-context (FIXME)
struct squeek_layout_state *layout; // unowned
LevelKeyboard *keyboard; // unowned reference; it's kept in server-context
GdkEventSequence *sequence; // unowned reference GdkEventSequence *sequence; // unowned reference
LfbEvent *event;
} EekGtkKeyboardPrivate; } EekGtkKeyboardPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA) G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA)
@ -81,85 +88,43 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
GtkAllocation allocation; GtkAllocation allocation;
gtk_widget_get_allocation (self, &allocation); gtk_widget_get_allocation (self, &allocation);
if (!priv->keyboard) {
return FALSE;
}
if (!priv->renderer) { if (!priv->renderer) {
PangoContext *pcontext = gtk_widget_get_pango_context (self); PangoContext *pcontext = gtk_widget_get_pango_context (self);
priv->renderer = eek_renderer_new ( priv->renderer = eek_renderer_new (priv->keyboard, pcontext);
priv->keyboard,
pcontext);
eek_renderer_set_allocation_size (priv->renderer, eek_renderer_set_allocation_size (priv->renderer,
priv->keyboard->layout,
allocation.width, allocation.width,
allocation.height); allocation.height);
eek_renderer_set_scale_factor (priv->renderer, eek_renderer_set_scale_factor (priv->renderer,
gtk_widget_get_scale_factor (self)); gtk_widget_get_scale_factor (self));
} }
eek_renderer_render_keyboard (priv->renderer, priv->submission, cr, priv->keyboard); eek_renderer_render_keyboard (priv->renderer, cr);
return FALSE; return FALSE;
} }
// Units of virtual pixels size
static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 540) {
return ARRANGEMENT_KIND_BASE;
}
return ARRANGEMENT_KIND_WIDE;
}
static void static void
eek_gtk_keyboard_real_size_allocate (GtkWidget *self, eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
GtkAllocation *allocation) GtkAllocation *allocation)
{ {
EekGtkKeyboardPrivate *priv = EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self)); eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
// check if the change would switch types
enum squeek_arrangement_kind new_type = get_type(
(uint32_t)(allocation->width - allocation->x),
(uint32_t)(allocation->height - allocation->y));
if (priv->layout->arrangement != new_type) {
priv->layout->arrangement = new_type;
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(priv->eekboard_context, priv->layout, time);
}
if (priv->renderer) { if (priv->renderer)
eek_renderer_set_allocation_size (priv->renderer, eek_renderer_set_allocation_size (priv->renderer,
priv->keyboard->layout,
allocation->width, allocation->width,
allocation->height); allocation->height);
}
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)-> GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->
size_allocate (self, allocation); size_allocate (self, allocation);
} }
static void
on_event_triggered (LfbEvent *event,
GAsyncResult *res,
gpointer unused)
{
g_autoptr (GError) err = NULL;
if (!lfb_event_trigger_feedback_finish (event, res, &err)) {
g_warning ("Failed to trigger feedback for '%s': %s",
lfb_event_get_event (event), err->message);
}
}
static void depress(EekGtkKeyboard *self, static void depress(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time) gdouble x, gdouble y, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (!priv->keyboard) {
return;
}
squeek_layout_depress(priv->keyboard->layout, squeek_layout_depress(priv->keyboard->layout,
priv->submission, priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, self); x, y, eek_renderer_get_transformation(priv->renderer), time, self);
@ -169,10 +134,7 @@ static void drag(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time) gdouble x, gdouble y, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (!priv->keyboard) { squeek_layout_drag(priv->keyboard->layout,
return;
}
squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission, priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, x, y, eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, self); priv->eekboard_context, self);
@ -181,10 +143,8 @@ static void drag(EekGtkKeyboard *self,
static void release(EekGtkKeyboard *self, guint32 time) static void release(EekGtkKeyboard *self, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (!priv->keyboard) {
return; squeek_layout_release(priv->keyboard->layout,
}
squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission, priv->submission,
eek_renderer_get_transformation(priv->renderer), time, eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, self); priv->eekboard_context, self);
@ -304,7 +264,7 @@ eek_gtk_keyboard_dispose (GObject *object)
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (priv->renderer) { if (priv->renderer) {
eek_renderer_free(priv->renderer); g_object_unref (priv->renderer);
priv->renderer = NULL; priv->renderer = NULL;
priv->renderer = NULL; priv->renderer = NULL;
} }
@ -317,11 +277,6 @@ eek_gtk_keyboard_dispose (GObject *object)
priv->keyboard = NULL; priv->keyboard = NULL;
} }
if (priv->event) {
g_clear_object (&priv->event);
lfb_uninit ();
}
G_OBJECT_CLASS (eek_gtk_keyboard_parent_class)->dispose (object); G_OBJECT_CLASS (eek_gtk_keyboard_parent_class)->dispose (object);
} }
@ -353,81 +308,29 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
static void static void
eek_gtk_keyboard_init (EekGtkKeyboard *self) eek_gtk_keyboard_init (EekGtkKeyboard *self)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self)); (void)self;
g_autoptr(GError) err = NULL;
if (lfb_init(SQUEEKBOARD_APP_ID, &err)) {
priv->event = lfb_event_new ("button-pressed");
} else {
g_warning ("Failed to init libfeedback: %s", err->message);
}
GtkIconTheme *theme = gtk_icon_theme_get_default ();
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
}
static void
on_notify_keyboard (GObject *object,
GParamSpec *spec,
EekGtkKeyboard *self) {
(void)spec;
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (self);
priv->keyboard = eekboard_context_service_get_keyboard(EEKBOARD_CONTEXT_SERVICE(object));
if (priv->renderer) {
eek_renderer_free(priv->renderer);
}
priv->renderer = NULL;
gtk_widget_queue_draw(GTK_WIDGET(self));
} }
/** /**
* eek_gtk_keyboard_new:
* @keyboard: an #EekKeyboard
*
* Create a new #GtkWidget displaying @keyboard. * Create a new #GtkWidget displaying @keyboard.
* Returns: a #GtkWidget * Returns: a #GtkWidget
*/ */
GtkWidget * GtkWidget *
eek_gtk_keyboard_new (EekboardContextService *eekservice, eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice,
struct submission *submission, struct submission *submission)
struct squeek_layout_state *layout)
{ {
EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL)); EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL));
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret); EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret);
priv->keyboard = keyboard;
priv->eekboard_context = eekservice; priv->eekboard_context = eekservice;
priv->submission = submission; priv->submission = submission;
priv->layout = layout;
priv->renderer = NULL;
g_signal_connect (eekservice,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ret);
on_notify_keyboard(G_OBJECT(eekservice), NULL, ret);
/* TODO: this is how a compound keyboard
* made out of a layout and a suggestion bar could start.
* GtkBox *box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
GtkEntry *fill = GTK_ENTRY(gtk_entry_new());
gtk_box_pack_start(box, GTK_WIDGET(fill), FALSE, FALSE, 0);
gtk_box_pack_start(box, GTK_WIDGET(ret), TRUE, TRUE, 0);
return GTK_WIDGET(box);*/
return GTK_WIDGET(ret); return GTK_WIDGET(ret);
} }
/** EekRenderer *eek_gtk_keyboard_get_renderer(EekGtkKeyboard *self) {
* eek_gtk_keyboard_emit_feedback: EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
* return priv->renderer;
* Emit button press haptic feedback via libfeedack.
*/
void
eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self)
{
EekGtkKeyboardPrivate *priv;
g_return_if_fail (EEK_IS_GTK_KEYBOARD (self));
priv = eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
if (priv->event) {
lfb_event_trigger_feedback_async (priv->event,
NULL,
(GAsyncReadyCallback)on_event_triggered,
NULL);
}
} }

View File

@ -31,7 +31,7 @@
#include "eek/eek-types.h" #include "eek/eek-types.h"
struct submission; struct submission;
struct squeek_layout_state; typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs
G_BEGIN_DECLS G_BEGIN_DECLS
#define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type()) #define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type())
@ -47,8 +47,8 @@ struct _EekGtkKeyboardClass
gpointer pdummy[24]; gpointer pdummy[24];
}; };
GtkWidget *eek_gtk_keyboard_new (EekboardContextService *eekservice, struct submission *submission, struct squeek_layout_state *layout); GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST;
void eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self); GtkWidget *eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice, struct submission *submission);
G_END_DECLS G_END_DECLS
#endif /* EEK_GTK_KEYBOARD_H */ #endif /* EEK_GTK_KEYBOARD_H */

View File

@ -21,7 +21,6 @@
#include "config.h" #include "config.h"
#define _XOPEN_SOURCE 500 #define _XOPEN_SOURCE 500
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -31,31 +30,47 @@
#include "eek-keyboard.h" #include "eek-keyboard.h"
/// External linkage for Rust. void level_keyboard_free(LevelKeyboard *self) {
/// The corresponding deinit is implemented in vkeyboard::KeyMap::drop xkb_keymap_unref(self->keymap);
struct keymap squeek_key_map_from_str(const char *keymap_str) { close(self->keymap_fd);
squeek_layout_free(self->layout);
g_free(self);
}
LevelKeyboard*
level_keyboard_new (const gchar *keyboard_type,
enum squeek_arrangement_kind t)
{
struct squeek_layout *layout = squeek_load_layout(keyboard_type, t);
LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
keyboard->layout = layout;
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) { if (!context) {
g_error("No context created"); g_error("No context created");
} }
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str, struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) { if (!keymap)
g_error("Bad keymap:\n%s", keymap_str); g_error("Bad keymap:\n%s", keymap_str);
}
xkb_context_unref(context); xkb_context_unref(context);
keyboard->keymap = keymap;
char *xkb_keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
size_t keymap_len = strlen(xkb_keymap_str) + 1; keyboard->keymap_len = strlen(keymap_str) + 1;
g_autofree char *path = strdup("/eek_keymap-XXXXXX"); g_autofree char *path = strdup("/eek_keymap-XXXXXX");
char *r = &path[strlen(path) - 6]; char *r = &path[strlen(path) - 6];
if (getrandom(r, 6, GRND_NONBLOCK) < 0) { getrandom(r, 6, GRND_NONBLOCK);
g_error("Failed to get random numbers: %s", strerror(errno));
}
for (unsigned i = 0; i < 6; i++) { for (unsigned i = 0; i < 6; i++) {
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good... r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
@ -64,39 +79,17 @@ struct keymap squeek_key_map_from_str(const char *keymap_str) {
if (keymap_fd < 0) { if (keymap_fd < 0) {
g_error("Failed to set up keymap fd"); g_error("Failed to set up keymap fd");
} }
keyboard->keymap_fd = keymap_fd;
shm_unlink(path); shm_unlink(path);
if (ftruncate(keymap_fd, (off_t)keymap_len)) { if (ftruncate(keymap_fd, (off_t)keyboard->keymap_len)) {
g_error("Failed to increase keymap fd size"); g_error("Failed to increase keymap fd size");
} }
char *ptr = mmap(NULL, keymap_len, PROT_WRITE, MAP_SHARED, char *ptr = mmap(NULL, keyboard->keymap_len, PROT_WRITE, MAP_SHARED,
keymap_fd, 0); keymap_fd, 0);
if ((void*)ptr == (void*)-1) { if ((void*)ptr == (void*)-1) {
g_error("Failed to set up mmap"); g_error("Failed to set up mmap");
} }
strncpy(ptr, xkb_keymap_str, keymap_len); strncpy(ptr, keymap_str, keyboard->keymap_len);
munmap(ptr, keymap_len); munmap(ptr, keyboard->keymap_len);
free(xkb_keymap_str);
xkb_keymap_unref(keymap);
struct keymap km = {
.fd = keymap_fd,
.fd_len = keymap_len,
};
return km;
}
void level_keyboard_free(LevelKeyboard *self) {
squeek_layout_free(self->layout);
g_free(self);
}
LevelKeyboard*
level_keyboard_new (struct squeek_layout *layout)
{
LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
keyboard->layout = layout;
return keyboard; return keyboard;
} }

View File

@ -1,17 +1,17 @@
/* /*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org> * Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc. * Copyright (C) 2010-2011 Red Hat, Inc.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License * modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of * as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version. * the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, but * This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of * WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
@ -32,23 +32,23 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/// Keymap container for Rust interoperability.
struct keymap {
uint32_t fd; // keymap formatted as XKB string
size_t fd_len; // length of the data inside keymap_fd
};
/// Keyboard state holder /// Keyboard state holder
struct _LevelKeyboard { struct _LevelKeyboard {
struct squeek_layout *layout; // owned struct squeek_layout *layout; // owned
// FIXME: This no longer needs to exist, keymap was folded into layout. struct xkb_keymap *keymap; // owned
int keymap_fd; // keymap formatted as XKB string
size_t keymap_len; // length of the data inside keymap_fd
guint id; // as a key to layout choices
}; };
typedef struct _LevelKeyboard LevelKeyboard; typedef struct _LevelKeyboard LevelKeyboard;
gchar *eek_keyboard_get_keymap(LevelKeyboard *keyboard); gchar * eek_keyboard_get_keymap
(LevelKeyboard *keyboard);
LevelKeyboard* LevelKeyboard*
level_keyboard_new (struct squeek_layout *layout); level_keyboard_new (const gchar *keyboard_type,
enum squeek_arrangement_kind t);
void level_keyboard_free(LevelKeyboard *self); void level_keyboard_free(LevelKeyboard *self);
G_END_DECLS G_END_DECLS

View File

@ -28,6 +28,27 @@
#include "eek-renderer.h" #include "eek-renderer.h"
#include "src/style.h" #include "src/style.h"
enum {
PROP_0,
PROP_PCONTEXT,
PROP_LAST
};
typedef struct _EekRendererPrivate
{
LevelKeyboard *keyboard; // unowned
PangoContext *pcontext; // owned
GtkCssProvider *css_provider; // owned
GtkStyleContext *view_context; // owned
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
gdouble allocation_width;
gdouble allocation_height;
gint scale_factor; /* the outputs scale factor */
struct transformation widget_to_layout;
} EekRendererPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
/* eek-keyboard-drawing.c */ /* eek-keyboard-drawing.c */
static void render_button_label (cairo_t *cr, GtkStyleContext *ctx, static void render_button_label (cairo_t *cr, GtkStyleContext *ctx,
@ -117,7 +138,9 @@ eek_render_button (EekRenderer *self,
gboolean pressed, gboolean pressed,
gboolean locked) gboolean locked)
{ {
GtkStyleContext *ctx = self->button_context; EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
GtkStyleContext *ctx = priv->button_context;
/* Set the name of the button on the widget path, using the name obtained /* Set the name of the button on the widget path, using the name obtained
from the button's symbol. */ from the button's symbol. */
g_autoptr (GtkWidgetPath) path = NULL; g_autoptr (GtkWidgetPath) path = NULL;
@ -137,7 +160,7 @@ eek_render_button (EekRenderer *self,
} }
gtk_style_context_add_class(ctx, outline_name); gtk_style_context_add_class(ctx, outline_name);
render_button_in_context(self->scale_factor, cr, ctx, button); render_button_in_context(priv->scale_factor, cr, ctx, button);
// Save and restore functions don't work if gtk_render_* was used in between // Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL); gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
@ -191,46 +214,118 @@ render_button_label (cairo_t *cr,
g_object_unref (layout); g_object_unref (layout);
} }
// FIXME: Pass just the active modifiers instead of entire submission
void void
eek_renderer_render_keyboard (EekRenderer *self, eek_renderer_render_keyboard (EekRenderer *self,
struct submission *submission, cairo_t *cr)
cairo_t *cr,
LevelKeyboard *keyboard)
{ {
g_return_if_fail (self->allocation_width > 0.0); EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
g_return_if_fail (self->allocation_height > 0.0);
g_return_if_fail (priv->keyboard);
g_return_if_fail (priv->allocation_width > 0.0);
g_return_if_fail (priv->allocation_height > 0.0);
/* Paint the background covering the entire widget area */ /* Paint the background covering the entire widget area */
gtk_render_background (self->view_context, gtk_render_background (priv->view_context,
cr, cr,
0, 0, 0, 0,
self->allocation_width, self->allocation_height); priv->allocation_width, priv->allocation_height);
cairo_save(cr); cairo_save(cr);
cairo_translate (cr, self->widget_to_layout.origin_x, self->widget_to_layout.origin_y); cairo_translate (cr, priv->widget_to_layout.origin_x, priv->widget_to_layout.origin_y);
cairo_scale (cr, self->widget_to_layout.scale, self->widget_to_layout.scale); cairo_scale (cr, priv->widget_to_layout.scale, priv->widget_to_layout.scale);
squeek_draw_layout_base_view(keyboard->layout, self, cr); squeek_draw_layout_base_view(priv->keyboard->layout, self, cr);
squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission); squeek_layout_draw_all_changed(priv->keyboard->layout, self, cr);
cairo_restore (cr); cairo_restore (cr);
} }
void static void
eek_renderer_free (EekRenderer *self) eek_renderer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{ {
if (self->pcontext) { EekRendererPrivate *priv = eek_renderer_get_instance_private (
g_object_unref (self->pcontext); EEK_RENDERER(object));
self->pcontext = NULL;
switch (prop_id) {
case PROP_PCONTEXT:
priv->pcontext = g_value_get_object (value);
g_object_ref (priv->pcontext);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
} }
g_object_unref(self->css_provider); }
g_object_unref(self->view_context);
g_object_unref(self->button_context); static void
eek_renderer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
(void)value;
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
eek_renderer_dispose (GObject *object)
{
EekRenderer *self = EEK_RENDERER (object);
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
if (priv->keyboard) {
priv->keyboard = NULL;
}
if (priv->pcontext) {
g_object_unref (priv->pcontext);
priv->pcontext = NULL;
}
// this is where renderer-specific surfaces would be released // this is where renderer-specific surfaces would be released
free(self); G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object);
} }
static void
eek_renderer_finalize (GObject *object)
{
EekRenderer *self = EEK_RENDERER(object);
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
g_object_unref(priv->css_provider);
g_object_unref(priv->view_context);
g_object_unref(priv->button_context);
G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object);
}
static void
eek_renderer_class_init (EekRendererClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = eek_renderer_set_property;
gobject_class->get_property = eek_renderer_get_property;
gobject_class->dispose = eek_renderer_dispose;
gobject_class->finalize = eek_renderer_finalize;
pspec = g_param_spec_object ("pango-context",
"Pango Context",
"Pango Context",
PANGO_TYPE_CONTEXT,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_PCONTEXT,
pspec);
}
static GType new_type(char *name) { static GType new_type(char *name) {
GTypeInfo info = {0}; GTypeInfo info = {0};
info.class_size = sizeof(GtkWidgetClass); info.class_size = sizeof(GtkWidgetClass);
@ -241,7 +336,7 @@ static GType new_type(char *name) {
); );
} }
static GType view_type(void) { static GType view_type() {
static GType type = 0; static GType type = 0;
if (!type) { if (!type) {
type = new_type("sq_view"); type = new_type("sq_view");
@ -249,7 +344,7 @@ static GType view_type(void) {
return type; return type;
} }
static GType button_type(void) { static GType button_type() {
static GType type = 0; static GType type = 0;
if (!type) { if (!type) {
type = new_type("sq_button"); type = new_type("sq_button");
@ -258,71 +353,81 @@ static GType button_type(void) {
} }
static void static void
renderer_init (EekRenderer *self) eek_renderer_init (EekRenderer *self)
{ {
self->pcontext = NULL; EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
self->allocation_width = 0.0;
self->allocation_height = 0.0;
self->scale_factor = 1;
self->css_provider = squeek_load_style(); priv->keyboard = NULL;
priv->pcontext = NULL;
priv->allocation_width = 0.0;
priv->allocation_height = 0.0;
priv->scale_factor = 1;
GtkIconTheme *theme = gtk_icon_theme_get_default ();
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
priv->css_provider = squeek_load_style();
} }
EekRenderer * EekRenderer *
eek_renderer_new (LevelKeyboard *keyboard, eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext) PangoContext *pcontext)
{ {
EekRenderer *renderer = calloc(1, sizeof(EekRenderer)); EekRenderer *renderer = g_object_new (EEK_TYPE_RENDERER,
renderer_init(renderer); "pango-context", pcontext,
renderer->pcontext = pcontext; NULL);
g_object_ref (renderer->pcontext); EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->keyboard = keyboard;
/* Create a style context for the layout */ /* Create a style context for the layout */
GtkWidgetPath *path = gtk_widget_path_new(); GtkWidgetPath *path = gtk_widget_path_new();
gtk_widget_path_append_type(path, view_type()); gtk_widget_path_append_type(path, view_type());
renderer->view_context = gtk_style_context_new(); priv->view_context = gtk_style_context_new();
gtk_style_context_set_path(renderer->view_context, path); gtk_style_context_set_path(priv->view_context, path);
gtk_widget_path_unref(path); gtk_widget_path_unref(path);
if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) { if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_style_context_add_class(renderer->view_context, "wide"); gtk_style_context_add_class(priv->view_context, "wide");
} }
gtk_style_context_add_provider (renderer->view_context, gtk_style_context_add_provider (priv->view_context,
GTK_STYLE_PROVIDER(renderer->css_provider), GTK_STYLE_PROVIDER(priv->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER); GTK_STYLE_PROVIDER_PRIORITY_USER);
/* Create a style context for the buttons */ /* Create a style context for the buttons */
path = gtk_widget_path_new(); path = gtk_widget_path_new();
gtk_widget_path_append_type(path, view_type()); gtk_widget_path_append_type(path, view_type());
if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) { if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_widget_path_iter_add_class(path, -1, "wide"); gtk_widget_path_iter_add_class(path, -1, "wide");
} }
gtk_widget_path_append_type(path, button_type()); gtk_widget_path_append_type(path, button_type());
renderer->button_context = gtk_style_context_new (); priv->button_context = gtk_style_context_new ();
gtk_style_context_set_path(renderer->button_context, path); gtk_style_context_set_path(priv->button_context, path);
gtk_widget_path_unref(path); gtk_widget_path_unref(path);
gtk_style_context_set_parent(renderer->button_context, renderer->view_context); gtk_style_context_set_parent(priv->button_context, priv->view_context);
gtk_style_context_set_state (renderer->button_context, GTK_STATE_FLAG_NORMAL); gtk_style_context_set_state (priv->button_context, GTK_STATE_FLAG_NORMAL);
gtk_style_context_add_provider (renderer->button_context, gtk_style_context_add_provider (priv->button_context,
GTK_STYLE_PROVIDER(renderer->css_provider), GTK_STYLE_PROVIDER(priv->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER); GTK_STYLE_PROVIDER_PRIORITY_USER);
return renderer; return renderer;
} }
void void
eek_renderer_set_allocation_size (EekRenderer *renderer, eek_renderer_set_allocation_size (EekRenderer *renderer,
struct squeek_layout *layout,
gdouble width, gdouble width,
gdouble height) gdouble height)
{ {
g_return_if_fail (EEK_IS_RENDERER(renderer));
g_return_if_fail (width > 0.0 && height > 0.0); g_return_if_fail (width > 0.0 && height > 0.0);
renderer->allocation_width = width; EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
renderer->allocation_height = height;
renderer->widget_to_layout = squeek_layout_calculate_transformation( priv->allocation_width = width;
layout, priv->allocation_height = height;
renderer->allocation_width, renderer->allocation_height);
priv->widget_to_layout = squeek_layout_calculate_transformation(
priv->keyboard->layout,
priv->allocation_width, priv->allocation_height);
// This is where size-dependent surfaces would be released // This is where size-dependent surfaces would be released
} }
@ -330,7 +435,10 @@ eek_renderer_set_allocation_size (EekRenderer *renderer,
void void
eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale) eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale)
{ {
renderer->scale_factor = scale; g_return_if_fail (EEK_IS_RENDERER(renderer));
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->scale_factor = scale;
} }
cairo_surface_t * cairo_surface_t *
@ -359,5 +467,9 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
struct transformation struct transformation
eek_renderer_get_transformation (EekRenderer *renderer) { eek_renderer_get_transformation (EekRenderer *renderer) {
return renderer->widget_to_layout; struct transformation failed = {0};
g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
return priv->widget_to_layout;
} }

View File

@ -25,36 +25,31 @@
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include "eek-types.h" #include "eek-types.h"
#include "src/submission.h"
struct squeek_layout; G_BEGIN_DECLS
/// Renders LevelKayboards #define EEK_TYPE_RENDERER (eek_renderer_get_type())
/// It cannot adjust styles at runtime. G_DECLARE_DERIVABLE_TYPE (EekRenderer, eek_renderer, EEK, RENDERER, GObject)
typedef struct EekRenderer
struct _EekRendererClass
{ {
PangoContext *pcontext; // owned GObjectClass parent_class;
GtkCssProvider *css_provider; // owned
GtkStyleContext *view_context; // owned
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
/// Style class for rendering the view and button CSS.
gchar *extra_style; // owned
// Mutable state cairo_surface_t *(* get_icon_surface) (EekRenderer *self,
/// Background extents const gchar *icon_name,
gdouble allocation_width; gint size,
gdouble allocation_height; gint scale);
gint scale_factor; /* the outputs scale factor */
/// Coords transformation
struct transformation widget_to_layout;
} EekRenderer;
/*< private >*/
/* padding */
gpointer pdummy[23];
};
GType eek_renderer_get_type (void) G_GNUC_CONST; GType eek_renderer_get_type (void) G_GNUC_CONST;
EekRenderer *eek_renderer_new (LevelKeyboard *keyboard, EekRenderer *eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext); PangoContext *pcontext);
void eek_renderer_set_allocation_size void eek_renderer_set_allocation_size
(EekRenderer *renderer, struct squeek_layout *layout, (EekRenderer *renderer,
gdouble width, gdouble width,
gdouble height); gdouble height);
void eek_renderer_set_scale_factor (EekRenderer *renderer, void eek_renderer_set_scale_factor (EekRenderer *renderer,
@ -64,10 +59,8 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name,
gint size, gint size,
gint scale); gint scale);
void eek_renderer_render_keyboard (EekRenderer *renderer, struct submission *submission, void eek_renderer_render_keyboard (EekRenderer *renderer,
cairo_t *cr, LevelKeyboard *keyboard); cairo_t *cr);
void
eek_renderer_free (EekRenderer *self);
struct transformation struct transformation
eek_renderer_get_transformation (EekRenderer *renderer); eek_renderer_get_transformation (EekRenderer *renderer);

View File

@ -590,32 +590,27 @@ phosh_layer_surface_set_size(PhoshLayerSurface *self, gint width, gint height)
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); 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->height == height && priv->width == width) { if (priv->height == height && priv->width == width)
return; return;
}
old_width = priv->width; old_width = priv->width;
old_height = priv->height; old_height = priv->height;
if (width != -1) { if (width != -1)
priv->width = width; priv->width = width;
}
if (height != -1) { if (height != -1)
priv->height = height; priv->height = height;
}
if (gtk_widget_get_mapped (GTK_WIDGET (self))) { if (gtk_widget_get_mapped (GTK_WIDGET (self))) {
zwlr_layer_surface_v1_set_size(priv->layer_surface, priv->width, priv->height); zwlr_layer_surface_v1_set_size(priv->layer_surface, priv->width, priv->height);
} }
if (priv->height != old_height) { if (priv->height != old_height)
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT]);
}
if (priv->width != old_width) { if (priv->width != old_width)
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH]);
}
} }
/** /**
@ -637,31 +632,25 @@ phosh_layer_surface_set_margins(PhoshLayerSurface *self, gint top, gint right, g
old_right = priv->margin_right; old_right = priv->margin_right;
old_bottom = priv->margin_bottom; old_bottom = priv->margin_bottom;
if (old_top == top && old_left == left && old_right == right && old_bottom == bottom) { if (old_top == top && old_left == left && old_right == right && old_bottom == bottom)
return; return;
}
priv->margin_top = top; priv->margin_top = top;
priv->margin_left = left; priv->margin_left = left;
priv->margin_right = right; priv->margin_right = right;
priv->margin_bottom = bottom; priv->margin_bottom = bottom;
if (priv->layer_surface) { if (priv->layer_surface)
zwlr_layer_surface_v1_set_margin(priv->layer_surface, top, right, bottom, left); zwlr_layer_surface_v1_set_margin(priv->layer_surface, top, right, bottom, left);
}
if (old_top != top) { if (old_top != top)
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP]);
} if (old_bottom != bottom)
if (old_bottom != bottom) {
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM]);
} if (old_left != left)
if (old_left != left) {
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT]);
} if (old_right != right)
if (old_right != right) {
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT]);
}
} }
/** /**
@ -680,15 +669,13 @@ phosh_layer_surface_set_exclusive_zone(PhoshLayerSurface *self, gint zone)
old_zone = priv->exclusive_zone; old_zone = priv->exclusive_zone;
if (old_zone == zone) { if (old_zone == zone)
return; return;
}
priv->exclusive_zone = zone; priv->exclusive_zone = zone;
if (priv->layer_surface) { if (priv->layer_surface)
zwlr_layer_surface_v1_set_exclusive_zone(priv->layer_surface, zone); zwlr_layer_surface_v1_set_exclusive_zone(priv->layer_surface, zone);
}
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE]);
} }
@ -706,14 +693,13 @@ phosh_layer_surface_set_kbd_interactivity (PhoshLayerSurface *self, gboolean int
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); 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->kbd_interactivity == interactivity) { if (priv->kbd_interactivity == interactivity)
return; return;
}
priv->kbd_interactivity = interactivity; priv->kbd_interactivity = interactivity;
if (priv->layer_surface) { if (priv->layer_surface)
zwlr_layer_surface_v1_set_keyboard_interactivity (priv->layer_surface, interactivity); zwlr_layer_surface_v1_set_keyboard_interactivity (priv->layer_surface, interactivity);
}
g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY]); g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY]);
} }
@ -731,7 +717,6 @@ phosh_layer_surface_wl_surface_commit (PhoshLayerSurface *self)
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); 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->wl_surface) { if (priv->wl_surface)
wl_surface_commit (priv->wl_surface); wl_surface_commit (priv->wl_surface);
}
} }

View File

@ -42,23 +42,18 @@ enum {
static guint signals[LAST_SIGNAL] = { 0, }; static guint signals[LAST_SIGNAL] = { 0, };
/** #define EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(obj) \
* EekboardContextService: (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServicePrivate))
*
* Handles layout state, gsettings, and virtual-keyboard.
*
* TODO: Restrict to managing keyboard layouts, and maybe button repeats,
* and the virtual keyboard protocol.
*
* The #EekboardContextService structure contains only private data
* and should only be accessed using the provided API.
*/
struct _EekboardContextService {
GObject parent;
struct squeek_layout_state *layout; // Unowned
struct _EekboardContextServicePrivate {
LevelKeyboard *keyboard; // currently used keyboard LevelKeyboard *keyboard; // currently used keyboard
GHashTable *keyboard_hash; // a table of available keyboards, per layout
char *overlay;
GSettings *settings; // Owned reference GSettings *settings; // Owned reference
uint32_t hint;
uint32_t purpose;
// Maybe TODO: it's used only for fetching layout type. // Maybe TODO: it's used only for fetching layout type.
// Maybe let UI push the type to this structure? // Maybe let UI push the type to this structure?
@ -67,7 +62,7 @@ struct _EekboardContextService {
struct submission *submission; // unowned struct submission *submission; // unowned
}; };
G_DEFINE_TYPE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT);
static void static void
eekboard_context_service_set_property (GObject *object, eekboard_context_service_set_property (GObject *object,
@ -93,7 +88,7 @@ eekboard_context_service_get_property (GObject *object,
switch (prop_id) { switch (prop_id) {
case PROP_KEYBOARD: case PROP_KEYBOARD:
g_value_set_object (value, context->keyboard); g_value_set_object (value, context->priv->keyboard);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -104,6 +99,13 @@ eekboard_context_service_get_property (GObject *object,
static void static void
eekboard_context_service_dispose (GObject *object) eekboard_context_service_dispose (GObject *object)
{ {
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
if (context->priv->keyboard_hash) {
g_hash_table_destroy (context->priv->keyboard_hash);
context->priv->keyboard_hash = NULL;
}
G_OBJECT_CLASS (eekboard_context_service_parent_class)-> G_OBJECT_CLASS (eekboard_context_service_parent_class)->
dispose (object); dispose (object);
} }
@ -111,58 +113,52 @@ eekboard_context_service_dispose (GObject *object)
static void static void
settings_get_layout(GSettings *settings, char **type, char **layout) settings_get_layout(GSettings *settings, char **type, char **layout)
{ {
if (!settings) {
return;
}
GVariant *inputs = g_settings_get_value(settings, "sources"); GVariant *inputs = g_settings_get_value(settings, "sources");
if (g_variant_n_children(inputs) == 0) { // current layout is always first
g_warning("No system layout present"); g_variant_get_child(inputs, 0, "(ss)", type, layout);
*type = NULL;
*layout = NULL;
} else {
// current layout is always first
g_variant_get_child(inputs, 0, "(ss)", type, layout);
}
g_variant_unref(inputs);
} }
void void
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state, uint32_t timestamp) { eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t)
gchar *layout_name = state->overlay_name; {
g_autofree gchar *keyboard_layout = NULL;
if (context->priv->overlay) {
keyboard_layout = g_strdup(context->priv->overlay);
} else {
g_autofree gchar *keyboard_type = NULL;
settings_get_layout(context->priv->settings,
&keyboard_type, &keyboard_layout);
}
if (layout_name == NULL) { if (!keyboard_layout) {
layout_name = state->layout_name; keyboard_layout = g_strdup("us");
}
switch (state->purpose) { EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
layout_name = "number";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
layout_name = "terminal";
break;
default:
;
}
if (layout_name == NULL) { switch (priv->purpose) {
layout_name = "us"; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
} case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
keyboard_layout = g_strdup("number");
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
keyboard_layout = g_strdup("terminal");
break;
default:
;
} }
// generic part follows // generic part follows
struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement); LevelKeyboard *keyboard = level_keyboard_new(keyboard_layout, t);
LevelKeyboard *keyboard = level_keyboard_new(layout);
// set as current // set as current
LevelKeyboard *previous_keyboard = context->keyboard; LevelKeyboard *previous_keyboard = context->priv->keyboard;
context->keyboard = keyboard; context->priv->keyboard = keyboard;
// Update the keymap if necessary. // Update the keymap if necessary.
// TODO: Update submission on change event // TODO: Update submission on change event
if (context->submission) { if (context->priv->submission) {
submission_use_layout(context->submission, keyboard->layout, timestamp); submission_set_keyboard(context->priv->submission, keyboard);
} }
// Update UI
g_object_notify (G_OBJECT(context), "keyboard"); g_object_notify (G_OBJECT(context), "keyboard");
// replacing the keyboard above will cause the previous keyboard to get destroyed from the UI side (eek_gtk_keyboard_dispose) // replacing the keyboard above will cause the previous keyboard to get destroyed from the UI side (eek_gtk_keyboard_dispose)
@ -171,23 +167,13 @@ eekboard_context_service_use_layout(EekboardContextService *context, struct sque
} }
} }
static void eekboard_context_service_update_settings_layout(EekboardContextService *context) { static void update_layout_and_type(EekboardContextService *context) {
g_autofree gchar *keyboard_layout = NULL; EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
g_autofree gchar *keyboard_type = NULL; enum squeek_arrangement_kind layout_kind = ARRANGEMENT_KIND_BASE;
settings_get_layout(context->settings, if (priv->ui) {
&keyboard_type, &keyboard_layout); layout_kind = server_context_service_get_layout_type(priv->ui);
if (g_strcmp0(context->layout->layout_name, keyboard_layout) != 0 || context->layout->overlay_name) {
g_free(context->layout->overlay_name);
context->layout->overlay_name = NULL;
if (keyboard_layout) {
g_free(context->layout->layout_name);
context->layout->layout_name = g_strdup(keyboard_layout);
}
// This must actually update the UI.
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
} }
eekboard_context_service_update_layout(context, layout_kind);
} }
static gboolean static gboolean
@ -198,14 +184,17 @@ settings_handle_layout_changed(GSettings *s,
(void)keys; (void)keys;
(void)n_keys; (void)n_keys;
EekboardContextService *context = user_data; EekboardContextService *context = user_data;
eekboard_context_service_update_settings_layout(context); g_free(context->priv->overlay);
context->priv->overlay = NULL;
update_layout_and_type(context);
return TRUE; return TRUE;
} }
static void static void
eekboard_context_service_constructed (GObject *object) eekboard_context_service_constructed (GObject *object)
{ {
(void)object; EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE (object);
update_layout_and_type(context);
} }
static void static void
@ -228,7 +217,7 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
g_signal_new (I_("destroyed"), g_signal_new (I_("destroyed"),
G_TYPE_FROM_CLASS(gobject_class), G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
0, G_STRUCT_OFFSET(EekboardContextServiceClass, destroyed),
NULL, NULL,
NULL, NULL,
g_cclosure_marshal_VOID__VOID, g_cclosure_marshal_VOID__VOID,
@ -252,31 +241,24 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
static void static void
eekboard_context_service_init (EekboardContextService *self) eekboard_context_service_init (EekboardContextService *self)
{ {
const char *schema_name = "org.gnome.desktop.input-sources"; self->priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(self);
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
g_autoptr(GSettingsSchema) schema = NULL;
if (!ssrc) { self->priv->keyboard_hash =
g_warning("No gsettings schemas installed. Layout switching unavailable."); g_hash_table_new_full (g_direct_hash,
return; g_direct_equal,
NULL,
(GDestroyNotify)g_object_unref);
self->priv->settings = g_settings_new ("org.gnome.desktop.input-sources");
gulong conn_id = g_signal_connect(self->priv->settings, "change-event",
G_CALLBACK(settings_handle_layout_changed),
self);
if (conn_id == 0) {
g_warning ("Could not connect to gsettings updates, layout"
" changing unavailable");
} }
schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE); self->priv->overlay = NULL;
if (schema) {
// Not referencing the found schema directly,
// because it's not clear how...
self->settings = g_settings_new (schema_name);
gulong conn_id = g_signal_connect(self->settings, "change-event",
G_CALLBACK(settings_handle_layout_changed),
self);
if (conn_id == 0) {
g_warning ("Could not connect to gsettings updates, "
"automatic layout changing unavailable");
}
} else {
g_warning("Gsettings schema %s is not installed on the system. "
"Layout switching unavailable", schema_name);
}
} }
/** /**
@ -289,6 +271,8 @@ void
eekboard_context_service_destroy (EekboardContextService *context) eekboard_context_service_destroy (EekboardContextService *context)
{ {
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context)); g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
g_free(context->priv->overlay);
g_signal_emit (context, signals[DESTROYED], 0); g_signal_emit (context, signals[DESTROYED], 0);
} }
@ -302,53 +286,44 @@ eekboard_context_service_destroy (EekboardContextService *context)
LevelKeyboard * LevelKeyboard *
eekboard_context_service_get_keyboard (EekboardContextService *context) eekboard_context_service_get_keyboard (EekboardContextService *context)
{ {
return context->keyboard; return context->priv->keyboard;
} }
void eekboard_context_service_set_hint_purpose(EekboardContextService *context, void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint, uint32_t purpose) uint32_t hint, uint32_t purpose)
{ {
if (context->layout->hint != hint || context->layout->purpose != purpose) { EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
context->layout->hint = hint;
context->layout->purpose = purpose; if (priv->hint != hint || priv->purpose != purpose) {
uint32_t time = gdk_event_get_time(NULL); priv->hint = hint;
eekboard_context_service_use_layout(context, context->layout, time); priv->purpose = purpose;
update_layout_and_type(context);
} }
} }
void void
eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) { eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) {
if (g_strcmp0(context->layout->overlay_name, name)) { context->priv->overlay = g_strdup(name);
g_free(context->layout->overlay_name); update_layout_and_type(context);
context->layout->overlay_name = g_strdup(name);
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
}
} }
const char* const char*
eekboard_context_service_get_overlay(EekboardContextService *context) { eekboard_context_service_get_overlay(EekboardContextService *context) {
return context->layout->overlay_name; return context->priv->overlay;
} }
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state) EekboardContextService *eekboard_context_service_new(void)
{ {
EekboardContextService *context = g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL); return g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
context->layout = state;
eekboard_context_service_update_settings_layout(context);
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
return context;
} }
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) { void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
context->submission = submission; context->priv->submission = submission;
if (context->submission) { if (context->priv->submission) {
uint32_t time = gdk_event_get_time(NULL); submission_set_keyboard(context->priv->submission, context->priv->keyboard);
submission_use_layout(context->submission, context->keyboard->layout, time);
} }
} }
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) { void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
context->ui = ui; context->priv->ui = ui;
} }

View File

@ -34,10 +34,58 @@ G_BEGIN_DECLS
#define EEKBOARD_CONTEXT_SERVICE_INTERFACE "org.fedorahosted.Eekboard.Context" #define EEKBOARD_CONTEXT_SERVICE_INTERFACE "org.fedorahosted.Eekboard.Context"
#define EEKBOARD_TYPE_CONTEXT_SERVICE (eekboard_context_service_get_type()) #define EEKBOARD_TYPE_CONTEXT_SERVICE (eekboard_context_service_get_type())
#define EEKBOARD_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextService))
#define EEKBOARD_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
#define EEKBOARD_IS_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE))
#define EEKBOARD_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE))
#define EEKBOARD_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
G_DECLARE_FINAL_TYPE(EekboardContextService, eekboard_context_service, EEKBOARD, CONTEXT_SERVICE, GObject)
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state); typedef struct _EekboardContextServiceClass EekboardContextServiceClass;
typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
/**
* EekboardContextService:
*
* Handles layout state, gsettings, and virtual-keyboard.
*
* TODO: Restrict to managing keyboard layouts, and maybe button repeats,
* and the virtual keyboard protocol.
*
* The #EekboardContextService structure contains only private data
* and should only be accessed using the provided API.
*/
struct _EekboardContextService {
GObject parent;
EekboardContextServicePrivate *priv;
};
/**
* EekboardContextServiceClass:
* @create_keyboard: virtual function for create a keyboard from string
* @enabled: class handler for #EekboardContextService::enabled signal
* @disabled: class handler for #EekboardContextService::disabled signal
*/
struct _EekboardContextServiceClass {
/*< private >*/
GObjectClass parent_class;
/*< public >*/
struct squeek_view *(*create_keyboard) (EekboardContextService *self,
const gchar *keyboard_type);
/* signals */
void (*destroyed) (EekboardContextService *self);
/*< private >*/
/* padding */
gpointer pdummy[24];
};
GType eekboard_context_service_get_type
(void) G_GNUC_CONST;
EekboardContextService *eekboard_context_service_new(void);
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission); void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission);
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui); void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
void eekboard_context_service_destroy (EekboardContextService *context); void eekboard_context_service_destroy (EekboardContextService *context);
@ -50,6 +98,6 @@ void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint, uint32_t hint,
uint32_t purpose); uint32_t purpose);
void void
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *layout, uint32_t timestamp); eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t);
G_END_DECLS G_END_DECLS
#endif /* EEKBOARD_CONTEXT_SERVICE_H */ #endif /* EEKBOARD_CONTEXT_SERVICE_H */

View File

@ -5,7 +5,6 @@ use std::env;
fn main() -> () { fn main() -> () {
check_builtin_layout( check_builtin_layout(
env::args().nth(1).expect("No argument given").as_str(), env::args().nth(1).expect("No argument given").as_str()
env::args().nth(2).map(|s| s == "allow_missing_return").unwrap_or(false),
); );
} }

View File

@ -1,7 +1,7 @@
project( project(
'squeekboard', 'squeekboard',
'c', 'rust', 'c', 'rust',
version: '1.10.0', version: '1.8.0',
license: 'GPLv3', license: 'GPLv3',
meson_version: '>=0.51.0', meson_version: '>=0.51.0',
default_options: [ default_options: [
@ -19,16 +19,6 @@ add_project_arguments(
'-Werror=missing-field-initializers', '-Werror=missing-field-initializers',
'-Werror=incompatible-pointer-types', '-Werror=incompatible-pointer-types',
'-Werror=int-conversion', '-Werror=int-conversion',
'-Werror=redundant-decls',
'-Werror=parentheses',
'-Wformat-nonliteral',
'-Wformat-security',
'-Winit-self',
'-Wmaybe-uninitialized',
'-Wold-style-definition',
'-Wredundant-decls',
'-Wstrict-prototypes',
'-Wunused-function',
], ],
language: 'c' language: 'c'
) )
@ -52,7 +42,6 @@ endif
prefix = get_option('prefix') prefix = get_option('prefix')
bindir = join_paths(prefix, get_option('bindir')) bindir = join_paths(prefix, get_option('bindir'))
datadir = join_paths(prefix, get_option('datadir')) datadir = join_paths(prefix, get_option('datadir'))
desktopdir = join_paths(datadir, 'applications')
pkgdatadir = join_paths(datadir, meson.project_name()) pkgdatadir = join_paths(datadir, meson.project_name())
if get_option('depdatadir') == '' if get_option('depdatadir') == ''
depdatadir = datadir depdatadir = datadir
@ -71,32 +60,6 @@ summary = [
] ]
message('\n'.join(summary)) message('\n'.join(summary))
# Rust deps are changing, depending on compile flags. Cargo can't handle it alone.
cargo_toml_in = files('Cargo.toml.in')
path_data = configuration_data()
path_data.set('path', meson.source_root())
cargo_toml_base = configure_file(
input: 'Cargo.toml.in',
output: 'Cargo.toml.base',
configuration: path_data,
)
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')
endif
cat = find_program('cat')
cargo_toml = custom_target(
'Cargo.toml',
output: 'Cargo.toml',
command: [cat, cargo_toml_base, cargo_deps],
capture: true,
)
dep_cargo = find_program('cargo') dep_cargo = find_program('cargo')
cargo_script = find_program('cargo.sh') cargo_script = find_program('cargo.sh')
cargo_build = find_program('cargo_build.sh') cargo_build = find_program('cargo_build.sh')

View File

@ -6,7 +6,3 @@ option('depdatadir',
option('tests', option('tests',
type: 'boolean', value: true, type: 'boolean', value: true,
description: 'Whether to compile unit tests') description: 'Whether to compile unit tests')
option('legacy',
type: 'boolean', value: false,
description: 'Build with Deban Buster versions of dependencies')

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:admin="http://webns.net/mvcb/">
<name>squeekboard</name>
<shortdesc>A Wayland virtual keyboard</shortdesc>
<description>A virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.</description>
<homepage rdf:resource="https://source.puri.sm/Librem5/squeekboard" />
<bug-database rdf:resource="https://source.puri.sm/Librem5/squeekboard/issues" />
<os>Linux</os>
<license rdf:resource="http://usefulinc.com/doap/licenses/gpl" />
<maintainer>
<foaf:Person>
<foaf:name>Dorota Czaplejewicz</foaf:name>
<foaf:mbox rdf:resource="mailto:dorota.czaplejewicz@puri.sm" />
</foaf:Person>
</maintainer>
</Project>

View File

@ -10,11 +10,8 @@ pub struct KeySym(pub String);
type View = String; type View = String;
/// Use to send modified keypresses /// Use to send modified keypresses
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq)]
pub enum Modifier { pub enum Modifier {
/// Control and Alt are the only modifiers
/// which doesn't interfere with levels,
/// so it's simple to implement as levels are deprecated in squeekboard.
Control, Control,
Alt, Alt,
} }
@ -30,33 +27,14 @@ pub enum Action {
/// When unlocked by pressing it or emitting a key /// When unlocked by pressing it or emitting a key
unlock: View, unlock: View,
}, },
/// Hold this modifier for as long as the button is pressed /// Set this modifier TODO: release?
ApplyModifier(Modifier), SetModifier(Modifier),
/// Submit some text /// Submit some text
Submit { Submit {
/// Text to submit with input-method. /// Text to submit with input-method
/// If None, then keys are to be submitted instead.
text: Option<CString>, text: Option<CString>,
/// The key events this symbol submits when submitting text is not possible /// The key events this symbol submits when submitting text is not possible
keys: Vec<KeySym>, keys: Vec<KeySym>,
}, },
/// Erase a position behind the cursor
Erase,
ShowPreferences, ShowPreferences,
} }
impl Action {
pub fn is_locked(&self, view_name: &str) -> bool {
match self {
Action::LockView { lock, unlock: _ } => lock == view_name,
_ => false,
}
}
pub fn is_active(&self, view_name: &str) -> bool {
match self {
Action::SetView(view) => view == view_name,
Action::LockView { lock, unlock: _ } => lock == view_name,
_ => false,
}
}
}

View File

@ -15,10 +15,9 @@ use std::vec::Vec;
use xkbcommon::xkb; use xkbcommon::xkb;
use ::action;
use ::keyboard::{ use ::keyboard::{
KeyState, PressType, KeyState, PressType,
generate_keymaps, generate_keycodes, KeyCode, FormattingError generate_keymap, generate_keycodes, FormattingError
}; };
use ::layout; use ::layout;
use ::layout::ArrangementKind; use ::layout::ArrangementKind;
@ -240,20 +239,14 @@ type ButtonIds = String;
#[derive(Debug, Default, Deserialize, PartialEq)] #[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
struct ButtonMeta { struct ButtonMeta {
// TODO: structure (action, keysym, text, modifier) as an enum /// Special action to perform on activation. Conflicts with keysym, text.
// to detect conflicts and missing values at compile time
/// Special action to perform on activation.
/// Conflicts with keysym, text, modifier.
action: Option<Action>, action: Option<Action>,
/// The name of the XKB keysym to emit on activation. /// The name of the XKB keysym to emit on activation.
/// Conflicts with action, text, modifier. /// Conflicts with action, text
keysym: Option<String>, keysym: Option<String>,
/// The text to submit on activation. Will be derived from ID if not present /// The text to submit on activation. Will be derived from ID if not present
/// Conflicts with action, keysym, modifier. /// Conflicts with action, keysym
text: Option<String>, text: Option<String>,
/// The modifier to apply while the key is locked
/// Conflicts with action, keysym, text
modifier: Option<Modifier>,
/// If not present, will be derived from text or the button ID /// If not present, will be derived from text or the button ID
label: Option<String>, label: Option<String>,
/// Conflicts with label /// Conflicts with label
@ -271,23 +264,6 @@ enum Action {
SetView(String), SetView(String),
#[serde(rename="show_prefs")] #[serde(rename="show_prefs")]
ShowPrefs, ShowPrefs,
/// Remove last character
#[serde(rename="erase")]
Erase,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
enum Modifier {
Control,
Shift,
Lock,
#[serde(alias="Mod1")]
Alt,
Mod2,
Mod3,
Mod4,
Mod5,
} }
#[derive(Debug, Clone, Deserialize, PartialEq)] #[derive(Debug, Clone, Deserialize, PartialEq)]
@ -382,45 +358,53 @@ impl Layout {
) )
)}).collect(); )}).collect();
let symbolmap: HashMap<String, KeyCode> = generate_keycodes( let keymap: HashMap<String, u32> = generate_keycodes(
extract_symbol_names(&button_actions) button_actions.iter()
.filter_map(|(_name, action)| {
match action {
::action::Action::Submit {
text: _, keys,
} => Some(keys),
_ => None,
}
})
.flatten()
.map(|named_keysym| named_keysym.0.as_str())
); );
let button_states = button_actions.into_iter().map(|(name, action)| {
let keycodes = match &action {
::action::Action::Submit { text: _, keys } => {
keys.iter().map(|named_keycode| {
*keymap.get(named_keycode.0.as_str())
.expect(
format!(
"keycode {} in key {} missing from keymap",
named_keycode.0,
name
).as_str()
)
}).collect()
},
_ => Vec::new(),
};
(
name.into(),
KeyState {
pressed: PressType::Released,
locked: false,
keycodes,
action,
}
)
});
let button_states = HashMap::<String, KeyState>::from_iter( let button_states = HashMap::<String, KeyState>::from_iter(
button_actions.into_iter().map(|(name, action)| { button_states
let keycodes = match &action {
::action::Action::Submit { text: _, keys } => {
keys.iter().map(|named_keysym| {
symbolmap.get(named_keysym.0.as_str())
.expect(
format!(
"keysym {} in key {} missing from symbol map",
named_keysym.0,
name
).as_str()
)
.clone()
}).collect()
},
action::Action::Erase => vec![
symbolmap.get("BackSpace")
.expect(&format!("BackSpace missing from symbol map"))
.clone(),
],
_ => Vec::new(),
};
(
name.into(),
KeyState {
pressed: PressType::Released,
keycodes,
action,
}
)
})
); );
let keymaps = match generate_keymaps(symbolmap) { // TODO: generate from symbols
let keymap_str = match generate_keymap(&button_states) {
Err(e) => { return (Err(e), warning_handler) }, Err(e) => { return (Err(e), warning_handler) },
Ok(v) => v, Ok(v) => v,
}; };
@ -433,8 +417,8 @@ impl Layout {
)} )}
); );
let views: Vec<_> = self.views.iter() let views = HashMap::from_iter(
.map(|(name, view)| { self.views.iter().map(|(name, view)| {
let rows = view.iter().map(|row| { let rows = view.iter().map(|row| {
let buttons = row.split_ascii_whitespace() let buttons = row.split_ascii_whitespace()
.map(|name| { .map(|name| {
@ -448,46 +432,30 @@ impl Layout {
&mut warning_handler, &mut warning_handler,
)) ))
}); });
layout::Row::new( ::layout::Row {
add_offsets( angle: 0,
buttons: add_offsets(
buttons, buttons,
|button| button.size.width, |button| button.size.width,
).collect() ).collect()
) }
}); });
let rows = add_offsets(rows, |row| row.get_size().height) let rows = add_offsets(rows, |row| row.get_height())
.collect(); .collect();
( (
name.clone(), name.clone(),
layout::View::new(rows) layout::View::new(rows)
) )
}).collect(); })
);
// Center views on the same point.
let views = {
let total_size = layout::View::calculate_super_size(
views.iter().map(|(_name, view)| view).collect()
);
HashMap::from_iter(views.into_iter().map(|(name, view)| (
name,
(
layout::c::Point {
x: (total_size.width - view.get_size().width) / 2.0,
y: (total_size.height - view.get_size().height) / 2.0,
},
view,
),
)))
};
( (
Ok(::layout::LayoutData { Ok(::layout::LayoutData {
views: views, views: views,
keymaps: keymaps.into_iter().map(|keymap_str| keymap_str: {
CString::new(keymap_str) CString::new(keymap_str)
.expect("Invalid keymap string generated") .expect("Invalid keymap string generated")
).collect(), },
// FIXME: use a dedicated field // FIXME: use a dedicated field
margins: layout::Margins { margins: layout::Margins {
top: self.margins.top, top: self.margins.top,
@ -519,27 +487,22 @@ fn create_action<H: logging::Handler>(
Action(Action), Action(Action),
Text(String), Text(String),
Keysym(String), Keysym(String),
Modifier(Modifier),
}; };
let submission = match ( let submission = match (
&symbol_meta.action, &symbol_meta.action,
&symbol_meta.keysym, &symbol_meta.keysym,
&symbol_meta.text, &symbol_meta.text
&symbol_meta.modifier,
) { ) {
(Some(action), None, None, None) => SubmitData::Action(action.clone()), (Some(action), None, None) => SubmitData::Action(action.clone()),
(None, Some(keysym), None, None) => SubmitData::Keysym(keysym.clone()), (None, Some(keysym), None) => SubmitData::Keysym(keysym.clone()),
(None, None, Some(text), None) => SubmitData::Text(text.clone()), (None, None, Some(text)) => SubmitData::Text(text.clone()),
(None, None, None, Some(modifier)) => { (None, None, None) => SubmitData::Text(name.into()),
SubmitData::Modifier(modifier.clone())
},
(None, None, None, None) => SubmitData::Text(name.into()),
_ => { _ => {
warning_handler.handle( warning_handler.handle(
logging::Level::Warning, logging::Level::Warning,
&format!( &format!(
"Button {} has more than one of (action, keysym, text, modifier)", "Button {} has more than one of (action, keysym, text)",
name, name,
), ),
); );
@ -595,7 +558,6 @@ fn create_action<H: logging::Handler>(
SubmitData::Action( SubmitData::Action(
Action::ShowPrefs Action::ShowPrefs
) => ::action::Action::ShowPreferences, ) => ::action::Action::ShowPreferences,
SubmitData::Action(Action::Erase) => action::Action::Erase,
SubmitData::Keysym(keysym) => ::action::Action::Submit { SubmitData::Keysym(keysym) => ::action::Action::Submit {
text: None, text: None,
keys: vec!(::action::KeySym( keys: vec!(::action::KeySym(
@ -627,27 +589,7 @@ fn create_action<H: logging::Handler>(
false => format!("U{:04X}", codepoint as u32), false => format!("U{:04X}", codepoint as u32),
}) })
}).collect(), }).collect(),
}, }
SubmitData::Modifier(modifier) => match modifier {
Modifier::Control => action::Action::ApplyModifier(
action::Modifier::Control,
),
Modifier::Alt => action::Action::ApplyModifier(
action::Modifier::Alt,
),
unsupported_modifier => {
warning_handler.handle(
logging::Level::Bug,
&format!(
"Modifier {:?} unsupported", unsupported_modifier,
),
);
action::Action::Submit {
text: None,
keys: Vec::new(),
}
},
},
} }
} }
@ -723,42 +665,17 @@ fn create_button<H: logging::Handler>(
} }
} }
fn extract_symbol_names<'a>(actions: &'a [(&str, action::Action)])
-> impl Iterator<Item=String> + 'a
{
actions.iter()
.filter_map(|(_name, act)| {
match act {
action::Action::Submit {
text: _, keys,
} => Some(keys.clone()),
action::Action::Erase => Some(vec!(action::KeySym("BackSpace".into()))),
_ => None,
}
})
.flatten()
.map(|named_keysym| named_keysym.0)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::error::Error as ErrorTrait;
use ::logging::ProblemPanic; use ::logging::ProblemPanic;
const THIS_FILE: &str = file!();
fn path_from_root(file: &'static str) -> PathBuf {
PathBuf::from(THIS_FILE)
.parent().unwrap()
.parent().unwrap()
.join(file)
}
#[test] #[test]
fn test_parse_path() { fn test_parse_path() {
assert_eq!( assert_eq!(
Layout::from_file(path_from_root("tests/layout.yaml")).unwrap(), Layout::from_file(PathBuf::from("tests/layout.yaml")).unwrap(),
Layout { Layout {
margins: Margins { top: 0f64, bottom: 0f64, side: 0f64 }, margins: Margins { top: 0f64, bottom: 0f64, side: 0f64 },
views: hashmap!( views: hashmap!(
@ -770,7 +687,6 @@ mod tests {
keysym: None, keysym: None,
action: None, action: None,
text: None, text: None,
modifier: None,
label: Some("test".into()), label: Some("test".into()),
outline: None, outline: None,
} }
@ -785,14 +701,13 @@ mod tests {
/// Check if the default protection works /// Check if the default protection works
#[test] #[test]
fn test_empty_views() { fn test_empty_views() {
let out = Layout::from_file(path_from_root("tests/layout2.yaml")); let out = Layout::from_file(PathBuf::from("tests/layout2.yaml"));
match out { match out {
Ok(_) => assert!(false, "Data mistakenly accepted"), Ok(_) => assert!(false, "Data mistakenly accepted"),
Err(e) => { Err(e) => {
let mut handled = false; let mut handled = false;
if let Error::Yaml(ye) = &e { if let Error::Yaml(ye) = &e {
handled = ye.to_string() handled = ye.description() == "missing field `views`";
.starts_with("missing field `views`");
}; };
if !handled { if !handled {
println!("Unexpected error {:?}", e); println!("Unexpected error {:?}", e);
@ -804,13 +719,13 @@ mod tests {
#[test] #[test]
fn test_extra_field() { fn test_extra_field() {
let out = Layout::from_file(path_from_root("tests/layout3.yaml")); let out = Layout::from_file(PathBuf::from("tests/layout3.yaml"));
match out { match out {
Ok(_) => assert!(false, "Data mistakenly accepted"), Ok(_) => assert!(false, "Data mistakenly accepted"),
Err(e) => { Err(e) => {
let mut handled = false; let mut handled = false;
if let Error::Yaml(ye) = &e { if let Error::Yaml(ye) = &e {
handled = ye.to_string() handled = ye.description()
.starts_with("unknown field `bad_field`"); .starts_with("unknown field `bad_field`");
}; };
if !handled { if !handled {
@ -823,14 +738,14 @@ mod tests {
#[test] #[test]
fn test_layout_punctuation() { fn test_layout_punctuation() {
let out = Layout::from_file(path_from_root("tests/layout_key1.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
.unwrap() .unwrap()
.build(ProblemPanic).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.get_buttons()[0].1 .buttons[0].1
.label, .label,
::layout::Label::Text(CString::new("test").unwrap()) ::layout::Label::Text(CString::new("test").unwrap())
); );
@ -838,14 +753,14 @@ mod tests {
#[test] #[test]
fn test_layout_unicode() { fn test_layout_unicode() {
let out = Layout::from_file(path_from_root("tests/layout_key2.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
.unwrap() .unwrap()
.build(ProblemPanic).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.get_buttons()[0].1 .buttons[0].1
.label, .label,
::layout::Label::Text(CString::new("test").unwrap()) ::layout::Label::Text(CString::new("test").unwrap())
); );
@ -854,37 +769,20 @@ mod tests {
/// Test multiple codepoints /// Test multiple codepoints
#[test] #[test]
fn test_layout_unicode_multi() { fn test_layout_unicode_multi() {
let out = Layout::from_file(path_from_root("tests/layout_key3.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
.unwrap() .unwrap()
.build(ProblemPanic).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.get_buttons()[0].1 .buttons[0].1
.state.borrow() .state.borrow()
.keycodes.len(), .keycodes.len(),
2 2
); );
} }
/// Test if erase yields a useable keycode
#[test]
fn test_layout_erase() {
let out = Layout::from_file(path_from_root("tests/layout_erase.yaml"))
.unwrap()
.build(ProblemPanic).0
.unwrap();
assert_eq!(
out.views["base"].1
.get_rows()[0].1
.get_buttons()[0].1
.state.borrow()
.keycodes.len(),
1
);
}
#[test] #[test]
fn parsing_fallback() { fn parsing_fallback() {
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME) assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
@ -930,7 +828,6 @@ mod tests {
keysym: None, keysym: None,
text: None, text: None,
action: None, action: None,
modifier: None,
label: Some("test".into()), label: Some("test".into()),
outline: None, outline: None,
} }
@ -948,7 +845,7 @@ mod tests {
#[test] #[test]
fn test_layout_margins() { fn test_layout_margins() {
let out = Layout::from_file(path_from_root("tests/layout_margins.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_margins.yaml"))
.unwrap() .unwrap()
.build(ProblemPanic).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
@ -962,35 +859,4 @@ mod tests {
} }
); );
} }
#[test]
fn test_extract_symbols() {
let actions = [(
"ac",
action::Action::Submit {
text: None,
keys: vec![
action::KeySym("a".into()),
action::KeySym("c".into()),
],
},
)];
assert_eq!(
extract_symbol_names(&actions[..]).collect::<Vec<_>>(),
vec!["a", "c"],
);
}
#[test]
fn test_extract_symbols_erase() {
let actions = [(
"Erase",
action::Action::Erase,
)];
assert_eq!(
extract_symbol_names(&actions[..]).collect::<Vec<_>>(),
vec!["BackSpace"],
);
}
} }

View File

@ -3,11 +3,9 @@
use cairo; use cairo;
use std::cell::RefCell; use std::cell::RefCell;
use ::action::Action;
use ::keyboard; use ::keyboard;
use ::layout::{ Button, Layout }; use ::layout::{ Button, Layout };
use ::layout::c::{ EekGtkKeyboard, Point }; use ::layout::c::{ EekGtkKeyboard, Point };
use ::submission::Submission;
use glib::translate::FromGlibPtrNone; use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt; use gtk::WidgetExt;
@ -39,37 +37,30 @@ mod c {
); );
} }
/// Draws all buttons that are not in the base state
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_draw_all_changed( fn squeek_layout_draw_all_changed(
layout: *mut Layout, layout: *mut Layout,
renderer: EekRenderer, renderer: EekRenderer,
cr: *mut cairo_sys::cairo_t, cr: *mut cairo_sys::cairo_t,
submission: *const Submission,
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = unsafe { &*submission };
let cr = unsafe { cairo::Context::from_raw_none(cr) }; let cr = unsafe { cairo::Context::from_raw_none(cr) };
let active_modifiers = submission.get_active_modifiers();
layout.foreach_visible_button(|offset, button| { let view = layout.get_current_view();
let state = RefCell::borrow(&button.state).clone(); for (row_offset, row) in &view.get_rows() {
let active_mod = match &state.action { for (x_offset, button) in &row.buttons {
Action::ApplyModifier(m) => active_modifiers.contains(m), let state = RefCell::borrow(&button.state).clone();
_ => false, if state.pressed == keyboard::PressType::Pressed || state.locked {
}; render_button_at_position(
let locked = state.action.is_active(&layout.current_view) renderer, &cr,
| active_mod; row_offset + Point { x: *x_offset, y: 0.0 },
if state.pressed == keyboard::PressType::Pressed || locked { button.as_ref(),
render_button_at_position( state.pressed, state.locked,
renderer, &cr, );
offset, }
button.as_ref(),
state.pressed, locked,
);
} }
}) }
} }
#[no_mangle] #[no_mangle]
@ -81,15 +72,17 @@ mod c {
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let cr = unsafe { cairo::Context::from_raw_none(cr) }; let cr = unsafe { cairo::Context::from_raw_none(cr) };
let view = layout.get_current_view();
layout.foreach_visible_button(|offset, button| { for (row_offset, row) in &view.get_rows() {
render_button_at_position( for (x_offset, button) in &row.buttons {
renderer, &cr, render_button_at_position(
offset, renderer, &cr,
button.as_ref(), row_offset + Point { x: *x_offset, y: 0.0 },
keyboard::PressType::Released, false, button.as_ref(),
); keyboard::PressType::Released, false,
}) );
}
}
} }
} }

View File

@ -49,23 +49,6 @@ void imservice_connect_listeners(struct zwp_input_method_v2 *im, struct imservic
zwp_input_method_v2_add_listener(im, &input_method_listener, imservice); zwp_input_method_v2_add_listener(im, &input_method_listener, imservice);
} }
void
eek_input_method_commit_string(struct zwp_input_method_v2 *zwp_input_method_v2, const char *text)
{
zwp_input_method_v2_commit_string(zwp_input_method_v2, text);
}
void
eek_input_method_delete_surrounding_text(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t before_length, uint32_t after_length) {
zwp_input_method_v2_delete_surrounding_text(zwp_input_method_v2, before_length, after_length);
};
void
eek_input_method_commit(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t serial)
{
zwp_input_method_v2_commit(zwp_input_method_v2, serial);
}
/// Declared explicitly because _destroy is inline, /// Declared explicitly because _destroy is inline,
/// making it unavailable in Rust /// making it unavailable in Rust
void imservice_destroy_im(struct zwp_input_method_v2 *im) { void imservice_destroy_im(struct zwp_input_method_v2 *im) {

View File

@ -1,8 +1,3 @@
/*! Manages zwp_input_method_v2 protocol.
*
* Library module.
*/
use std::boxed::Box; use std::boxed::Box;
use std::ffi::CString; use std::ffi::CString;
use std::fmt; use std::fmt;
@ -37,12 +32,9 @@ pub mod c {
fn imservice_destroy_im(im: *mut c::InputMethod); fn imservice_destroy_im(im: *mut c::InputMethod);
#[allow(improper_ctypes)] // IMService will never be dereferenced in C #[allow(improper_ctypes)] // IMService will never be dereferenced in C
pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService); pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
pub fn eek_input_method_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); fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32);
fn server_context_service_show_keyboard(imservice: *const UIManager); fn server_context_service_show_keyboard(imservice: *const UIManager);
fn server_context_service_keyboard_release_visibility(imservice: *const UIManager); fn server_context_service_hide_keyboard(imservice: *const UIManager);
} }
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
@ -165,7 +157,7 @@ pub mod c {
} }
} else { } else {
if let Some(ui) = imservice.ui_manager { if let Some(ui) = imservice.ui_manager {
unsafe { server_context_service_keyboard_release_visibility(ui); } unsafe { server_context_service_hide_keyboard(ui); }
} }
} }
} }
@ -185,7 +177,7 @@ pub mod c {
imservice.current.active = false; imservice.current.active = false;
if let Some(ui) = imservice.ui_manager { if let Some(ui) = imservice.ui_manager {
unsafe { server_context_service_keyboard_release_visibility(ui); } unsafe { server_context_service_hide_keyboard(ui); }
} }
} }
@ -336,7 +328,7 @@ impl Default for IMProtocolState {
pub struct IMService { pub struct IMService {
/// Owned reference (still created and destroyed in C) /// Owned reference (still created and destroyed in C)
pub im: *mut c::InputMethod, pub im: *const c::InputMethod,
/// Unowned reference. Be careful, it's shared with C at large /// Unowned reference. Be careful, it's shared with C at large
state_manager: *const c::StateManager, state_manager: *const c::StateManager,
/// Unowned reference. Be careful, it's shared with C at large /// Unowned reference. Be careful, it's shared with C at large
@ -348,11 +340,6 @@ pub struct IMService {
serial: Wrapping<u32>, serial: Wrapping<u32>,
} }
pub enum SubmitError {
/// The input method had not been activated
NotActive,
}
impl IMService { impl IMService {
pub fn new( pub fn new(
im: *mut c::InputMethod, im: *mut c::InputMethod,
@ -377,51 +364,4 @@ impl IMService {
} }
imservice imservice
} }
pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_commit_string(self.im, text.as_ptr())
}
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn delete_surrounding_text(
&self,
before: u32, after: u32,
) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_delete_surrounding_text(
self.im,
before, after,
)
}
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn commit(&mut self) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_commit(self.im, self.serial.0)
}
self.serial += Wrapping(1u32);
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn is_active(&self) -> bool {
self.current.active
}
} }

View File

@ -1,19 +1,14 @@
/*! State of the emulated keyboard and keys. /*! State of the emulated keyboard and keys.
* Regards the keyboard as if it was composed of switches. */ * Regards the keyboard as if it was composed of switches. */
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::mem;
use std::ptr;
use std::rc::Rc;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action; use ::action::Action;
use ::util; use ::logging;
// Traits
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
@ -23,46 +18,30 @@ pub enum PressType {
Pressed = 1, Pressed = 1,
} }
/// The extended, unambiguous layout-keycode pub type KeyCode = u32;
#[derive(Debug, Clone)]
pub struct KeyCode {
pub code: u32,
pub keymap_idx: usize,
}
bitflags!{
/// Map to `virtual_keyboard.modifiers` modifiers values
/// From https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Keyboard_State
pub struct Modifiers: u8 {
const SHIFT = 0x1;
const LOCK = 0x2;
const CONTROL = 0x4;
/// Alt
const MOD1 = 0x8;
const MOD2 = 0x10;
const MOD3 = 0x20;
/// Meta
const MOD4 = 0x40;
/// AltGr
const MOD5 = 0x80;
}
}
/// When the submitted actions of keys need to be tracked,
/// they need a stable, comparable ID
#[derive(Clone, PartialEq)]
pub struct KeyStateId(*const KeyState);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct KeyState { pub struct KeyState {
pub pressed: PressType, pub pressed: PressType,
/// A cache of raw keycodes derived from Action::Submit given a keymap pub locked: bool,
/// A cache of raw keycodes derived from Action::Sumbit given a keymap
pub keycodes: Vec<KeyCode>, pub keycodes: Vec<KeyCode>,
/// Static description of what the key does when pressed or released /// Static description of what the key does when pressed or released
pub action: Action, pub action: Action,
} }
impl KeyState { impl KeyState {
#[must_use]
pub fn into_activated(self) -> KeyState {
match self.action {
Action::LockView { lock: _, unlock: _ } => KeyState {
locked: self.locked ^ true,
..self
},
_ => self,
}
}
#[must_use] #[must_use]
pub fn into_released(self) -> KeyState { pub fn into_released(self) -> KeyState {
KeyState { KeyState {
@ -70,27 +49,13 @@ impl KeyState {
..self ..self
} }
} }
#[must_use]
pub fn into_pressed(self) -> KeyState {
KeyState {
pressed: PressType::Pressed,
..self
}
}
/// KeyStates instances are the unique identifiers of pressed keys,
/// and the actions submitted with them.
pub fn get_id(keystate: &Rc<RefCell<KeyState>>) -> KeyStateId {
KeyStateId(keystate.as_ptr() as *const KeyState)
}
} }
/// Sorts an iterator by converting it to a Vector and back /// Sorts an iterator by converting it to a Vector and back
fn sorted<'a, I: Iterator<Item=String>>( fn sorted<'a, I: Iterator<Item=&'a str>>(
iter: I iter: I
) -> impl Iterator<Item=String> { ) -> impl Iterator<Item=&'a str> {
let mut v: Vec<String> = iter.collect(); let mut v: Vec<&'a str> = iter.collect();
v.sort(); v.sort();
v.into_iter() v.into_iter()
} }
@ -98,17 +63,14 @@ fn sorted<'a, I: Iterator<Item=String>>(
/// Generates a mapping where each key gets a keycode, starting from ~~8~~ /// Generates a mapping where each key gets a keycode, starting from ~~8~~
/// HACK: starting from 9, because 8 results in keycode 0, /// HACK: starting from 9, because 8 results in keycode 0,
/// which the compositor likes to discard /// which the compositor likes to discard
pub fn generate_keycodes<'a, C: IntoIterator<Item=String>>( pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C, key_names: C
) -> HashMap<String, KeyCode> { ) -> HashMap<String, u32> {
HashMap::from_iter( HashMap::from_iter(
// Sort to remove a source of indeterminism in keycode assignment. // sort to remove a source of indeterminism in keycode assignment
sorted(key_names.into_iter()) sorted(key_names.into_iter())
.zip(util::cycle_count(9..255)) .map(|name| String::from(name))
.map(|(name, (code, keymap_idx))| ( .zip(9..)
String::from(name),
KeyCode { code, keymap_idx },
))
) )
} }
@ -133,54 +95,9 @@ impl From<io::Error> for FormattingError {
} }
} }
/// Index is the key code, String is the occupant. /// Generates a de-facto single level keymap. TODO: actually drop second level
/// Starts all empty. pub fn generate_keymap(
/// https://gitlab.freedesktop.org/xorg/xserver/-/issues/260 keystates: &HashMap::<String, KeyState>
type SingleKeyMap = [Option<String>; 256];
fn single_key_map_new() -> SingleKeyMap {
// Why can't we just initialize arrays without tricks -_- ?
unsafe {
// Inspired by
// https://www.reddit.com/r/rust/comments/5n7bh1/how_to_create_an_array_of_a_type_with_clone_but/
#[cfg(feature = "rustc_less_1_36")]
let mut array: SingleKeyMap = mem::uninitialized();
#[cfg(not(feature = "rustc_less_1_36"))]
let mut array: SingleKeyMap = mem::MaybeUninit::uninit().assume_init();
for element in array.iter_mut() {
ptr::write(element, None);
}
array
}
}
pub fn generate_keymaps(symbolmap: HashMap::<String, KeyCode>)
-> Result<Vec<String>, FormattingError>
{
let mut bins: Vec<SingleKeyMap> = Vec::new();
for (name, KeyCode { code, keymap_idx }) in symbolmap.into_iter() {
if keymap_idx >= bins.len() {
bins.resize_with(
keymap_idx + 1,
|| single_key_map_new(),
);
}
bins[keymap_idx][code as usize] = Some(name);
}
let mut out = Vec::new();
for bin in bins {
out.push(generate_keymap(&bin)?);
}
Ok(out)
}
/// Generates a de-facto single level keymap.
/// Key codes must not repeat and must remain between 9 and 255.
fn generate_keymap(
symbolmap: &SingleKeyMap,
) -> Result<String, FormattingError> { ) -> Result<String, FormattingError> {
let mut buf: Vec<u8> = Vec::new(); let mut buf: Vec<u8> = Vec::new();
writeln!( writeln!(
@ -191,80 +108,66 @@ fn generate_keymap(
minimum = 8; minimum = 8;
maximum = 255;" maximum = 255;"
)?; )?;
let pairs: Vec<(&String, usize)> = symbolmap.iter()
// Attach a key code to each cell.
.enumerate()
// Get rid of empty keycodes.
.filter_map(|(code, name)| name.as_ref().map(|n| (n, code)))
.collect();
// Xorg can only consume up to 255 keys, so this may not work in Xwayland. for (name, state) in keystates.iter() {
// Two possible solutions: if let Action::Submit { text: _, keys } = &state.action {
// - use levels to cram multiple characters into one key if let 0 = keys.len() {
// - swap layouts on key presses log_print!(
for (_name, keycode) in &pairs { logging::Level::Warning,
write!( "Key {} has no keysyms", name,
buf, );
" };
<I{}> = {0};", for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
keycode, write!(
)?; buf,
"
<{}> = {};",
named_keysym.0,
keycode,
)?;
}
}
} }
writeln!( writeln!(
buf, buf,
" "
indicator 1 = \"Caps Lock\"; // Xwayland won't accept without it.
}}; }};
xkb_symbols \"squeekboard\" {{ xkb_symbols \"squeekboard\" {{
"
name[Group1] = \"Letters\";
name[Group2] = \"Numbers/Symbols\";"
)?; )?;
for (name, keycode) in pairs { for (name, state) in keystates.iter() {
write!( if let Action::Submit { text: _, keys } = &state.action {
buf, for keysym in keys.iter() {
" write!(
key <I{}> {{ [ {} ] }};", buf,
keycode, "
name, key <{}> {{ [ {0} ] }};",
)?; keysym.0,
)?;
}
}
} }
writeln!( writeln!(
buf, buf,
" "
}}; }};
xkb_types \"squeekboard\" {{ xkb_types \"squeekboard\" {{
virtual_modifiers Squeekboard; // No modifiers! Needed for Xorg for some reason.
// Those names are needed for Xwayland.
type \"ONE_LEVEL\" {{
modifiers= none;
level_name[Level1]= \"Any\";
}};
type \"TWO_LEVEL\" {{
level_name[Level1]= \"Base\";
}};
type \"ALPHABETIC\" {{
level_name[Level1]= \"Base\";
}};
type \"KEYPAD\" {{
level_name[Level1]= \"Base\";
}};
type \"SHIFT+ALT\" {{
level_name[Level1]= \"Base\";
}};
type \"TWO_LEVEL\" {{
modifiers = Shift;
map[Shift] = Level2;
level_name[Level1] = \"Base\";
level_name[Level2] = \"Shift\";
}};
}}; }};
xkb_compatibility \"squeekboard\" {{ xkb_compatibility \"squeekboard\" {{
// Needed for Xwayland again.
interpret Any+AnyOf(all) {{
action= SetMods(modifiers=modMapMods,clearLocks);
}};
}}; }};
}};" }};"
)?; )?;
@ -279,16 +182,24 @@ mod tests {
use xkbcommon::xkb; use xkbcommon::xkb;
use ::action::KeySym;
#[test] #[test]
fn test_keymap_single_resolve() { fn test_keymap_multi() {
let mut key_map = single_key_map_new();
key_map[9] = Some("a".into());
key_map[10] = Some("c".into());
let keymap_str = generate_keymap(&key_map).unwrap();
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let keymap_str = generate_keymap(&hashmap!{
"ac".into() => KeyState {
action: Action::Submit {
text: None,
keys: vec!(KeySym("a".into()), KeySym("c".into())),
},
keycodes: vec!(9, 10),
locked: false,
pressed: PressType::Released,
},
}).unwrap();
let keymap = xkb::Keymap::new_from_string( let keymap = xkb::Keymap::new_from_string(
&context, &context,
keymap_str.clone(), keymap_str.clone(),
@ -301,36 +212,4 @@ mod tests {
assert_eq!(state.key_get_one_sym(9), xkb::KEY_a); assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
assert_eq!(state.key_get_one_sym(10), xkb::KEY_c); assert_eq!(state.key_get_one_sym(10), xkb::KEY_c);
} }
#[test]
fn test_keymap_second_resolve() {
let keymaps = generate_keymaps(hashmap!(
"a".into() => KeyCode { keymap_idx: 1, code: 9 },
)).unwrap();
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let keymap = xkb::Keymap::new_from_string(
&context,
keymaps[1].clone(), // this index is part of the test
xkb::KEYMAP_FORMAT_TEXT_V1,
xkb::KEYMAP_COMPILE_NO_FLAGS,
).expect("Failed to create keymap");
let state = xkb::State::new(&keymap);
assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
}
#[test]
fn test_symbolmap_overflow() {
// The 257th key (U1101) is interesting.
// Use Unicode encoding for being able to use in xkb keymaps.
let keynames = (0..258).map(|num| format!("U{:04X}", 0x1000 + num));
let keycodes = generate_keycodes(keynames);
// test now
let code = keycodes.get("U1101").expect("Did not find the tested keysym");
assert_eq!(code.keymap_idx, 1);
}
} }

View File

@ -7,23 +7,13 @@
#include "eek/eek-gtk-keyboard.h" #include "eek/eek-gtk-keyboard.h"
#include "eek/eek-renderer.h" #include "eek/eek-renderer.h"
#include "eek/eek-types.h" #include "eek/eek-types.h"
#include "src/submission.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h"
enum squeek_arrangement_kind { enum squeek_arrangement_kind {
ARRANGEMENT_KIND_BASE = 0, ARRANGEMENT_KIND_BASE = 0,
ARRANGEMENT_KIND_WIDE = 1, ARRANGEMENT_KIND_WIDE = 1,
}; };
struct squeek_layout_state {
enum squeek_arrangement_kind arrangement;
enum zwp_text_input_v3_content_purpose purpose;
enum zwp_text_input_v3_content_hint hint;
char *layout_name;
char *overlay_name;
};
struct squeek_layout; struct squeek_layout;
EekBounds squeek_button_get_bounds(const struct squeek_button*); EekBounds squeek_button_get_bounds(const struct squeek_button*);
@ -39,6 +29,7 @@ struct transformation squeek_layout_calculate_transformation(
double allocation_width, double allocation_size); double allocation_width, double allocation_size);
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type); struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
const char *squeek_layout_get_keymap(const struct squeek_layout*);
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *); enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
void squeek_layout_free(struct squeek_layout*); void squeek_layout_free(struct squeek_layout*);
@ -62,6 +53,6 @@ void squeek_layout_drag(struct squeek_layout *layout,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, EekboardContextService *manager, uint32_t timestamp, EekboardContextService *manager,
EekGtkKeyboard *ui_keyboard); EekGtkKeyboard *ui_keyboard);
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission); void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr); void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
#endif #endif

View File

@ -26,10 +26,10 @@ use std::vec::Vec;
use ::action::Action; use ::action::Action;
use ::drawing; use ::drawing;
use ::keyboard::KeyState; use ::keyboard::{ KeyState, PressType };
use ::logging; use ::logging;
use ::manager; use ::manager;
use ::submission::{ Submission, SubmitData, Timestamp }; use ::submission::{ Submission, Timestamp };
use ::util::find_max_double; use ::util::find_max_double;
// Traits // Traits
@ -52,14 +52,6 @@ pub mod c {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget); pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
#[no_mangle]
extern "C" {
#[allow(improper_ctypes)]
pub fn eek_gtk_keyboard_emit_feedback(
keyboard: EekGtkKeyboard,
);
}
/// Defined in eek-types.h /// Defined in eek-types.h
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -107,8 +99,8 @@ pub mod c {
impl Bounds { impl Bounds {
pub fn contains(&self, point: &Point) -> bool { pub fn contains(&self, point: &Point) -> bool {
point.x > self.x && point.x < self.x + self.width (point.x > self.x && point.x < self.x + self.width
&& point.y > self.y && point.y < self.y + self.height && point.y > self.y && point.y < self.y + self.height)
} }
} }
@ -236,6 +228,13 @@ pub mod c {
height: allocation_height, height: allocation_height,
}) })
} }
#[no_mangle]
pub extern "C"
fn squeek_layout_get_keymap(layout: *const Layout) -> *const c_char {
let layout = unsafe { &*layout };
layout.keymap_str.as_ptr()
}
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
@ -250,7 +249,7 @@ pub mod c {
unsafe { Box::from_raw(layout) }; unsafe { Box::from_raw(layout) };
} }
/// Entry points for more complex procedures and algorithms which span multiple modules /// Entry points for more complex procedures and algoithms which span multiple modules
pub mod procedures { pub mod procedures {
use super::*; use super::*;
@ -330,8 +329,11 @@ pub mod c {
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
let state = layout.find_button_by_position(point) let state = {
.map(|place| place.button.state.clone()); let view = layout.get_current_view();
view.find_button_by_position(point)
.map(|place| place.button.state.clone())
};
if let Some(state) = state { if let Some(state) = state {
seat::handle_press_key( seat::handle_press_key(
@ -342,9 +344,6 @@ pub mod c {
); );
// maybe TODO: draw on the display buffer here // maybe TODO: draw on the display buffer here
drawing::queue_redraw(ui_keyboard); drawing::queue_redraw(ui_keyboard);
unsafe {
eek_gtk_keyboard_emit_feedback(ui_keyboard);
}
}; };
} }
@ -375,7 +374,8 @@ pub mod c {
let pressed = layout.pressed_keys.clone(); let pressed = layout.pressed_keys.clone();
let button_info = { let button_info = {
let place = layout.find_button_by_position(point); let view = layout.get_current_view();
let place = view.find_button_by_position(point);
place.map(|place| {( place.map(|place| {(
place.button.state.clone(), place.button.state.clone(),
place.button.clone(), place.button.clone(),
@ -408,9 +408,6 @@ pub mod c {
&state, &state,
); );
// maybe TODO: draw on the display buffer here // maybe TODO: draw on the display buffer here
unsafe {
eek_gtk_keyboard_emit_feedback(ui_keyboard);
}
} }
} else { } else {
for wrapped_key in pressed { for wrapped_key in pressed {
@ -486,63 +483,40 @@ pub struct Button {
} }
/// The graphical representation of a row of buttons /// The graphical representation of a row of buttons
#[derive(Clone, Debug)]
pub struct Row { pub struct Row {
/// Buttons together with their offset from the left relative to the row. /// Buttons together with their offset from the left
/// ie. the first button always start at 0. pub buttons: Vec<(f64, Box<Button>)>,
buttons: Vec<(f64, Box<Button>)>, /// Angle is not really used anywhere...
pub angle: i32,
/// Total size of the row
size: Size,
} }
impl Row { impl Row {
pub fn new(buttons: Vec<(f64, Box<Button>)>) -> Row { pub fn get_height(&self) -> f64 {
// Make sure buttons are sorted by offset. find_max_double(
debug_assert!({ self.buttons.iter(),
let mut sorted = buttons.clone();
sorted.sort_by(|(f1, _), (f2, _)| f1.partial_cmp(f2).unwrap());
sorted.iter().map(|(f, _)| *f).collect::<Vec<_>>()
== buttons.iter().map(|(f, _)| *f).collect::<Vec<_>>()
});
let width = buttons.iter().next_back()
.map(|(x_offset, button)| button.size.width + x_offset)
.unwrap_or(0.0);
let height = find_max_double(
buttons.iter(),
|(_offset, button)| button.size.height, |(_offset, button)| button.size.height,
); )
Row { buttons, size: Size { width, height } }
} }
pub fn get_size(&self) -> Size { fn get_width(&self) -> f64 {
self.size.clone() self.buttons.iter().next_back()
} .map(|(x_offset, button)| button.size.width + x_offset)
.unwrap_or(0.0)
pub fn get_buttons(&self) -> &Vec<(f64, Box<Button>)> {
&self.buttons
} }
/// Finds the first button that covers the specified point /// Finds the first button that covers the specified point
/// relative to row's position's origin /// relative to row's position's origin
fn find_button_by_position(&self, x: f64) -> &(f64, Box<Button>) fn find_button_by_position(&self, point: c::Point)
-> Option<(f64, &Box<Button>)>
{ {
// Buttons are sorted so we can use a binary search to find the clicked self.buttons.iter().find(|(x_offset, button)| {
// button. Note this doesn't check whether the point is actually within c::Bounds {
// a button. This is on purpose as we want a click past the left edge of x: *x_offset, y: 0.0,
// the left-most button to register as a click. width: button.size.width,
let result = self.buttons.binary_search_by( height: button.size.height,
|&(f, _)| f.partial_cmp(&x).unwrap() }.contains(&point)
); })
.map(|(x_offset, button)| (*x_offset, button))
let index = result.unwrap_or_else(|r| r);
let index = if index > 0 { index - 1 } else { 0 };
&self.buttons[index]
} }
} }
@ -553,100 +527,56 @@ pub struct Spacing {
} }
pub struct View { pub struct View {
/// Rows together with their offsets from the top left /// Rows together with their offsets from the top
rows: Vec<(c::Point, Row)>, rows: Vec<(f64, Row)>,
/// Total size of the view
size: Size,
} }
impl View { impl View {
pub fn new(rows: Vec<(f64, Row)>) -> View { pub fn new(rows: Vec<(f64, Row)>) -> View {
// Make sure rows are sorted by offset. View { rows }
debug_assert!({
let mut sorted = rows.clone();
sorted.sort_by(|(f1, _), (f2, _)| f1.partial_cmp(f2).unwrap());
sorted.iter().map(|(f, _)| *f).collect::<Vec<_>>()
== rows.iter().map(|(f, _)| *f).collect::<Vec<_>>()
});
// No need to call `get_rows()`,
// as the biggest row is the most far reaching in both directions
// because they are all centered.
let width = find_max_double(rows.iter(), |(_offset, row)| row.size.width);
let height = rows.iter().next_back()
.map(|(y_offset, row)| row.size.height + y_offset)
.unwrap_or(0.0);
// Center the rows
let rows = rows.into_iter().map(|(y_offset, row)| {(
c::Point {
x: (width - row.size.width) / 2.0,
y: y_offset,
},
row,
)}).collect::<Vec<_>>();
View { rows, size: Size { width, height } }
} }
/// Finds the first button that covers the specified point /// Finds the first button that covers the specified point
/// relative to view's position's origin /// relative to view's position's origin
fn find_button_by_position(&self, point: c::Point) fn find_button_by_position(&self, point: c::Point)
-> Option<ButtonPlace> -> Option<ButtonPlace>
{ {
// Only test bounds of the view here, letting rows/column search extend self.get_rows().iter().find_map(|(row_offset, row)| {
// to the edges of these bounds. // make point relative to the inside of the row
let bounds = c::Bounds { row.find_button_by_position({
x: 0.0, c::Point { x: point.x, y: point.y } - row_offset
y: 0.0, }).map(|(button_x_offset, button)| ButtonPlace {
width: self.size.width, button,
height: self.size.height, offset: row_offset + c::Point {
}; x: button_x_offset,
if !bounds.contains(&point) { y: 0.0,
return None; },
} })
// Rows are sorted so we can use a binary search to find the row.
let result = self.rows.binary_search_by(
|(f, _)| f.y.partial_cmp(&point.y).unwrap()
);
let index = result.unwrap_or_else(|r| r);
let index = if index > 0 { index - 1 } else { 0 };
let row = &self.rows[index];
let button = row.1.find_button_by_position(point.x - row.0.x);
Some(ButtonPlace {
button: &button.1,
offset: &row.0 + c::Point { x: button.0, y: 0.0 },
}) })
} }
pub fn get_size(&self) -> Size { fn get_width(&self) -> f64 {
self.size.clone() // No need to call `get_rows()`,
// as the biggest row is the most far reaching in both directions
// because they are all centered.
find_max_double(self.rows.iter(), |(_offset, row)| row.get_width())
}
fn get_height(&self) -> f64 {
self.rows.iter().next_back()
.map(|(y_offset, row)| row.get_height() + y_offset)
.unwrap_or(0.0)
} }
/// Returns positioned rows, with appropriate x offsets (centered) /// Returns positioned rows, with appropriate x offsets (centered)
pub fn get_rows(&self) -> &Vec<(c::Point, Row)> { pub fn get_rows(&self) -> Vec<(c::Point, &Row)> {
&self.rows let available_width = self.get_width();
} self.rows.iter().map(|(y_offset, row)| {(
c::Point {
/// Returns a size which contains all the views x: (available_width - row.get_width()) / 2.0,
/// if they are all centered on the same point. y: *y_offset,
pub fn calculate_super_size(views: Vec<&View>) -> Size { },
Size { row,
height: find_max_double( )}).collect()
views.iter(),
|view| view.size.height,
),
width: find_max_double(
views.iter(),
|view| view.size.width,
),
}
} }
} }
@ -675,12 +605,11 @@ pub struct Layout {
// Views own the actual buttons which have state // Views own the actual buttons which have state
// Maybe they should own UI only, // Maybe they should own UI only,
// and keys should be owned by a dedicated non-UI-State? // and keys should be owned by a dedicated non-UI-State?
/// Point is the offset within the layout pub views: HashMap<String, View>,
pub views: HashMap<String, (c::Point, View)>,
// Non-UI stuff // Non-UI stuff
/// xkb keymaps applicable to the contained keys. Unchangeable /// xkb keymap applicable to the contained keys. Unchangeable
pub keymaps: Vec<CString>, pub keymap_str: CString,
// Changeable state // Changeable state
// a Vec would be enough, but who cares, this will be small & fast enough // a Vec would be enough, but who cares, this will be small & fast enough
// TODO: turn those into per-input point *_buttons to track dragging. // TODO: turn those into per-input point *_buttons to track dragging.
@ -690,13 +619,13 @@ pub struct Layout {
// When the list tracks actual location, // When the list tracks actual location,
// it becomes possible to place popovers and other UI accurately. // it becomes possible to place popovers and other UI accurately.
pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>, pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
pub locked_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
} }
/// A builder structure for picking up layout data from storage /// A builder structure for picking up layout data from storage
pub struct LayoutData { pub struct LayoutData {
/// Point is the offset within layout pub views: HashMap<String, View>,
pub views: HashMap<String, (c::Point, View)>, pub keymap_str: CString,
pub keymaps: Vec<CString>,
pub margins: Margins, pub margins: Margins,
} }
@ -719,19 +648,15 @@ impl Layout {
kind, kind,
current_view: "base".to_owned(), current_view: "base".to_owned(),
views: data.views, views: data.views,
keymaps: data.keymaps, keymap_str: data.keymap_str,
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
locked_keys: HashSet::new(),
margins: data.margins, margins: data.margins,
} }
} }
pub fn get_current_view_position(&self) -> &(c::Point, View) {
&self.views
.get(&self.current_view).expect("Selected nonexistent view")
}
pub fn get_current_view(&self) -> &View { pub fn get_current_view(&self) -> &View {
&self.views.get(&self.current_view).expect("Selected nonexistent view").1 self.views.get(&self.current_view).expect("Selected nonexistent view")
} }
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> { fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
@ -745,9 +670,16 @@ impl Layout {
/// Calculates size without margins /// Calculates size without margins
fn calculate_inner_size(&self) -> Size { fn calculate_inner_size(&self) -> Size {
View::calculate_super_size( Size {
self.views.iter().map(|(_, (_offset, v))| v).collect() height: find_max_double(
) self.views.iter(),
|(_name, view)| view.get_height(),
),
width: find_max_double(
self.views.iter(),
|(_name, view)| view.get_width(),
),
}
} }
/// Size including margins /// Size including margins
@ -782,42 +714,6 @@ impl Layout {
scale: 1.0, scale: 1.0,
}) })
} }
fn find_button_by_position(&self, point: c::Point) -> Option<ButtonPlace> {
let (offset, layout) = self.get_current_view_position();
layout.find_button_by_position(point - offset)
}
pub fn foreach_visible_button<F>(&self, mut f: F)
where F: FnMut(c::Point, &Box<Button>)
{
let (view_offset, view) = self.get_current_view_position();
for (row_offset, row) in view.get_rows() {
for (x_offset, button) in &row.buttons {
let offset = view_offset
+ row_offset.clone()
+ c::Point { x: *x_offset, y: 0.0 };
f(offset, button);
}
}
}
pub fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
let mut out = Vec::new();
let view = self.get_current_view();
for (_, row) in view.get_rows() {
for (_, button) in &row.buttons {
let locked = {
let state = RefCell::borrow(&button.state).clone();
state.action.is_locked(&self.current_view)
};
if locked {
out.push(button.state.clone());
}
}
}
out
}
} }
mod procedures { mod procedures {
@ -867,9 +763,14 @@ mod procedures {
let button = make_button_with_state("1".into(), state); let button = make_button_with_state("1".into(), state);
let button_ptr = as_ptr(&button); let button_ptr = as_ptr(&button);
let row = Row::new(vec!((0.1, button))); let row = Row {
buttons: vec!((0.1, button)),
angle: 0,
};
let view = View::new(vec!((1.2, row))); let view = View {
rows: vec!((1.2, row)),
};
assert_eq!( assert_eq!(
find_key_places(&view, &state_clone.clone()).into_iter() find_key_places(&view, &state_clone.clone()).into_iter()
@ -880,7 +781,9 @@ mod procedures {
) )
); );
let view = View::new(vec![]); let view = View {
rows: Vec::new(),
};
assert_eq!( assert_eq!(
find_key_places(&view, &state_clone.clone()).is_empty(), find_key_places(&view, &state_clone.clone()).is_empty(),
true true
@ -938,9 +841,9 @@ mod seat {
#[must_use] #[must_use]
fn unstick_locks(layout: &mut Layout) -> ViewChange { fn unstick_locks(layout: &mut Layout) -> ViewChange {
let mut new_view = None; let mut new_view = None;
for key in layout.get_locked_keys().clone() { for key in layout.locked_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
let key = RefCell::borrow(key); let mut key = RefCell::borrow_mut(key);
match &key.action { match &key.action {
Action::LockView { lock: _, unlock: view } => { Action::LockView { lock: _, unlock: view } => {
new_view = Some(view.clone()); new_view = Some(view.clone());
@ -951,6 +854,7 @@ mod seat {
a, a,
), ),
}; };
key.locked = false;
} }
ViewChange { ViewChange {
@ -971,38 +875,13 @@ mod seat {
"Key {:?} was already pressed", rckey, "Key {:?} was already pressed", rckey,
); );
} }
let key: KeyState = { let mut key = rckey.borrow_mut();
RefCell::borrow(rckey).clone() submission.virtual_keyboard.switch(
}; &key.keycodes,
let action = key.action.clone(); PressType::Pressed,
match action { time,
Action::Submit { );
text: Some(text), key.pressed = PressType::Pressed;
keys: _,
} => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Text(&text),
&key.keycodes,
time,
),
Action::Submit {
text: None,
keys: _,
} => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Keycodes,
&key.keycodes,
time,
),
Action::Erase => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Erase,
&key.keycodes,
time,
),
_ => {},
};
RefCell::replace(rckey, key.into_pressed());
} }
pub fn handle_release_key( pub fn handle_release_key(
@ -1020,43 +899,38 @@ mod seat {
// update // update
let key = key.into_released(); let key = key.into_released();
let key = match action {
Action::LockView { lock: _, unlock: _ } => key.into_activated(),
_ => key,
};
// process changes // process changes
match action { match action {
Action::Submit { text: _, keys: _ } Action::Submit { text: _, keys: _ } => {
| Action::Erase
=> {
unstick_locks(layout).apply(); unstick_locks(layout).apply();
submission.handle_release(KeyState::get_id(rckey), time); submission.virtual_keyboard.switch(
&key.keycodes,
PressType::Released,
time,
);
}, },
Action::SetView(view) => { Action::SetView(view) => {
try_set_view(layout, view) try_set_view(layout, view)
}, },
Action::LockView { lock, unlock } => { Action::LockView { lock, unlock } => {
let gets_locked = !key.action.is_locked(&layout.current_view); // The button that triggered this will be in the right state
// due to commit at the end.
unstick_locks(layout) unstick_locks(layout)
// It doesn't matter what the resulting view should be, // It doesn't matter what the resulting view should be,
// it's getting changed anyway. // it's getting changed anyway.
.choose_view( .choose_view(
match gets_locked { match key.locked {
true => lock.clone(), true => lock.clone(),
false => unlock.clone(), false => unlock.clone(),
} }
) )
.apply() .apply()
}, },
Action::ApplyModifier(modifier) => {
// FIXME: key id is unneeded with stateless locks
let key_id = KeyState::get_id(rckey);
let gets_locked = !submission.is_modifier_active(modifier.clone());
match gets_locked {
true => submission.handle_add_modifier(
key_id,
modifier, time,
),
false => submission.handle_drop_modifier(key_id, time),
}
}
// only show when UI is present // only show when UI is present
Action::ShowPreferences => if let Some(ui) = &ui { Action::ShowPreferences => if let Some(ui) = &ui {
// only show when layout manager is available // only show when layout manager is available
@ -1083,11 +957,20 @@ mod seat {
} }
} }
}, },
Action::SetModifier(_) => log_print!(
logging::Level::Bug,
"Modifiers unsupported",
),
}; };
let pointer = ::util::Pointer(rckey.clone()); let pointer = ::util::Pointer(rckey.clone());
// Apply state changes // Apply state changes
layout.pressed_keys.remove(&pointer); layout.pressed_keys.remove(&pointer);
if key.locked {
layout.locked_keys.insert(pointer);
} else {
layout.locked_keys.remove(&pointer);
}
// Commit activated button state changes // Commit activated button state changes
RefCell::replace(rckey, key); RefCell::replace(rckey, key);
} }
@ -1098,11 +981,11 @@ mod test {
use super::*; use super::*;
use std::ffi::CString; use std::ffi::CString;
use ::keyboard::PressType;
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> { pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
Rc::new(RefCell::new(::keyboard::KeyState { Rc::new(RefCell::new(::keyboard::KeyState {
pressed: PressType::Released, pressed: PressType::Released,
locked: false,
keycodes: Vec::new(), keycodes: Vec::new(),
action: Action::SetView("default".into()), action: Action::SetView("default".into()),
})) }))
@ -1123,56 +1006,39 @@ mod test {
#[test] #[test]
fn check_centering() { fn check_centering() {
// A B // foo
// ---bar--- // ---bar---
let view = View::new(vec![ let view = View::new(vec![
( (
0.0, 0.0,
Row::new(vec![ Row {
( angle: 0,
buttons: vec![(
0.0, 0.0,
Box::new(Button { Box::new(Button {
size: Size { width: 5.0, height: 10.0 }, size: Size { width: 10.0, height: 10.0 },
..*make_button_with_state("A".into(), make_state()) ..*make_button_with_state("foo".into(), make_state())
}), }),
), )]
( },
5.0,
Box::new(Button {
size: Size { width: 5.0, height: 10.0 },
..*make_button_with_state("B".into(), make_state())
}),
),
]),
), ),
( (
10.0, 10.0,
Row::new(vec![ Row {
( angle: 0,
buttons: vec![(
0.0, 0.0,
Box::new(Button { Box::new(Button {
size: Size { width: 30.0, height: 10.0 }, size: Size { width: 30.0, height: 10.0 },
..*make_button_with_state("bar".into(), make_state()) ..*make_button_with_state("bar".into(), make_state())
}), }),
), )]
]), },
) )
]); ]);
assert!( assert!(
view.find_button_by_position(c::Point { x: 5.0, y: 5.0 }) view.find_button_by_position(c::Point { x: 5.0, y: 5.0 })
.unwrap().button.name.to_str().unwrap() == "A" .is_none()
);
assert!(
view.find_button_by_position(c::Point { x: 14.99, y: 5.0 })
.unwrap().button.name.to_str().unwrap() == "A"
);
assert!(
view.find_button_by_position(c::Point { x: 15.01, y: 5.0 })
.unwrap().button.name.to_str().unwrap() == "B"
);
assert!(
view.find_button_by_position(c::Point { x: 25.0, y: 5.0 })
.unwrap().button.name.to_str().unwrap() == "B"
); );
} }
@ -1182,19 +1048,23 @@ mod test {
let view = View::new(vec![ let view = View::new(vec![
( (
0.0, 0.0,
Row::new(vec![( Row {
0.0, angle: 0,
Box::new(Button { buttons: vec![(
size: Size { width: 1.0, height: 1.0 }, 0.0,
..*make_button_with_state("foo".into(), make_state()) Box::new(Button {
}), size: Size { width: 1.0, height: 1.0 },
)]), ..*make_button_with_state("foo".into(), make_state())
}),
)]
},
), ),
]); ]);
let layout = Layout { let layout = Layout {
current_view: String::new(), current_view: String::new(),
keymaps: Vec::new(), keymap_str: CString::new("").unwrap(),
kind: ArrangementKind::Base, kind: ArrangementKind::Base,
locked_keys: HashSet::new(),
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
// Lots of bottom margin // Lots of bottom margin
margins: Margins { margins: Margins {
@ -1204,7 +1074,7 @@ mod test {
bottom: 1.0, bottom: 1.0,
}, },
views: hashmap! { views: hashmap! {
String::new() => (c::Point { x: 0.0, y: 0.0 }, view), String::new() => view,
}, },
}; };
assert_eq!( assert_eq!(

View File

@ -35,6 +35,5 @@ mod style;
mod submission; mod submission;
pub mod tests; pub mod tests;
pub mod util; pub mod util;
mod ui_manager;
mod vkeyboard; mod vkeyboard;
mod xdg; mod xdg;

View File

@ -31,16 +31,22 @@ pub enum Error {
impl ::std::fmt::Display for Error { impl ::std::fmt::Display for Error {
fn fmt(&self, out: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, out: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
out.write_str(match self { use ::std::error::Error;
out.write_str(self.description())
}
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
match self {
&Error::NotWellFormed => "Language tag is not well-formed.", &Error::NotWellFormed => "Language tag is not well-formed.",
// this is exception: here we do want exhaustive match so we don't publish version with // this is exception: here we do want exhaustive match so we don't publish version with
// missing descriptions by mistake. // missing descriptions by mistake.
&Error::__NonExhaustive => panic!("Placeholder error must not be instantiated!"), &Error::__NonExhaustive => panic!("Placeholder error must not be instantiated!"),
}) }
} }
} }
/// Convenience Result alias. /// Convenience Result alias.
type Result<T> = ::std::result::Result<T, Error>; type Result<T> = ::std::result::Result<T, Error>;

View File

@ -14,7 +14,6 @@ sources = [
config_h, config_h,
'dbus.c', 'dbus.c',
'imservice.c', 'imservice.c',
'popover.c',
'server-context-service.c', 'server-context-service.c',
'wayland.c', 'wayland.c',
'../eek/eek.c', '../eek/eek.c',
@ -37,10 +36,9 @@ cc = meson.get_compiler('c')
deps = [ deps = [
# dependency('glib-2.0', version: '>=2.26.0'), # dependency('glib-2.0', version: '>=2.26.0'),
dependency('gio-2.0', version: '>=2.26.0'), dependency('gio-2.0', version: '>=2.26.0'),
dependency('gio-unix-2.0'),
dependency('gnome-desktop-3.0', version: '>=3.0'), dependency('gnome-desktop-3.0', version: '>=3.0'),
dependency('gtk+-3.0', version: '>=3.0'), dependency('gtk+-3.0', version: '>=3.0'),
dependency('libfeedback-0.0'), dependency('libcroco-0.6'),
dependency('wayland-client', version: '>=1.14'), dependency('wayland-client', version: '>=1.14'),
dependency('xkbcommon'), dependency('xkbcommon'),
cc.find_library('m'), cc.find_library('m'),
@ -57,8 +55,7 @@ rslibs = custom_target(
output: ['librs.a'], output: ['librs.a'],
install: false, install: false,
console: true, console: true,
command: [cargo_build] + ['@OUTPUT@', '--lib'] + cargo_build_flags, command: [cargo_build] + cargo_build_flags + ['@OUTPUT@', '--lib']
depends: cargo_toml,
) )
build_rstests = custom_target( build_rstests = custom_target(
@ -72,17 +69,17 @@ build_rstests = custom_target(
output: ['src'], output: ['src'],
install: false, install: false,
console: true, console: true,
command: [cargo_script, 'test', '--no-run'] + cargo_build_flags, command: [cargo_script, 'test', '--no-run'],
depends: [rslibs, cargo_toml], # no point building tests if the code itself fails depends: rslibs, # no point building tests if the code itself fails
) )
test( test(
'rstest', 'rstest',
cargo_script, cargo_script,
args: ['test'] + cargo_build_flags, args: ['test'],
# this is a whole Carg-based test suite, let it run for a while # this is a whole Carg-based test suite, let it run for a while
timeout: 900, timeout: 900,
depends: [build_rstests, cargo_toml], depends: build_rstests,
) )
libsqueekboard = static_library('libsqueekboard', libsqueekboard = static_library('libsqueekboard',

View File

@ -4,14 +4,10 @@
#include "wayland-client-protocol.h" #include "wayland-client-protocol.h"
struct squeek_outputs; struct squeek_outputs;
struct squeek_output_handle {
struct wl_output *output;
struct squeek_outputs *outputs;
};
struct squeek_outputs *squeek_outputs_new(void); struct squeek_outputs *squeek_outputs_new();
void squeek_outputs_free(struct squeek_outputs*); 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);
struct squeek_output_handle squeek_outputs_get_current(struct squeek_outputs*); struct wl_output *squeek_outputs_get_current(struct squeek_outputs*);
int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output); int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output);
#endif #endif

View File

@ -17,7 +17,7 @@ pub mod c {
// Defined in C // Defined in C
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, PartialEq, Copy)] #[derive(Clone, PartialEq)]
pub struct WlOutput(*const c_void); pub struct WlOutput(*const c_void);
#[repr(C)] #[repr(C)]
@ -105,24 +105,6 @@ pub mod c {
type COutputs = ::util::c::Wrapped<Outputs>; 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())
}
}
// Defined in Rust // Defined in Rust
extern fn outputs_handle_geometry( extern fn outputs_handle_geometry(
@ -258,15 +240,46 @@ pub mod c {
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_outputs_get_current(raw_collection: COutputs) -> OutputHandle { fn squeek_outputs_get_current(raw_collection: COutputs) -> WlOutput {
let collection = raw_collection.clone_ref(); let collection = raw_collection.clone_ref();
let collection = collection.borrow(); let collection = collection.borrow();
OutputHandle { collection.outputs[0].output.clone()
wl_output: collection.outputs[0].output.clone(),
outputs: raw_collection.clone(),
}
} }
#[no_mangle]
pub extern "C"
fn squeek_outputs_get_perceptual_width(
raw_collection: COutputs,
wl_output: WlOutput,
) -> i32 {
let collection = raw_collection.clone_ref();
let collection = collection.borrow();
let output_state = find_output(&collection, wl_output)
.map(|o| &o.current);
match output_state {
Some(OutputState {
current_mode: Some(super::Mode { width, height } ),
transform: Some(transform),
scale,
}) => {
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => width / scale,
_ => height / scale,
}
},
_ => {
log_print!(
logging::Level::Surprise,
"Not enough info received on output",
);
0
},
}
}
// TODO: handle unregistration // TODO: handle unregistration
fn find_output( fn find_output(
@ -292,14 +305,6 @@ pub mod c {
} }
} }
/// Generic size
#[derive(Clone)]
pub struct Size {
pub width: u32,
pub height: u32,
}
/// wl_output mode
#[derive(Clone)] #[derive(Clone)]
struct Mode { struct Mode {
width: i32, width: i32,
@ -310,16 +315,10 @@ struct Mode {
pub struct OutputState { pub struct OutputState {
current_mode: Option<Mode>, current_mode: Option<Mode>,
transform: Option<c::Transform>, transform: Option<c::Transform>,
pub scale: i32, scale: i32,
} }
impl OutputState { impl OutputState {
// More properly, this would have been a builder kind of struct,
// with wl_output gradually adding properties to it
// before it reached a fully initialized state,
// when it would transform into a struct without all (some?) of the Options.
// However, it's not clear which state is fully initialized,
// and whether it would make things easier at all anyway.
fn uninitialized() -> OutputState { fn uninitialized() -> OutputState {
OutputState { OutputState {
current_mode: None, current_mode: None,
@ -327,32 +326,6 @@ impl OutputState {
scale: 1, scale: 1,
} }
} }
pub fn get_pixel_size(&self) -> Option<Size> {
use self::c::Transform;
match self {
OutputState {
current_mode: Some(Mode { width, height } ),
transform: Some(transform),
scale: _,
} => Some(
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => Size {
width: *width as u32,
height: *height as u32,
},
_ => Size {
width: *height as u32,
height: *width as u32,
},
}
),
_ => None,
}
}
} }
pub struct Output { pub struct Output {

View File

@ -1,69 +0,0 @@
#include <gio/gio.h>
static void
call_dbus_cb (GDBusProxy *proxy,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GError) err = NULL;
g_autoptr (GVariant) output = NULL;
output = g_dbus_proxy_call_finish (proxy, res, &err);
if (err) {
g_warning ("Can't open panel %s", err->message);
}
g_object_unref (proxy);
}
static void
create_dbus_proxy_cb (GObject *source_object, GAsyncResult *res, char *panel)
{
GDBusProxy *proxy;
g_autoptr (GError) err = NULL;
GVariantBuilder builder;
GVariant *params[3];
GVariant *array[1];
proxy = g_dbus_proxy_new_for_bus_finish (res, &err);
if (err != NULL) {
g_warning ("Can't open panel %s: %s", panel, err->message);
g_free (panel);
return;
}
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
g_variant_builder_add (&builder, "v", g_variant_new_string (""));
array[0] = g_variant_new ("v", g_variant_new ("(sav)", panel, &builder));
params[0] = g_variant_new_string ("launch-panel");
params[1] = g_variant_new_array (G_VARIANT_TYPE ("v"), array, 1);
params[2] = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
g_dbus_proxy_call (proxy,
"Activate",
g_variant_new_tuple (params, 3),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) call_dbus_cb,
NULL);
g_free (panel);
}
void
popover_open_settings_panel (char *panel)
{
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.gnome.ControlCenter",
"/org/gnome/ControlCenter",
"org.gtk.Actions",
NULL,
(GAsyncReadyCallback) create_dbus_proxy_cb,
g_strdup (panel));
}

View File

@ -3,7 +3,6 @@
use gio; use gio;
use gtk; use gtk;
use std::ffi::CString; use std::ffi::CString;
use std::cmp::Ordering;
use ::layout::c::{ Bounds, EekGtkKeyboard }; use ::layout::c::{ Bounds, EekGtkKeyboard };
use ::locale; use ::locale;
use ::locale::{ OwnedTranslation, Translation, compare_current_locale }; use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
@ -12,29 +11,16 @@ use ::logging;
use ::manager; use ::manager;
use ::resources; use ::resources;
// Traits
use gio::ActionMapExt; use gio::ActionMapExt;
use gio::SettingsExt; use gio::SettingsExt;
#[cfg(feature = "gio_v0_5")]
use gio::SimpleActionExt; use gio::SimpleActionExt;
use glib::translate::FromGlibPtrNone; use glib::translate::FromGlibPtrNone;
use glib::variant::ToVariant; use glib::variant::ToVariant;
#[cfg(not(feature = "gtk_v0_5"))]
use gtk::BuilderExtManual;
use gtk::PopoverExt; use gtk::PopoverExt;
use gtk::WidgetExt; use gtk::WidgetExt;
use std::io::Write; use std::io::Write;
use ::logging::Warn; use ::logging::Warn;
mod c {
use std::os::raw::c_char;
#[no_mangle]
extern "C" {
pub fn popover_open_settings_panel(panel: *const c_char);
}
}
mod variants { mod variants {
use glib; use glib;
use glib::Variant; use glib::Variant;
@ -43,7 +29,6 @@ mod variants {
use glib::ToVariant; use glib::ToVariant;
use glib::translate::FromGlibPtrFull; use glib::translate::FromGlibPtrFull;
use glib::translate::FromGlibPtrNone;
use glib::translate::ToGlibPtr; use glib::translate::ToGlibPtr;
/// Unpacks tuple & array variants /// Unpacks tuple & array variants
@ -106,7 +91,7 @@ mod variants {
unsafe { unsafe {
let ret = glib_sys::g_variant_builder_end(builder); let ret = glib_sys::g_variant_builder_end(builder);
glib_sys::g_variant_builder_unref(builder); glib_sys::g_variant_builder_unref(builder);
glib::Variant::from_glib_none(ret) glib::Variant::from_glib_full(ret)
} }
} }
} }
@ -138,12 +123,6 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
xml, xml,
" "
</section> </section>
<section>
<item>
<attribute name=\"label\" translatable=\"yes\">Keyboard Settings</attribute>
<attribute name=\"action\">settings</attribute>
</item>
</section>
</menu> </menu>
</interface>" </interface>"
).unwrap(); ).unwrap();
@ -152,40 +131,19 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
) )
} }
fn get_settings(schema_name: &str) -> Option<gio::Settings> {
let mut error_handler = logging::Print{};
gio::SettingsSchemaSource::get_default()
.or_warn(
&mut error_handler,
logging::Problem::Surprise,
"No gsettings schemas installed.",
)
.and_then(|sss|
sss.lookup(schema_name, true)
.or_warn(
&mut error_handler,
logging::Problem::Surprise,
&format!("Gsettings schema {} not installed", schema_name),
)
)
.map(|_sschema| gio::Settings::new(schema_name))
}
fn set_layout(kind: String, name: String) { fn set_layout(kind: String, name: String) {
let settings = get_settings("org.gnome.desktop.input-sources"); let settings = gio::Settings::new("org.gnome.desktop.input-sources");
if let Some(settings) = settings { let inputs = settings.get_value("sources").unwrap();
let inputs = settings.get_value("sources").unwrap(); let current = (kind.clone(), name.clone());
let current = (kind.clone(), name.clone()); let inputs = variants::get_tuples(inputs).into_iter()
let inputs = variants::get_tuples(inputs).into_iter() .filter(|t| t != &current);
.filter(|t| t != &current); let inputs = vec![(kind, name)].into_iter()
let inputs = vec![(kind, name)].into_iter() .chain(inputs).collect();
.chain(inputs).collect(); settings.set_value(
settings.set_value( "sources",
"sources", &variants::ArrayPairString(inputs).to_variant()
&variants::ArrayPairString(inputs).to_variant(), );
); settings.apply();
settings.apply();
}
} }
/// A reference to what the user wants to see /// A reference to what the user wants to see
@ -325,13 +283,9 @@ pub fn show(
let overlay_layouts = resources::get_overlays().into_iter() let overlay_layouts = resources::get_overlays().into_iter()
.map(|name| LayoutId::Local(name.to_string())); .map(|name| LayoutId::Local(name.to_string()));
let settings = get_settings("org.gnome.desktop.input-sources"); let settings = gio::Settings::new("org.gnome.desktop.input-sources");
let inputs = settings let inputs = settings.get_value("sources").unwrap();
.map(|settings| { let inputs = variants::get_tuples(inputs);
let inputs = settings.get_value("sources").unwrap();
variants::get_tuples(inputs)
})
.unwrap_or_else(|| Vec::new());
let system_layouts: Vec<LayoutId> = inputs.into_iter() let system_layouts: Vec<LayoutId> = inputs.into_iter()
.map(|(kind, name)| LayoutId::System { kind, name }) .map(|(kind, name)| LayoutId::System { kind, name })
@ -350,13 +304,8 @@ pub fn show(
.zip(all_layouts.clone().into_iter()) .zip(all_layouts.clone().into_iter())
.collect(); .collect();
human_names.sort_unstable_by(|(tr_a, layout_a), (tr_b, layout_b)| { human_names.sort_unstable_by(|(tr_a, _), (tr_b, _)| {
// Sort first by layout then name compare_current_locale(&tr_a.0, &tr_b.0)
match (layout_a, layout_b) {
(LayoutId::Local(_), LayoutId::System { .. }) => Ordering::Greater,
(LayoutId::System { .. }, LayoutId::Local(_)) => Ordering::Less,
_ => compare_current_locale(&tr_a.0, &tr_b.0)
}
}); });
// GVariant doesn't natively support `enum`s, // GVariant doesn't natively support `enum`s,
@ -435,14 +384,8 @@ pub fn show(
menu_inner.popdown(); menu_inner.popdown();
}); });
let settings_action = gio::SimpleAction::new("settings", None);
settings_action.connect_activate(move |_, _| {
unsafe { c::popover_open_settings_panel(CString::new("region").unwrap().as_ptr()) };
});
let action_group = gio::SimpleActionGroup::new(); let action_group = gio::SimpleActionGroup::new();
action_group.add_action(&layout_action); action_group.add_action(&layout_action);
action_group.add_action(&settings_action);
menu.insert_action_group("popup", Some(&action_group)); menu.insert_action_group("popup", Some(&action_group));
}; };

View File

@ -11,35 +11,22 @@ use std::iter::FromIterator;
// and what a convenience layout. "_wide" is not a layout, // and what a convenience layout. "_wide" is not a layout,
// neither is "number" // neither is "number"
const KEYBOARDS: &[(*const str, *const str)] = &[ const KEYBOARDS: &[(*const str, *const str)] = &[
// layouts: us must be left as first, as it is the, // layouts
// fallback layout. The others should be alphabetical.
("us", include_str!("../data/keyboards/us.yaml")), ("us", include_str!("../data/keyboards/us.yaml")),
("us_wide", include_str!("../data/keyboards/us_wide.yaml")), ("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
("br", include_str!("../data/keyboards/br.yaml")),
("de", include_str!("../data/keyboards/de.yaml")), ("de", include_str!("../data/keyboards/de.yaml")),
("be", include_str!("../data/keyboards/be.yaml")),
("be_wide", include_str!("../data/keyboards/be_wide.yaml")),
("de_wide", include_str!("../data/keyboards/de_wide.yaml")), ("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
("dk", include_str!("../data/keyboards/dk.yaml")), ("el", include_str!("../data/keyboards/el.yaml")),
("es", include_str!("../data/keyboards/es.yaml")), ("es", include_str!("../data/keyboards/es.yaml")),
("fi", include_str!("../data/keyboards/fi.yaml")), ("fi", include_str!("../data/keyboards/fi.yaml")),
("fr", include_str!("../data/keyboards/fr.yaml")),
("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
("it+fur", include_str!("../data/keyboards/it+fur.yaml")),
("gr", include_str!("../data/keyboards/gr.yaml")),
("it", include_str!("../data/keyboards/it.yaml")), ("it", include_str!("../data/keyboards/it.yaml")),
("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")), ("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")), ("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")),
("no", include_str!("../data/keyboards/no.yaml")), ("no", include_str!("../data/keyboards/no.yaml")),
("number", include_str!("../data/keyboards/number.yaml")), ("number", include_str!("../data/keyboards/number.yaml")),
("pl", include_str!("../data/keyboards/pl.yaml")),
("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
("ru", include_str!("../data/keyboards/ru.yaml")),
("se", include_str!("../data/keyboards/se.yaml")), ("se", include_str!("../data/keyboards/se.yaml")),
("ua", include_str!("../data/keyboards/ua.yaml")),
// layout+overlay // layout+overlay
("terminal", include_str!("../data/keyboards/terminal.yaml")), ("terminal", include_str!("../data/keyboards/terminal.yaml")),
("terminal_wide", include_str!("../data/keyboards/terminal_wide.yaml")),
// Overlays // Overlays
("emoji", include_str!("../data/keyboards/emoji.yaml")), ("emoji", include_str!("../data/keyboards/emoji.yaml")),
]; ];
@ -76,10 +63,8 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
("de-DE", include_str!("../data/langs/de-DE.txt")), ("de-DE", include_str!("../data/langs/de-DE.txt")),
("en-US", include_str!("../data/langs/en-US.txt")), ("en-US", include_str!("../data/langs/en-US.txt")),
("es-ES", include_str!("../data/langs/es-ES.txt")), ("es-ES", include_str!("../data/langs/es-ES.txt")),
("fur-IT", include_str!("../data/langs/fur-IT.txt")),
("ja-JP", include_str!("../data/langs/ja-JP.txt")), ("ja-JP", include_str!("../data/langs/ja-JP.txt")),
("pl-PL", include_str!("../data/langs/pl-PL.txt")), ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
("ru-RU", include_str!("../data/langs/ru-RU.txt")),
]; ];
pub fn get_layout_names(lang: &str) pub fn get_layout_names(lang: &str)

View File

@ -30,119 +30,125 @@
enum { enum {
PROP_0, PROP_0,
PROP_SIZE_CONSTRAINT_LANDSCAPE,
PROP_SIZE_CONSTRAINT_PORTRAIT,
PROP_VISIBLE, PROP_VISIBLE,
PROP_ENABLED,
PROP_LAST PROP_LAST
}; };
typedef struct _ServerContextServiceClass ServerContextServiceClass;
struct _ServerContextService { struct _ServerContextService {
GObject parent; GObject parent;
EekboardContextService *state; // unowned EekboardContextService *state; // unowned
/// Needed for instantiating the widget /// Needed for instantiating the widget
struct submission *submission; // unowned struct submission *submission; // unowned
struct squeek_layout_state *layout;
struct ui_manager *manager; // unowned
gboolean visible; gboolean visible;
gboolean enabled;
PhoshLayerSurface *window; PhoshLayerSurface *window;
GtkWidget *widget; // nullable GtkWidget *widget; // nullable
guint hiding; guint hiding;
guint last_requested_height; guint last_requested_height;
enum squeek_arrangement_kind last_type;
gdouble size_constraint_landscape[2];
gdouble size_constraint_portrait[2];
};
struct _ServerContextServiceClass {
GObjectClass parent_class;
}; };
G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT); G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT);
static void static void
on_destroy (ServerContextService *self, GtkWidget *widget) on_destroy (GtkWidget *widget, gpointer user_data)
{ {
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self)); ServerContextService *context = user_data;
g_assert (widget == GTK_WIDGET(self->window)); g_assert (widget == GTK_WIDGET(context->window));
self->window = NULL; context->window = NULL;
self->widget = NULL; context->widget = NULL;
//eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context)); //eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context));
} }
static void static void
on_notify_map (ServerContextService *self, GtkWidget *widget) make_widget (ServerContextService *context);
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
g_object_set (self, "visible", TRUE, NULL); static void
on_notify_keyboard (GObject *object,
GParamSpec *spec,
ServerContextService *context)
{
/* Recreate the keyboard widget to keep in sync with the keymap. */
if (context->window)
make_widget(context);
gboolean visible;
g_object_get (context, "visible", &visible, NULL);
if (visible) {
server_context_service_hide_keyboard(context);
server_context_service_show_keyboard(context);
}
}
static void
on_notify_map (GObject *object,
ServerContextService *context)
{
g_object_set (context, "visible", TRUE, NULL);
} }
static void static void
on_notify_unmap (ServerContextService *self, GtkWidget *widget) on_notify_unmap (GObject *object,
ServerContextService *context)
{ {
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self)); (void)object;
g_object_set (context, "visible", FALSE, NULL);
g_object_set (self, "visible", FALSE, NULL);
} }
static uint32_t static uint32_t
calculate_height(int32_t width, GdkRectangle *geometry) calculate_height(int32_t width)
{ {
uint32_t height; uint32_t height = 180;
if (geometry->width > geometry->height) { if (width < 360 && width > 0) {
// 1:5 ratio works fine on lanscape mode, and makes sure there's height = ((unsigned)width * 7 / 12); // to match 360×210
// room left for the app window } else if (width < 540) {
height = width / 5; height = 180 + (540 - (unsigned)width) * 30 / 180; // smooth transition
} 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; return height;
} }
enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 540) {
return ARRANGEMENT_KIND_BASE;
}
return ARRANGEMENT_KIND_WIDE;
}
static void static void
on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface) on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context)
{ {
GdkDisplay *display = NULL;
GdkWindow *window = NULL;
GdkMonitor *monitor = NULL;
GdkRectangle geometry;
gint width; gint width;
gint height; 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), g_object_get(G_OBJECT(surface),
"configured-width", &width, "configured-width", &width,
"configured-height", &height, "configured-height", &height,
NULL); NULL);
// check if the change would switch types
// In order to improve height calculation, we need the monitor geometry so enum squeek_arrangement_kind new_type = get_type((uint32_t)width, (uint32_t)height);
// we can use different algorithms for portrait and landscape mode. if (context->last_type != new_type) {
// Note: this is a temporary fix until the size manager is complete. context->last_type = new_type;
display = gdk_display_get_default (); eekboard_context_service_update_layout(context->state, context->last_type);
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, guint desired_height = calculate_height(width);
// 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; guint configured_height = (guint)height;
// if height was already requested once but a different one was given // if height was already requested once but a different one was given
// (for the same set of surrounding properties), // (for the same set of surrounding properties),
@ -150,8 +156,8 @@ on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
// as it's likely to create pointless loops // as it's likely to create pointless loops
// of request->reject->request_again->... // of request->reject->request_again->...
if (desired_height != configured_height if (desired_height != configured_height
&& self->last_requested_height != desired_height) { && context->last_requested_height != desired_height) {
self->last_requested_height = desired_height; context->last_requested_height = desired_height;
phosh_layer_surface_set_size(surface, 0, phosh_layer_surface_set_size(surface, 0,
(gint)desired_height); (gint)desired_height);
phosh_layer_surface_set_exclusive_zone(surface, (gint)desired_height); phosh_layer_surface_set_exclusive_zone(surface, (gint)desired_height);
@ -160,20 +166,19 @@ on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
} }
static void static void
make_window (ServerContextService *self) make_window (ServerContextService *context)
{ {
if (self->window) { if (context->window)
g_error("Window already present"); g_error("Window already present");
}
struct squeek_output_handle output = squeek_outputs_get_current(squeek_wayland->outputs); struct wl_output *output = squeek_outputs_get_current(squeek_wayland->outputs);
squeek_uiman_set_output(self->manager, output); int32_t width = squeek_outputs_get_perceptual_width(squeek_wayland->outputs, output);
uint32_t height = squeek_uiman_get_perceptual_height(self->manager); uint32_t height = calculate_height(width);
self->window = g_object_new ( context->window = g_object_new (
PHOSH_TYPE_LAYER_SURFACE, PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell, "layer-shell", squeek_wayland->layer_shell,
"wl-output", output.output, "wl-output", output,
"height", height, "height", height,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
@ -185,11 +190,11 @@ make_window (ServerContextService *self)
NULL NULL
); );
g_object_connect (self->window, g_object_connect (context->window,
"swapped-signal::destroy", G_CALLBACK(on_destroy), self, "signal::destroy", G_CALLBACK(on_destroy), context,
"swapped-signal::map", G_CALLBACK(on_notify_map), self, "signal::map", G_CALLBACK(on_notify_map), context,
"swapped-signal::unmap", G_CALLBACK(on_notify_unmap), self, "signal::unmap", G_CALLBACK(on_notify_unmap), context,
"swapped-signal::configured", G_CALLBACK(on_surface_configure), self, "signal::configured", G_CALLBACK(on_surface_configure), context,
NULL); NULL);
// The properties below are just to make hacking easier. // The properties below are just to make hacking easier.
@ -197,106 +202,90 @@ make_window (ServerContextService *self)
// and there's no space in the protocol for others. // and there's no space in the protocol for others.
// Those may still be useful in the future, // Those may still be useful in the future,
// or for hacks with regular windows. // or for hacks with regular windows.
gtk_widget_set_can_focus (GTK_WIDGET(self->window), FALSE); gtk_widget_set_can_focus (GTK_WIDGET(context->window), FALSE);
g_object_set (G_OBJECT(self->window), "accept_focus", FALSE, NULL); g_object_set (G_OBJECT(context->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(self->window), gtk_window_set_title (GTK_WINDOW(context->window),
_("Squeekboard")); _("Squeekboard"));
gtk_window_set_icon_name (GTK_WINDOW(self->window), "squeekboard"); gtk_window_set_icon_name (GTK_WINDOW(context->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(self->window), TRUE); gtk_window_set_keep_above (GTK_WINDOW(context->window), TRUE);
} }
static void static void
destroy_window (ServerContextService *self) destroy_window (ServerContextService *context)
{ {
gtk_widget_destroy (GTK_WIDGET (self->window)); gtk_widget_destroy (GTK_WIDGET (context->window));
self->window = NULL; context->window = NULL;
} }
static void static void
make_widget (ServerContextService *self) make_widget (ServerContextService *context)
{ {
if (self->widget) { if (context->widget) {
gtk_widget_destroy(self->widget); gtk_widget_destroy(context->widget);
self->widget = NULL; context->widget = NULL;
}
self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->layout);
gtk_widget_set_has_tooltip (self->widget, TRUE);
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
gtk_widget_show_all(self->widget);
}
static void
server_context_service_real_show_keyboard (ServerContextService *self)
{
if (!self->enabled) {
return;
} }
if (!self->window) { LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (context->state);
make_window (self);
}
if (!self->widget) {
make_widget (self);
}
self->visible = TRUE;
gtk_widget_show (GTK_WIDGET(self->window));
}
static void context->widget = eek_gtk_keyboard_new (keyboard, context->state, context->submission);
server_context_service_real_hide_keyboard (ServerContextService *self)
{ gtk_widget_set_has_tooltip (context->widget, TRUE);
gtk_widget_hide (GTK_WIDGET(self->window)); gtk_container_add (GTK_CONTAINER(context->window), context->widget);
self->visible = FALSE; gtk_widget_show (context->widget);
} }
static gboolean static gboolean
on_hide (ServerContextService *self) on_hide (ServerContextService *context)
{ {
server_context_service_real_hide_keyboard(self); gtk_widget_hide (GTK_WIDGET(context->window));
self->hiding = 0; context->hiding = 0;
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
void static void
server_context_service_show_keyboard (ServerContextService *self) server_context_service_real_show_keyboard (ServerContextService *context)
{ {
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self)); if (context->hiding) {
g_source_remove (context->hiding);
if (self->hiding) { context->hiding = 0;
g_source_remove (self->hiding);
self->hiding = 0;
} }
if (!self->visible) { if (!context->window)
server_context_service_real_show_keyboard (self); make_window (context);
if (!context->widget)
make_widget (context);
context->visible = TRUE;
gtk_widget_show (GTK_WIDGET(context->window));
}
static void
server_context_service_real_hide_keyboard (ServerContextService *context)
{
if (!context->hiding)
context->hiding = g_timeout_add (200, (GSourceFunc) on_hide, context);
context->visible = FALSE;
}
void
server_context_service_show_keyboard (ServerContextService *context)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(context));
if (!context->visible) {
server_context_service_real_show_keyboard (context);
} }
} }
void void
server_context_service_hide_keyboard (ServerContextService *self) server_context_service_hide_keyboard (ServerContextService *context)
{ {
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self)); g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(context));
if (self->visible) { if (context->visible) {
server_context_service_real_hide_keyboard (self); server_context_service_real_hide_keyboard (context);
}
}
/// 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_keyboard_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);
} }
} }
@ -306,15 +295,26 @@ server_context_service_set_property (GObject *object,
const GValue *value, const GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
ServerContextService *self = SERVER_CONTEXT_SERVICE(object); ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
GVariant *variant;
switch (prop_id) { switch (prop_id) {
case PROP_SIZE_CONSTRAINT_LANDSCAPE:
variant = g_value_get_variant (value);
g_variant_get (variant, "(dd)",
&context->size_constraint_landscape[0],
&context->size_constraint_landscape[1]);
break;
case PROP_SIZE_CONSTRAINT_PORTRAIT:
variant = g_value_get_variant (value);
g_variant_get (variant, "(dd)",
&context->size_constraint_portrait[0],
&context->size_constraint_portrait[1]);
break;
case PROP_VISIBLE: case PROP_VISIBLE:
self->visible = g_value_get_boolean (value); context->visible = g_value_get_boolean (value);
break;
case PROP_ENABLED:
server_context_service_set_enabled (self, g_value_get_boolean (value));
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -327,10 +327,10 @@ server_context_service_get_property (GObject *object,
GValue *value, GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
ServerContextService *self = SERVER_CONTEXT_SERVICE(object); ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
switch (prop_id) { switch (prop_id) {
case PROP_VISIBLE: case PROP_VISIBLE:
g_value_set_boolean (value, self->visible); g_value_set_boolean (value, context->visible);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -341,10 +341,10 @@ server_context_service_get_property (GObject *object,
static void static void
server_context_service_dispose (GObject *object) server_context_service_dispose (GObject *object)
{ {
ServerContextService *self = SERVER_CONTEXT_SERVICE(object); ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
destroy_window (self); destroy_window (context);
self->widget = NULL; context->widget = NULL;
G_OBJECT_CLASS (server_context_service_parent_class)->dispose (object); G_OBJECT_CLASS (server_context_service_parent_class)->dispose (object);
} }
@ -359,6 +359,26 @@ server_context_service_class_init (ServerContextServiceClass *klass)
gobject_class->get_property = server_context_service_get_property; gobject_class->get_property = server_context_service_get_property;
gobject_class->dispose = server_context_service_dispose; gobject_class->dispose = server_context_service_dispose;
pspec = g_param_spec_variant ("size-constraint-landscape",
"Size constraint landscape",
"Size constraint landscape",
G_VARIANT_TYPE("(dd)"),
NULL,
G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_SIZE_CONSTRAINT_LANDSCAPE,
pspec);
pspec = g_param_spec_variant ("size-constraint-portrait",
"Size constraint portrait",
"Size constraint portrait",
G_VARIANT_TYPE("(dd)"),
NULL,
G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_SIZE_CONSTRAINT_PORTRAIT,
pspec);
/** /**
* Flag to indicate if keyboard is visible or not. * Flag to indicate if keyboard is visible or not.
*/ */
@ -370,62 +390,27 @@ server_context_service_class_init (ServerContextServiceClass *klass)
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_VISIBLE, PROP_VISIBLE,
pspec); pspec);
/**
* ServerContextServie:keyboard:
*
* Does the user want the keyboard to show up automatically?
*/
pspec =
g_param_spec_boolean ("enabled",
"Enabled",
"Whether the keyboard is enabled",
TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (gobject_class,
PROP_ENABLED,
pspec);
} }
static void static void
server_context_service_init (ServerContextService *self) { server_context_service_init (ServerContextService *state) {
const char *schema_name = "org.gnome.desktop.a11y.applications"; (void)state;
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
g_autoptr(GSettingsSchema) schema = NULL;
self->enabled = TRUE;
if (!ssrc) {
g_warning("No gsettings schemas installed.");
return;
}
schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE);
if (schema) {
g_autoptr(GSettings) settings = g_settings_new (schema_name);
g_settings_bind (settings, "screen-keyboard-enabled",
self, "enabled", G_SETTINGS_BIND_GET);
} else {
g_warning("Gsettings schema %s is not installed on the system. "
"Enabling by default.", schema_name);
}
} }
ServerContextService * ServerContextService *
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman) server_context_service_new (EekboardContextService *state, struct submission *submission)
{ {
ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL); ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
ui->submission = submission; ui->submission = submission;
ui->state = self; ui->state = state;
ui->layout = layout; g_signal_connect (state,
ui->manager = uiman; "notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ui);
return ui; return ui;
} }
void enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *service)
server_context_service_set_enabled (ServerContextService *self, gboolean enabled)
{ {
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self)); return service->last_type;
self->enabled = enabled;
if (!self->enabled) {
server_context_service_hide_keyboard (self);
}
} }

View File

@ -20,20 +20,26 @@
#include "src/layout.h" #include "src/layout.h"
#include "src/submission.h" #include "src/submission.h"
#include "ui_manager.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define SERVER_TYPE_CONTEXT_SERVICE (server_context_service_get_type()) #define SERVER_TYPE_CONTEXT_SERVICE (server_context_service_get_type())
#define SERVER_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextService))
#define SERVER_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass))
#define SERVER_IS_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SERVER_TYPE_CONTEXT_SERVICE))
#define SERVER_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SERVER_TYPE_CONTEXT_SERVICE))
#define SERVER_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass))
/** Manages the lifecycle of the window displaying layouts. */ /** Manages the lifecycle of the window displaying layouts. */
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject) typedef struct _ServerContextService ServerContextService;
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman); GType server_context_service_get_type
(void) G_GNUC_CONST;
ServerContextService *server_context_service_new(EekboardContextService *state, struct submission *submission);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *); enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
void server_context_service_show_keyboard (ServerContextService *self); void server_context_service_show_keyboard (ServerContextService *context);
void server_context_service_hide_keyboard (ServerContextService *self); void server_context_service_hide_keyboard (ServerContextService *context);
void server_context_service_set_enabled (ServerContextService *self, gboolean enabled);
G_END_DECLS G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */ #endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -28,11 +28,9 @@
#include "eek/eek.h" #include "eek/eek.h"
#include "eekboard/eekboard-context-service.h" #include "eekboard/eekboard-context-service.h"
#include "dbus.h" #include "dbus.h"
#include "layout.h"
#include "outputs.h" #include "outputs.h"
#include "submission.h" #include "submission.h"
#include "server-context-service.h" #include "server-context-service.h"
#include "ui_manager.h"
#include "wayland.h" #include "wayland.h"
#include <gdk/gdkwayland.h> #include <gdk/gdkwayland.h>
@ -40,13 +38,11 @@
/// Global application state /// Global application state
struct squeekboard { struct squeekboard {
struct squeek_wayland wayland; // Just hooks. struct squeek_wayland wayland;
DBusHandler *dbus_handler; // Controls visibility of the OSK. DBusHandler *dbus_handler;
EekboardContextService *settings_context; // Gsettings hooks. EekboardContextService *settings_context;
ServerContextService *ui_context; // mess, includes the entire UI ServerContextService *ui_context;
struct submission *submission; // Wayland text input handling. struct submission *submission;
struct squeek_layout_state layout_choice; // Currently wanted layout.
struct ui_manager *ui_manager; // UI shape tracker/chooser. TODO: merge with layuot choice
}; };
@ -203,9 +199,7 @@ main (int argc, char **argv)
g_warning("Wayland input method interface not available"); g_warning("Wayland input method interface not available");
} }
instance.ui_manager = squeek_uiman_new(); instance.settings_context = eekboard_context_service_new();
instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
// set up dbus // set up dbus
@ -213,13 +207,12 @@ main (int argc, char **argv)
// dbus is not strictly necessary for the useful operation // dbus is not strictly necessary for the useful operation
// if text-input is used, as it can bring the keyboard in and out // if text-input is used, as it can bring the keyboard in and out
GBusType bus_type; GBusType bus_type;
if (opt_system) { if (opt_system)
bus_type = G_BUS_TYPE_SYSTEM; bus_type = G_BUS_TYPE_SYSTEM;
} else if (opt_address) { else if (opt_address)
bus_type = G_BUS_TYPE_NONE; bus_type = G_BUS_TYPE_NONE;
} else { else
bus_type = G_BUS_TYPE_SESSION; bus_type = G_BUS_TYPE_SESSION;
}
GDBusConnection *connection = NULL; GDBusConnection *connection = NULL;
GError *error = NULL; GError *error = NULL;
@ -229,9 +222,9 @@ main (int argc, char **argv)
error = NULL; error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (connection == NULL) { if (connection == NULL) {
g_printerr ("Can't connect to the bus: %s. " g_printerr ("Can't connect to the bus: %s\n", error->message);
"Visibility switching unavailable.", error->message);
g_error_free (error); g_error_free (error);
exit (1);
} }
break; break;
case G_BUS_TYPE_NONE: case G_BUS_TYPE_NONE:
@ -253,28 +246,25 @@ main (int argc, char **argv)
g_assert_not_reached (); g_assert_not_reached ();
break; break;
} }
guint owner_id = 0;
DBusHandler *service = NULL;
if (connection) {
service = dbus_handler_new(connection, DBUS_SERVICE_PATH);
if (service == NULL) { DBusHandler *service = dbus_handler_new(connection, DBUS_SERVICE_PATH);
g_printerr ("Can't create dbus server\n");
exit (1);
}
instance.dbus_handler = service;
owner_id = g_bus_own_name_on_connection (connection, if (service == NULL) {
DBUS_SERVICE_INTERFACE, g_printerr ("Can't create dbus server\n");
G_BUS_NAME_OWNER_FLAGS_NONE, exit (1);
on_name_acquired, }
on_name_lost, instance.dbus_handler = service;
NULL,
NULL); guint owner_id = g_bus_own_name_on_connection (connection,
if (owner_id == 0) { DBUS_SERVICE_INTERFACE,
g_printerr ("Can't own the name\n"); G_BUS_NAME_OWNER_FLAGS_NONE,
exit (1); on_name_acquired,
} on_name_lost,
NULL,
NULL);
if (owner_id == 0) {
g_printerr ("Can't own the name\n");
exit (1);
} }
instance.submission = get_submission(instance.wayland.input_method_manager, instance.submission = get_submission(instance.wayland.input_method_manager,
@ -286,9 +276,7 @@ main (int argc, char **argv)
ServerContextService *ui_context = server_context_service_new( ServerContextService *ui_context = server_context_service_new(
instance.settings_context, instance.settings_context,
instance.submission, instance.submission);
&instance.layout_choice,
instance.ui_manager);
if (!ui_context) { if (!ui_context) {
g_error("Could not initialize GUI"); g_error("Could not initialize GUI");
exit(1); exit(1);
@ -308,15 +296,9 @@ main (int argc, char **argv)
g_main_loop_run (loop); g_main_loop_run (loop);
if (connection) { g_bus_unown_name (owner_id);
if (service) { g_object_unref (service);
if (owner_id != 0) { g_object_unref (connection);
g_bus_unown_name (owner_id);
}
g_object_unref (service);
}
g_object_unref (connection);
}
g_main_loop_unref (loop); g_main_loop_unref (loop);
squeek_wayland_deinit (&instance.wayland); squeek_wayland_deinit (&instance.wayland);

View File

@ -2,6 +2,6 @@
#define __STYLE_H #define __STYLE_H
#include "gtk/gtk.h" #include "gtk/gtk.h"
GtkCssProvider *squeek_load_style(void); GtkCssProvider *squeek_load_style();
#endif #endif

View File

@ -6,7 +6,6 @@
#include "eek/eek-types.h" #include "eek/eek-types.h"
struct submission; struct submission;
struct squeek_layout;
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager, struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
struct zwp_virtual_keyboard_manager_v1 *vkmanager, struct zwp_virtual_keyboard_manager_v1 *vkmanager,
@ -16,5 +15,5 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
// Defined in Rust // Defined in Rust
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state); struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state);
void submission_set_ui(struct submission *self, ServerContextService *ui_context); void submission_set_ui(struct submission *self, ServerContextService *ui_context);
void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time); void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
#endif #endif

View File

@ -17,20 +17,9 @@
* and those events SHOULD NOT cause any lost events. * and those events SHOULD NOT cause any lost events.
* */ * */
use std::collections::HashSet;
use std::ffi::CString;
use ::action::Modifier;
use ::imservice;
use ::imservice::IMService; use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
use ::layout;
use ::util::vec_remove;
use ::vkeyboard;
use ::vkeyboard::VirtualKeyboard; use ::vkeyboard::VirtualKeyboard;
// traits
use std::iter::FromIterator;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
use super::*; use super::*;
@ -38,6 +27,7 @@ pub mod c {
use std::os::raw::c_void; use std::os::raw::c_void;
use ::imservice::c::InputMethod; use ::imservice::c::InputMethod;
use ::layout::c::LevelKeyboard;
use ::vkeyboard::c::ZwpVirtualKeyboardV1; use ::vkeyboard::c::ZwpVirtualKeyboardV1;
// The following defined in C // The following defined in C
@ -66,11 +56,7 @@ pub mod c {
Box::<Submission>::into_raw(Box::new( Box::<Submission>::into_raw(Box::new(
Submission { Submission {
imservice, imservice,
modifiers_active: Vec::new(),
virtual_keyboard: VirtualKeyboard(vk), virtual_keyboard: VirtualKeyboard(vk),
pressed: Vec::new(),
keymap_fds: Vec::new(),
keymap_idx: None,
} }
)) ))
} }
@ -94,257 +80,21 @@ pub mod c {
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn submission_use_layout( fn submission_set_keyboard(submission: *mut Submission, keyboard: LevelKeyboard) {
submission: *mut Submission,
layout: *const layout::Layout,
time: u32,
) {
if submission.is_null() { if submission.is_null() {
panic!("Null submission pointer"); panic!("Null submission pointer");
} }
let submission: &mut Submission = unsafe { &mut *submission }; let submission: &mut Submission = unsafe { &mut *submission };
let layout = unsafe { &*layout }; submission.virtual_keyboard.update_keymap(keyboard);
submission.use_layout(layout, Timestamp(time));
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Timestamp(pub u32); pub struct Timestamp(pub u32);
#[derive(Clone)]
enum SubmittedAction {
/// A collection of keycodes that were pressed
VirtualKeyboard(Vec<KeyCode>),
IMService,
}
pub struct Submission { pub struct Submission {
// used by C callbacks internally, TODO: make use with virtual keyboard
#[allow(dead_code)]
imservice: Option<Box<IMService>>, imservice: Option<Box<IMService>>,
virtual_keyboard: VirtualKeyboard, pub virtual_keyboard: VirtualKeyboard,
modifiers_active: Vec<(KeyStateId, Modifier)>,
pressed: Vec<(KeyStateId, SubmittedAction)>,
keymap_fds: Vec<vkeyboard::c::KeyMap>,
keymap_idx: Option<usize>,
}
pub enum SubmitData<'a> {
Text(&'a CString),
Erase,
Keycodes,
}
impl Submission {
/// Sends a submit text event if possible;
/// otherwise sends key press and makes a note of it
pub fn handle_press(
&mut self,
key_id: KeyStateId,
data: SubmitData,
keycodes: &Vec<KeyCode>,
time: Timestamp,
) {
let mods_are_on = !self.modifiers_active.is_empty();
let was_committed_as_text = match (&mut self.imservice, mods_are_on) {
(Some(imservice), false) => {
enum Outcome {
Submitted(Result<(), imservice::SubmitError>),
NotSubmitted,
};
let submit_outcome = match data {
SubmitData::Text(text) => {
Outcome::Submitted(imservice.commit_string(text))
},
SubmitData::Erase => {
/* Delete_surrounding_text takes byte offsets,
* so cannot work without get_surrounding_text.
* This is a bug in the protocol.
*/
// imservice.delete_surrounding_text(1, 0),
Outcome::NotSubmitted
},
SubmitData::Keycodes => Outcome::NotSubmitted,
};
match submit_outcome {
Outcome::Submitted(result) => {
match result.and_then(|()| imservice.commit()) {
Ok(()) => true,
Err(imservice::SubmitError::NotActive) => false,
}
},
Outcome::NotSubmitted => false,
}
},
(_, _) => false,
};
let submit_action = match was_committed_as_text {
true => SubmittedAction::IMService,
false => {
let keycodes_count = keycodes.len();
for keycode in keycodes.iter() {
self.select_keymap(keycode.keymap_idx, time);
let keycode = keycode.code;
match keycodes_count {
// Pressing a key made out of a single keycode is simple:
// press on press, release on release.
1 => self.virtual_keyboard.switch(
keycode,
PressType::Pressed,
time,
),
// A key made of multiple keycodes
// has to submit them one after the other.
_ => {
self.virtual_keyboard.switch(
keycode.clone(),
PressType::Pressed,
time,
);
self.virtual_keyboard.switch(
keycode.clone(),
PressType::Released,
time,
);
},
};
}
SubmittedAction::VirtualKeyboard(keycodes.clone())
},
};
self.pressed.push((key_id, submit_action));
}
pub fn handle_release(&mut self, key_id: KeyStateId, time: Timestamp) {
let index = self.pressed.iter().position(|(id, _)| *id == key_id);
if let Some(index) = index {
let (_id, action) = self.pressed.remove(index);
match action {
// string already sent, nothing to do
SubmittedAction::IMService => {},
// no matter if the imservice got activated,
// keys must be released
SubmittedAction::VirtualKeyboard(keycodes) => {
let keycodes_count = keycodes.len();
match keycodes_count {
1 => {
let keycode = &keycodes[0];
self.select_keymap(keycode.keymap_idx, time);
self.virtual_keyboard.switch(
keycode.code,
PressType::Released,
time,
);
},
// Design choice here: submit multiple all at press time
// and do nothing at release time.
_ => {},
};
},
}
};
}
pub fn handle_add_modifier(
&mut self,
key_id: KeyStateId,
modifier: Modifier, _time: Timestamp,
) {
self.modifiers_active.push((key_id, modifier));
self.update_modifiers();
}
pub fn handle_drop_modifier(
&mut self,
key_id: KeyStateId,
_time: Timestamp,
) {
vec_remove(&mut self.modifiers_active, |(id, _)| *id == key_id);
self.update_modifiers();
}
fn update_modifiers(&mut self) {
let raw_modifiers = self.modifiers_active.iter()
.map(|(_id, m)| match m {
Modifier::Control => Modifiers::CONTROL,
Modifier::Alt => Modifiers::MOD1,
})
.fold(Modifiers::empty(), |m, n| m | n);
self.virtual_keyboard.set_modifiers_state(raw_modifiers);
}
pub fn is_modifier_active(&self, modifier: Modifier) -> bool {
self.modifiers_active.iter()
.position(|(_id, m)| *m == modifier)
.is_some()
}
pub fn get_active_modifiers(&self) -> HashSet<Modifier> {
HashSet::from_iter(
self.modifiers_active.iter().map(|(_id, m)| m.clone())
)
}
fn clear_all_modifiers(&mut self) {
// Looks like an optimization,
// but preemptive cleaning is needed before setting a new keymap,
// so removing this check would break keymap setting.
if self.modifiers_active.is_empty() {
return;
}
self.modifiers_active = Vec::new();
self.virtual_keyboard.set_modifiers_state(Modifiers::empty())
}
fn release_all_virtual_keys(&mut self, time: Timestamp) {
let virtual_pressed = self.pressed
.clone().into_iter()
.filter_map(|(id, action)| {
match action {
SubmittedAction::VirtualKeyboard(_) => Some(id),
_ => None,
}
});
for id in virtual_pressed {
self.handle_release(id, time);
}
}
/// Changes keymap and clears pressed keys and modifiers.
///
/// It's not obvious if clearing is the right thing to do,
/// but keymap update may (or may not) do that,
/// possibly putting self.modifiers_active and self.pressed out of sync,
/// so a consistent stance is adopted to avoid that.
/// Alternatively, modifiers could be restored on the new keymap.
/// That approach might be difficult
/// due to modifiers meaning different things in different keymaps.
fn select_keymap(&mut self, idx: usize, time: Timestamp) {
if self.keymap_idx != Some(idx) {
self.keymap_idx = Some(idx);
self.clear_all_modifiers();
self.release_all_virtual_keys(time);
let keymap = &self.keymap_fds[idx];
self.virtual_keyboard.update_keymap(keymap);
}
}
pub fn use_layout(&mut self, layout: &layout::Layout, time: Timestamp) {
self.keymap_fds = layout.keymaps.iter()
.map(|keymap_str| vkeyboard::c::KeyMap::from_cstr(
keymap_str.as_c_str()
))
.collect();
self.keymap_idx = None;
// This can probably be eliminated,
// because key presses can trigger an update anyway.
// However, self.keymap_idx needs to become Option<>
// in order to force update on new layouts.
self.select_keymap(0, time);
}
} }

View File

@ -26,104 +26,49 @@ impl CountAndPrint {
} }
} }
pub fn check_builtin_layout(name: &str, missing_return: bool) { pub fn check_builtin_layout(name: &str) {
check_layout( check_layout(Layout::from_resource(name).expect("Invalid layout data"))
Layout::from_resource(name).expect("Invalid layout data"),
missing_return,
)
} }
pub fn check_layout_file(path: &str) { pub fn check_layout_file(path: &str) {
check_layout( check_layout(Layout::from_file(path.into()).expect("Invalid layout file"))
Layout::from_file(path.into()).expect("Invalid layout file"),
false,
)
} }
fn check_sym_in_keymap(state: &xkb::State, sym_name: &str) -> bool { fn check_layout(layout: Layout) {
let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
if sym == xkb::KEY_NoSymbol {
panic!(format!("Entered invalid keysym: {}", sym_name));
}
let map = state.get_keymap();
let range = map.min_keycode()..=map.max_keycode();
range.flat_map(|code| state.key_get_syms(code))
.find(|s| **s == sym)
.is_some()
}
fn check_sym_presence(
states: &[xkb::State],
sym_name: &str,
handler: &mut dyn logging::Handler,
) {
let found = states.iter()
.position(|state| {
check_sym_in_keymap(&state, sym_name)
});
if let None = found {
handler.handle(
logging::Level::Surprise,
&format!("There's no way to input the keysym {} on this layout", sym_name),
)
}
}
fn check_layout(layout: Layout, allow_missing_return: bool) {
let handler = CountAndPrint::new(); let handler = CountAndPrint::new();
let (layout, mut handler) = layout.build(handler); let (layout, handler) = layout.build(handler);
if handler.0 > 0 { if handler.0 > 0 {
println!("{} problems while parsing layout", handler.0) println!("{} problems while parsing layout", handler.0)
} }
let layout = layout.expect("layout broken"); let layout = layout.expect("layout broken");
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let xkb_states: Vec<xkb::State> = layout.keymaps.iter() let keymap_str = layout.keymap_str
.map(|keymap_str| { .clone()
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); .into_string().expect("Failed to decode keymap string");
let keymap_str = keymap_str
.clone() let keymap = xkb::Keymap::new_from_string(
.into_string().expect("Failed to decode keymap string"); &context,
let keymap = xkb::Keymap::new_from_string( keymap_str.clone(),
&context, xkb::KEYMAP_FORMAT_TEXT_V1,
keymap_str.clone(), xkb::KEYMAP_COMPILE_NO_FLAGS,
xkb::KEYMAP_FORMAT_TEXT_V1, ).expect("Failed to create keymap");
xkb::KEYMAP_COMPILE_NO_FLAGS,
).expect("Failed to create keymap");
xkb::State::new(&keymap)
})
.collect();
check_sym_presence(&xkb_states, "BackSpace", &mut handler);
let mut printer = logging::Print;
check_sym_presence(
&xkb_states,
"Return",
if allow_missing_return { &mut printer }
else { &mut handler },
);
let state = xkb::State::new(&keymap);
// "Press" each button with keysyms // "Press" each button with keysyms
for (_pos, view) in layout.views.values() { for view in layout.views.values() {
for (_y, row) in view.get_rows() { for (_y, row) in &view.get_rows() {
for (_x, button) in row.get_buttons() { for (_x, button) in &row.buttons {
let keystate = button.state.borrow(); let keystate = button.state.borrow();
for keycode in &keystate.keycodes { for keycode in &keystate.keycodes {
match xkb_states[keycode.keymap_idx].key_get_one_sym(keycode.code) { match state.key_get_one_sym(*keycode) {
xkb::KEY_NoSymbol => { xkb::KEY_NoSymbol => {
eprintln!( eprintln!("{}", keymap_str);
"keymap {}: {}", panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name);
keycode.keymap_idx,
layout.keymaps[keycode.keymap_idx].to_str().unwrap(),
);
panic!(
"Keysym for code {:?} on key {} ({:?}) can't be resolved",
keycode,
button.name.to_string_lossy(),
button.name,
);
}, },
_ => {}, _ => {},
} }

View File

@ -1,14 +0,0 @@
#ifndef UI_MANAGER__
#define UI_MANAGER__
#include <inttypes.h>
#include "outputs.h"
struct ui_manager;
struct ui_manager *squeek_uiman_new(void);
void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output);
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
#endif

View File

@ -1,81 +0,0 @@
/* Copyright (C) 2020 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Centrally manages the shape of the UI widgets, and the choice of layout.
*
* Coordinates this based on information collated from all possible sources.
*/
use std::cmp::min;
use ::outputs::c::OutputHandle;
mod c {
use super::*;
use ::util::c::Wrapped;
#[no_mangle]
pub extern "C"
fn squeek_uiman_new() -> Wrapped<Manager> {
Wrapped::new(Manager { output: None })
}
/// Used to size the layer surface containing all the OSK widgets.
#[no_mangle]
pub extern "C"
fn squeek_uiman_get_perceptual_height(
uiman: Wrapped<Manager>,
) -> u32 {
let uiman = uiman.clone_ref();
let uiman = uiman.borrow();
// TODO: what to do when there's no output?
uiman.get_perceptual_height().unwrap_or(0)
}
#[no_mangle]
pub extern "C"
fn squeek_uiman_set_output(
uiman: Wrapped<Manager>,
output: OutputHandle,
) {
let uiman = uiman.clone_ref();
let mut uiman = uiman.borrow_mut();
uiman.output = Some(output);
}
}
/// Stores current state of all things influencing what the UI should look like.
pub struct Manager {
/// Shared output handle, current state updated whenever it's needed.
// TODO: Stop assuming that the output never changes.
// (There's no way for the output manager to update the ui manager.)
// FIXME: Turn into an OutputState and apply relevant connections elsewhere.
// Otherwise testability and predictablity is low.
output: Option<OutputHandle>,
//// Pixel size of the surface. Needs explicit updating.
//surface_size: Option<Size>,
}
impl Manager {
fn get_perceptual_height(&self) -> Option<u32> {
let output_info = (&self.output).as_ref()
.and_then(|o| o.get_state())
.map(|os| (os.scale as u32, os.get_pixel_size()));
match output_info {
Some((scale, Some(px_size))) => Some({
let height = if (px_size.width < 720) & (px_size.width > 0) {
px_size.width * 7 / 12 // to match 360×210
} else if px_size.width < 1080 {
360 + (1080 - px_size.width) * 60 / 360 // smooth transition
} else {
360
};
// Don't exceed half the display size
min(height, px_size.height / 2) / scale
}),
Some((scale, None)) => Some(360 / scale),
None => None,
}
}
}

View File

@ -98,8 +98,7 @@ pub mod c {
Rc::from_raw(self.0) Rc::from_raw(self.0)
} }
/// Creates a new Rc reference to the same data. /// Creates a new Rc reference to the same data
/// Use for accessing the underlying data as a reference.
pub fn clone_ref(&self) -> Rc<RefCell<T>> { pub fn clone_ref(&self) -> Rc<RefCell<T>> {
// A bit dangerous: the Rc may be in use elsewhere // A bit dangerous: the Rc may be in use elsewhere
let used_rc = unsafe { Rc::from_raw(self.0) }; let used_rc = unsafe { Rc::from_raw(self.0) };
@ -131,7 +130,6 @@ pub mod c {
impl<T> COpaquePtr for Wrapped<T> {} impl<T> COpaquePtr for Wrapped<T> {}
} }
/// Clones the underlying data structure, like ToOwned.
pub trait CloneOwned { pub trait CloneOwned {
type Owned; type Owned;
fn clone_owned(&self) -> Self::Owned; fn clone_owned(&self) -> Self::Owned;
@ -197,29 +195,6 @@ pub trait WarningHandler {
fn handle(&mut self, warning: &str); fn handle(&mut self, warning: &str);
} }
/// Removes the first matcing item
pub fn vec_remove<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, pred: F) -> Option<T> {
let idx = v.iter().position(pred);
idx.map(|idx| v.remove(idx))
}
/// Repeats all the items of the iterator forever,
/// but returns the cycle number alongside.
/// Inefficient due to all the vectors, but doesn't have to be fast.
pub fn cycle_count<T, I: Clone + Iterator<Item=T>>(iter: I)
-> impl Iterator<Item=(T, usize)>
{
let numbered_copies = vec![iter].into_iter()
.cycle()
.enumerate();
numbered_copies.flat_map(|(idx, cycle)|
// Pair each element from the cycle with a copy of the index.
cycle.zip(
vec![idx].into_iter().cycle() // Repeat the index forever.
)
)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -234,12 +209,4 @@ mod tests {
assert_eq!(s.insert(Pointer(Rc::new(2u32))), true); assert_eq!(s.insert(Pointer(Rc::new(2u32))), true);
assert_eq!(s.remove(&Pointer(first)), true); assert_eq!(s.remove(&Pointer(first)), true);
} }
#[test]
fn check_count() {
assert_eq!(
cycle_count(5..8).take(7).collect::<Vec<_>>(),
vec![(5, 0), (6, 0), (7, 0), (5, 1), (6, 1), (7, 1), (5, 2)]
);
}
} }

View File

@ -1,47 +1,20 @@
/*! Managing the events belonging to virtual-keyboard interface. */ /*! Managing the events belonging to virtual-keyboard interface. */
use ::keyboard::{ Modifiers, PressType }; use ::keyboard::{ KeyCode, PressType };
use ::layout::c::LevelKeyboard;
use ::submission::Timestamp; use ::submission::Timestamp;
/// Standard xkb keycode
type KeyCode = u32;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
use std::ffi::CStr; use super::*;
use std::os::raw::{ c_char, c_void }; use std::os::raw::c_void;
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct ZwpVirtualKeyboardV1(*const c_void); pub struct ZwpVirtualKeyboardV1(*const c_void);
#[repr(C)]
pub struct KeyMap {
fd: u32,
fd_len: usize,
}
impl KeyMap {
pub fn from_cstr(s: &CStr) -> KeyMap {
unsafe {
squeek_key_map_from_str(s.as_ptr())
}
}
}
impl Drop for KeyMap {
fn drop(&mut self) {
unsafe {
close(self.fd as u32);
}
}
}
#[no_mangle] #[no_mangle]
extern "C" { extern "C" {
// From libc, to let KeyMap get deallocated.
fn close(fd: u32);
pub fn eek_virtual_keyboard_v1_key( pub fn eek_virtual_keyboard_v1_key(
virtual_keyboard: ZwpVirtualKeyboardV1, virtual_keyboard: ZwpVirtualKeyboardV1,
timestamp: u32, timestamp: u32,
@ -51,15 +24,8 @@ pub mod c {
pub fn eek_virtual_keyboard_update_keymap( pub fn eek_virtual_keyboard_update_keymap(
virtual_keyboard: ZwpVirtualKeyboardV1, virtual_keyboard: ZwpVirtualKeyboardV1,
keymap: *const KeyMap, keyboard: LevelKeyboard,
); );
pub fn eek_virtual_keyboard_set_modifiers(
virtual_keyboard: ZwpVirtualKeyboardV1,
modifiers: u32,
);
pub fn squeek_key_map_from_str(keymap_str: *const c_char) -> KeyMap;
} }
} }
@ -67,33 +33,46 @@ pub mod c {
pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1); pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1);
impl VirtualKeyboard { impl VirtualKeyboard {
// TODO: error out if keymap not set // TODO: split out keyboard state management
pub fn switch( pub fn switch(
&self, &self,
keycode: KeyCode, keycodes: &Vec<KeyCode>,
action: PressType, action: PressType,
timestamp: Timestamp, timestamp: Timestamp,
) { ) {
let keycode = keycode - 8; let keycodes_count = keycodes.len();
unsafe { for keycode in keycodes.iter() {
c::eek_virtual_keyboard_v1_key( let keycode = keycode - 8;
self.0, timestamp.0, keycode, action.clone() as u32 match (action, keycodes_count) {
); // Pressing a key made out of a single keycode is simple:
// press on press, release on release.
(_, 1) => unsafe {
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, action.clone() as u32
);
},
// A key made of multiple keycodes
// has to submit them one after the other
(PressType::Pressed, _) => unsafe {
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, PressType::Pressed as u32
);
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, PressType::Released as u32
);
},
// Design choice here: submit multiple all at press time
// and do nothing at release time
(PressType::Released, _) => {},
}
} }
} }
pub fn set_modifiers_state(&self, modifiers: Modifiers) { pub fn update_keymap(&self, keyboard: LevelKeyboard) {
let modifiers = modifiers.bits() as u32;
unsafe {
c::eek_virtual_keyboard_set_modifiers(self.0, modifiers);
}
}
pub fn update_keymap(&self, keymap: &c::KeyMap) {
unsafe { unsafe {
c::eek_virtual_keyboard_update_keymap( c::eek_virtual_keyboard_update_keymap(
self.0, self.0,
keymap as *const c::KeyMap, keyboard,
); );
} }
} }

View File

@ -14,16 +14,10 @@ eek_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard
} }
void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, struct keymap *keymap) { void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, const LevelKeyboard *keyboard) {
zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1, zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keymap->fd, keymap->fd_len); keyboard->keymap_fd, keyboard->keymap_len);
}
void
eek_virtual_keyboard_set_modifiers(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t mods_depressed) {
zwp_virtual_keyboard_v1_modifiers(zwp_virtual_keyboard_v1,
mods_depressed, 0, 0, 0);
} }
int squeek_output_add_listener(struct wl_output *wl_output, int squeek_output_add_listener(struct wl_output *wl_output,

View File

@ -1,10 +0,0 @@
---
# Erase only
views:
base:
- "BackSpace"
outlines:
default: { width: 0, height: 0 }
buttons:
BackSpace:
action: erase

View File

@ -49,39 +49,23 @@ endforeach
# and the need to call it manually # and the need to call it manually
foreach layout : [ foreach layout : [
'us', 'us_wide', 'us', 'us_wide',
'br',
'be', 'be_wide',
'de', 'de_wide', 'de', 'de_wide',
'dk', 'el',
'es', 'es',
'fi', 'fi',
'fr', 'fr_wide',
'it+fur',
'gr',
'it', 'it',
'jp+kana','jp+kana_wide', 'jp+kana','jp+kana_wide',
'no', 'no',
'number', 'number',
'pl', 'pl_wide',
'ru',
'se', 'se',
'ua', 'terminal',
'terminal', 'terminal_wide',
'emoji', 'emoji',
] ]
extra = []
if layout == 'emoji'
extra += ['allow_missing_return']
endif
test( test(
'test_layout_' + layout, 'test_layout_' + layout,
cargo_script, cargo_script,
args: ['run'] + cargo_build_flags args: ['run', '--example', 'test_layout', layout]
+ ['--example', 'test_layout', '--', layout]
+ extra,
workdir: meson.build_root(),
) )
endforeach endforeach

View File

@ -12,9 +12,8 @@ test_layout = custom_target('squeekboard-test-layout',
build_always_stale: true, build_always_stale: true,
output: ['squeekboard-test-layout'], output: ['squeekboard-test-layout'],
console: true, console: true,
command: [cargo_build, '--rename', 'test_layout', '@OUTPUT@', '--bin', 'test_layout'] command: [cargo_build] + cargo_build_flags
+ cargo_build_flags, + ['--rename', 'test_layout', '@OUTPUT@', '--bin', 'test_layout'],
install: true, install: true,
install_dir: bindir, install_dir: bindir,
depends: cargo_toml,
) )