Compare commits

..

3 Commits

117 changed files with 2949 additions and 5395 deletions

View File

@ -1,4 +1,4 @@
image: debian:bullseye image: debian:buster
stages: stages:
- build - build
@ -11,110 +11,29 @@ 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
- apt-get -y build-dep .
build_docs:
<<: *tags
stage: build
artifacts:
paths:
- _build
script:
- apt-get -y install python3-pip python3-sphinx
- pip3 install recommonmark
- ./doc/build.sh _build
build_meson: build_meson:
tags: <<: *tags
- librem5
stage: build stage: build
artifacts: artifacts:
paths: paths:
- _build - _build
expire_in: 3h expire_in: 3h
script: script:
- apt-get -y build-dep . - meson . _build/ -Ddepdatadir=/usr/share
- meson . _build/ -Ddepdatadir=/usr/share --werror
- ninja -C _build install - ninja -C _build install
build_deb: build_deb:
tags: <<: *tags
- librem5
stage: build stage: build
artifacts: artifacts:
paths: paths:
- "*.deb" - "*.deb"
script: script:
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:amber:
image: pureos/amber
tags:
- librem5
stage: build
artifacts:
paths:
- "*.deb"
script:
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- apt-get -y update
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:buster:
image: "debian:buster"
tags:
- librem5
stage: build
artifacts:
paths:
- "*.deb"
script:
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- apt-get -y update
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:arm64:
tags:
- librem5:arm64
stage: build
artifacts:
paths:
- "*.deb"
script:
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
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 - apt-get -y install devscripts
- debuild -i -us -uc -b - debuild -i -us -uc -b
- cp ../*.deb . - cp ../*.deb .
@ -129,21 +48,9 @@ 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 .
- ninja -C _build test - ninja -C _build test
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"]

469
Cargo.lock generated
View File

@ -1,75 +1,93 @@
# 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.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"atk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ]
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", [[package]]
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", name = "ansi_term"
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[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"
dependencies = [ dependencies = [
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (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 = "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"
[[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"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[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"
dependencies = [ dependencies = [
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.16 (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.54" version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.1" version = "2.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "0.4.5" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -79,162 +97,160 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[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"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "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"
dependencies = [ dependencies = [
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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"
dependencies = [ dependencies = [
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (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"
dependencies = [ dependencies = [
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (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"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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"
dependencies = [ dependencies = [
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (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"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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"
dependencies = [ dependencies = [
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (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"
dependencies = [ dependencies = [
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (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"
dependencies = [ dependencies = [
"atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", "gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[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"
dependencies = [ dependencies = [
"atk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -244,12 +260,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.71" version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[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"
[[package]] [[package]]
@ -257,48 +273,53 @@ 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"
[[package]]
name = "memchr"
version = "2.2.1"
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"
dependencies = [ dependencies = [
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (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 = "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"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "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"
dependencies = [ dependencies = [
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.17" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.18" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -306,93 +327,110 @@ dependencies = [
[[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"
dependencies = [ dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.4 (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"
dependencies = [ dependencies = [
"regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.12 (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.18" version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rs" name = "rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
"xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.111" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.111" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.8.13" version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.31" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "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"
dependencies = [ dependencies = [
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "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.7" version = "0.1.7"
@ -403,6 +441,16 @@ name = "unicode-xid"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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"
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.8" version = "0.3.8"
@ -427,60 +475,67 @@ 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"
dependencies = [ dependencies = [
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "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"
dependencies = [ dependencies = [
"linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[metadata] [metadata]
"checksum atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86b7499272acf036bb5820c6e346bbfb5acc5dceb104bc2c4fd7e6e33dfcde6a" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum atk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e552c1776737a4c80110d06b36d099f47c727335f9aaa5d942a72b6863a8ec6f" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7017e53393e713212aed7aea336b6553be4927f58c37070a56c2fe3d107e489"
"checksum cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e05db47de3b0f09a222fa4bba2eab957d920d4243962a86b2d77ab401e4a359c" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff65ba02cac715be836f63429ab00a767d48336efc5497c5637afb53b4f14d63" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" "checksum cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd940f0d609699e343ef71c4af5f66423afbf30d666f796dabd8fd15229cf5b6"
"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" "checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2"
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9" "checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6243e995f41f3a61a31847e54cc719edce93dd9140c89dca3b9919be1cfe22d5" "checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96"
"checksum gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9726408ee1bbada83094326a99b9c68fea275f9dbb515de242a69e72051f4fcc" "checksum gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc3aa730cb4df3de5d9fed59f43afdf9e5fb2d3d10bfcbd04cec031435ce87f5"
"checksum gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d8991b060a9e9161bafd09bf4a202e6fd404f5b4dd1a08d53a1e84256fb34ab0" "checksum gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08284f16ce4d909b10d785a763ba190e222d2c1557b29908bf0a661e27a8ac3b"
"checksum gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6adf679e91d1bff0c06860287f80403e7db54c2d2424dce0a470023b56c88fbb" "checksum gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "108548ebf5329b551f2b97ab356908d14627905abb74b936c3372de1535aee81"
"checksum gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6261b5d34c30c2d59f879e643704cf54cb44731f3a2038000b68790c03e360e3" "checksum gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "29a44b051990573448edc80b1995237f8b97b5734d2aec05105b9242aa10af11"
"checksum gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4fad225242b9eae7ec8a063bb86974aca56885014672375e5775dc0ea3533911" "checksum gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6975ada29f7924dc1c90b30ed3b32d777805a275556c05e420da4fbdc22eb250"
"checksum glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "be27232841baa43e0fd5ae003f7941925735b2f733a336dc75f07b9eff415e7b" "checksum glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a333edf5b9f1411c246ef14e7881b087255f04c56dbef48c64a0cb039b4b340"
"checksum glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2" "checksum glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3573351e846caed9f11207b275cd67bc07f0c2c94fb628e5d7c92ca056c7882d"
"checksum gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9" "checksum gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08475e4a08f27e6e2287005950114735ed61cec2cb8c1187682a5aec8c69b715"
"checksum gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "709f1074259d4685b96133f92b75c7f35b504715b0fcdc96ec95de2607296a60" "checksum gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a6b30f194f09a17bb7ffa95c3ecdb405abd3b75ff981f831b1f6d18fe115ff"
"checksum gtk-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53def660c7b48b00b510c81ef2d2fbd3c570f1527081d8d7947f471513e1a4c1" "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 lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" "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 maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393fa071b144f8ffb83ede273758983cf414ca3c0b1d2a5a9ce325b3ba3dd786" "checksum pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2cb169402a3eb1ba034a7cc7d95b8b1c106e9be5ba4be79a5a93dc1a2795f4"
"checksum pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86b93d84907b3cf0819bff8f13598ba72843bee579d5ebc2502e4b0367b4be7d" "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 pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea"
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" "checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" "checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad"
"checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" "checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
"checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" "checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
"checksum serde_yaml 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5" "checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582"
"checksum syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
"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-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 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 vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "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-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 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 xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fda0ea5f7ddabd51deeeda7799bee06274112f577da7dd3d954b8eda731b2fce"
"checksum yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" "checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"

View File

@ -2,39 +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 = "2.32.*"
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 = []
# 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,42 +3,10 @@ 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.
Nonwithstanding 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 behaviour,
- 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 useable,
- 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 benefitting the user compared to those benefitting 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
----------------------- -----------------------
@ -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][] file.
Testing Testing
------- -------
@ -71,7 +40,7 @@ Most common testing is done in CI. Occasionally, and for each release, do perfor
Testing with an application: Testing with an application:
``` ```
python3 tools/entry.py python3 tests/entry.py
``` ```
Testing visibility: Testing visibility:
@ -93,24 +62,6 @@ $ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb',
Coding Coding
------ ------
### Project structure
Rust modules should be split into 2 categories: libraries, and user interface. They differ in the way they do error handling.
Libraries should:
- not panic due to external surprises, only due to internal inconsistencies
- pass errors and surprises they can't handle to the callers instead
- not silence errors and surprises
User interface modules should:
- try to provide safe values whenever they encounter an error
- do the logging
- give libraries the ability to report errors and surprises (e.g. via giving them loggers)
### Style
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 - skipping brackets `{}` after every `if()`, `else`, and similar
@ -171,15 +122,12 @@ sh /source_path/cargo.sh test
### Cargo dependencies ### Cargo dependencies
All Cargo dependencies must be selected in the version available in PureOS, and added to the file `debian/control`. Please check with https://software.pureos.net/search_pkg?term=librust .
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

@ -56,4 +56,4 @@ $ src/squeekboard
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,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

@ -1,11 +1,18 @@
# Maintained by: Mark Müller <markmueller86@gmail.com> # Maintained by: Mark Müller <markmueller86@gmail.com>
--- ---
bounds: { x: 0, y: 1, width: 360, height: 208 }
outlines: outlines:
default: { width: 35.33, height: 52 } default:
altline: { width: 52.67, height: 52 } bounds: { x: 0, y: 0, width: 35.33, height: 52 }
wide: { width: 62, height: 52 } altline:
spaceline: { width: 99.67, height: 52 } bounds: { x: 0, y: 0, width: 52.67, height: 52 }
special: { width: 35.33, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 99.67, height: 52 }
special:
bounds: { x: 0, y: 0, width: 35.33, height: 52 }
views: views:
base: base:
@ -45,7 +52,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,11 +1,18 @@
# Maintained by: Mark Müller <markmueller86@gmail.com> # Maintained by: Mark Müller <markmueller86@gmail.com>
--- ---
bounds: { x: 0, y: 1, width: 540, height: 168 }
outlines: outlines:
default: { width: 48, height: 42 } default:
altline: { width: 81, height: 42 } bounds: { x: 0, y: 0, width: 48, height: 42 }
wide: { width: 108, height: 42 } altline:
spaceline: { width: 216, height: 42 } bounds: { x: 0, y: 0, width: 81, height: 42 }
special: { width: 48, height: 42 } wide:
bounds: { x: 0, y: 0, width: 108, height: 42 }
spaceline:
bounds: { x: 0, y: 0, width: 216, height: 42 }
special:
bounds: { x: 0, y: 0, width: 48, height: 42 }
views: views:
base: base:
@ -45,7 +52,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

@ -2,12 +2,19 @@
# University of the Aegean, Department of Mathematics, atsol@aegean.gr # University of the Aegean, Department of Mathematics, atsol@aegean.gr
# Sep 2019 # Sep 2019
--- ---
bounds: { x: 0, y: 0.33, width: 360, height: 210 }
outlines: outlines:
default: { width: 32, height: 52 } default:
altline: { width: 48.39024, height: 52 } bounds: { x: 0, y: 0, width: 32, height: 52 }
wide: { width: 62, height: 52 } altline:
outline7: { width: 88.97561, height: 52 } bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
spaceline: { width: 150.5853, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
outline7:
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
views: views:
base: base:
@ -46,7 +53,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 +0,0 @@
---
outlines:
default: { width: 52, height: 52 }
altline: { width: 40, height: 52 }
narrow: { width: 22, height: 52 }
views:
base:
- "😀 😁 😅 😂 😊 😇 🙃"
- "😍 😘 😋 😜 😎 🥳 😔"
- "😢 😭 😡 😱 🤔 😬 🙄"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
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:
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:
action: "show_prefs"
outline: "altline"
icon: "keyboard-mode-symbolic"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
keysym: BackSpace
blank:
outline: "narrow"
text: ""

View File

@ -1,10 +1,17 @@
--- ---
bounds: { x: 0, y: 1, width: 360, height: 210 }
outlines: outlines:
default: { width: 35.33, height: 52 } default:
altline: { width: 52.67, height: 52 } bounds: { x: 0, y: 0, width: 35.33, height: 52 }
wide: { width: 62, height: 52 } altline:
spaceline: { width: 99.67, height: 52 } bounds: { x: 0, y: 0, width: 52.67, height: 52 }
special: { width: 44, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 99.67, height: 52 }
special:
bounds: { x: 0, y: 0, width: 44, height: 52 }
views: views:
base: base:
@ -44,7 +51,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

@ -1,10 +1,17 @@
--- ---
bounds: { x: 0, y: 0.33, width: 360, height: 210 }
outlines: outlines:
default: { width: 32, height: 52 } default:
altline: { width: 48.39024, height: 52 } bounds: { x: 0, y: 0, width: 32, height: 52 }
wide: { width: 62, height: 52 } altline:
outline7: { width: 88.97561, height: 52 } bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
spaceline: { width: 150.5853, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
outline7:
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
views: views:
base: base:
@ -39,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,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,34 +1,41 @@
# Italian layout created by Antonio Pandolfo # Italian layout created by Antonio Pandolfo
# 03 october 2019 # 03 october 2019
--- ---
bounds: { x: 0, y: 1, width: 360, height: 210 }
outlines: outlines:
default: { width: 35.33, height: 52 } default:
altline: { width: 52.67, height: 52 } bounds: { x: 0, y: 0, width: 35.33, height: 52 }
wide: { width: 62, height: 52 } altline:
spaceline: { width: 99.67, height: 52 } bounds: { x: 0, y: 0, width: 52.67, height: 52 }
special: { width: 44, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 99.67, height: 52 }
special:
bounds: { x: 0, y: 0, width: 44, height: 52 }
views: views:
base: base:
- "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 +53,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 +83,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

@ -1,11 +1,18 @@
# Maintained by: Mark Müller <markmueller86@gmail.com> # Maintained by: Mark Müller <markmueller86@gmail.com>
--- ---
bounds: { x: 0, y: 1, width: 360, height: 208 }
outlines: outlines:
default: { width: 62, height: 52 } default:
default-wide: { width: 62, height: 52 } bounds: { x: 0, y: 0, width: 62, height: 52 }
altline: { width: 62, height: 52 } default-wide:
wide: { width: 62, height: 52 } bounds: { x: 0, y: 0, width: 62, height: 52 }
special: { width: 62, height: 52 } altline:
bounds: { x: 0, y: 0, width: 62, height: 52 }
wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
special:
bounds: { x: 0, y: 0, width: 62, height: 52 }
views: views:
base: # hiragana base: # hiragana
@ -195,7 +202,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

@ -1,11 +1,18 @@
# Maintained by: Mark Müller <markmueller86@gmail.com> # Maintained by: Mark Müller <markmueller86@gmail.com>
--- ---
bounds: { x: 0, y: 1, width: 540, height: 168 }
outlines: outlines:
default: { width: 62, height: 42 } default:
default-wide: { width: 62, height: 42 } bounds: { x: 0, y: 0, width: 62, height: 42 }
altline: { width: 62, height: 42 } default-wide:
wide: { width: 62, height: 42 } bounds: { x: 0, y: 0, width: 62, height: 42 }
special: { width: 62, height: 42 } altline:
bounds: { x: 0, y: 0, width: 62, height: 42 }
wide:
bounds: { x: 0, y: 0, width: 62, height: 42 }
special:
bounds: { x: 0, y: 0, width: 62, height: 42 }
views: views:
base: # hiragana base: # hiragana
@ -195,7 +202,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

@ -1,10 +1,17 @@
--- ---
bounds: { x: 0, y: 0.33, width: 360, height: 210 }
outlines: outlines:
default: { width: 32, height: 52 } default:
altline: { width: 48.39024, height: 52 } bounds: { x: 0, y: 0, width: 32, height: 52 }
wide: { width: 62, height: 52 } altline:
outline7: { width: 88.97561, height: 52 } bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
spaceline: { width: 150.5853, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
outline7:
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
views: views:
base: base:
@ -39,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,9 +1,15 @@
--- ---
bounds: { x: 0, y: 0.33, width: 360, height: 210 }
outlines: outlines:
default: { width: 37.46341, height: 52 } default:
altline: { width: 48.39024, height: 52 } bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
outline7: { width: 88.97561, height: 52 } altline:
spaceline: { width: 120.5853, height: 52 } bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
outline7:
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 120.5853, height: 52 }
views: views:
base: base:
@ -16,14 +22,14 @@ 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: " "
Return: Return:
outline: outline7 outline: outline7
icon: "key-enter" icon: "key-enter"
keysym: "Return" keysym: "BackSpace"
asterisk: asterisk:
text: "*" text: "*"
numbersign: numbersign:

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

@ -1,11 +1,17 @@
--- ---
bounds: { x: 0, y: 0.33, width: 360, height: 210 }
outlines: outlines:
default: { width: 32, height: 52 } default:
altline: { width: 48.39024, height: 52 } bounds: { x: 0, y: 0, width: 32, height: 52 }
wide: { width: 64, height: 52 } altline:
spaceline: { width: 142, height: 52 } bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
special: { width: 44, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
outline7:
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
views: views:
base: base:
@ -26,7 +32,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 +44,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 +67,7 @@ buttons:
outline: "altline" outline: "altline"
label: "*/=" label: "*/="
".": ".":
outline: "special" outline: altline
space: space:
outline: spaceline outline: spaceline
text: " " text: " "
@ -162,3 +163,4 @@ buttons:
text: "[" text: "["
bracketright: bracketright:
text: "]" text: "]"

View File

@ -1,204 +0,0 @@
---
outlines:
default: { width: 35.33, height: 46 }
action: { width: 59, height: 46 }
altline: { width: 52.67, height: 46 }
wide: { width: 59, height: 46 }
spaceline: { width: 140, height: 46 }
special: { width: 44, height: 46 }
small: { width: 59, height: 22 }
views:
base:
- "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:
- "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:
- "Ctrl Alt ↑ ↓ ← →"
- "1 2 3 4 5 6 7 8 9 0"
- "* # $ / & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "Ctrl Alt ↑ ↓ ← →"
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° @ { }"
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace"
- "show_letters preferences space period Return"
actions:
- "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"
Tab:
outline: "action"
keysym: "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

@ -1,10 +1,17 @@
--- ---
bounds: { x: 0, y: 1, width: 360, height: 208 }
outlines: outlines:
default: { width: 35.33, height: 52 } default:
altline: { width: 52.67, height: 52 } bounds: { x: 0, y: 0, width: 35.33, height: 52 }
wide: { width: 62, height: 52 } altline:
spaceline: { width: 142, height: 52 } bounds: { x: 0, y: 0, width: 52.67, height: 52 }
special: { width: 44, height: 52 } wide:
bounds: { x: 0, y: 0, width: 62, height: 52 }
spaceline:
bounds: { x: 0, y: 0, width: 142, height: 52 }
special:
bounds: { x: 0, y: 0, width: 44, height: 52 }
views: views:
base: base:
@ -39,9 +46,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

@ -1,10 +1,17 @@
--- ---
bounds: { x: 0, y: 1, width: 540, height: 168 }
outlines: outlines:
default: { width: 54, height: 42 } default:
altline: { width: 81, height: 42 } bounds: { x: 0, y: 0, width: 54, height: 42 }
wide: { width: 108, height: 42 } altline:
spaceline: { width: 216, height: 42 } bounds: { x: 0, y: 0, width: 81, height: 42 }
special: { width: 54, height: 42 } wide:
bounds: { x: 0, y: 0, width: 108, height: 42 }
spaceline:
bounds: { x: 0, y: 0, width: 216, height: 42 }
special:
bounds: { x: 0, y: 0, width: 54, height: 42 }
views: views:
base: base:
@ -39,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: "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

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

@ -2,7 +2,6 @@ sq_view {
background-color: rgba(0, 0, 0, 255); background-color: rgba(0, 0, 0, 255);
color: #ffffff; color: #ffffff;
font-family: cantarell, sans-serif; font-family: cantarell, sans-serif;
font-size: 25px;
} }
sq_view sq_button { sq_view sq_button {
@ -36,14 +35,6 @@ sq_button.locked {
color: #2b292f; color: #2b292f;
} }
sq_button.action {
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

@ -2,7 +2,6 @@ sq_view {
background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/ background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/
color: @theme_text_color; /*#ffffff;*/ color: @theme_text_color; /*#ffffff;*/
font-family: cantarell, sans-serif; font-family: cantarell, sans-serif;
font-size: 25px;
} }
sq_view sq_button { sq_view sq_button {
@ -39,14 +38,6 @@ sq_button.locked {
color: @theme_bg_color; /*#2b292f;*/ color: @theme_bg_color; /*#2b292f;*/
} }
sq_button.action {
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;*/

292
debian/changelog vendored
View File

@ -1,295 +1,3 @@
squeekboard (1.9.3.0pureos0.1) byzantium; urgency=medium
* Upload to byzantium
-- Guido Günther <agx@sigxcpu.org> Tue, 22 Sep 2020 12:42:41 +0200
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
[ Dorota Czaplejewicz ]
* action: Rename Level to View
* keyboard: Introduce a KeyCode type wrapping u32
* layout: Centralize handling key releases
* layout: Make handling presses uniform
* UI: Drop indirection for show/hide functions
* managers: Move visible flag to UI manager
* dbus_service: Remove unused function
* dbus: Remove unneeded gobjectness
* dbus: Rename handler from eekboard_service
* context: Moved keymap setting together with its generation
* key-emitter: Remove unused
* eekboard_context_service: Drop unused enable property
* services: Split out layout management from EekboardContextService
* submission: Move away from virtual-keyboard
* submission: Create a new wrapper over imservice
* imservice: Limited scope of unsafe
* EekGtkKeyboard: Use a direct reference to EekboardContext
* submission: Take over virtual_keyboard handling
* keyboard: Cleanups of unused code
* levelkeyboard: Drop unused manager references
* keyboard: Gather up keymap handling, drop layout
* submission: Remove wildcard reexport
* imservice: Rename commit_state to done to match protocol
* ci: Clean up `..` before it's searched for artifacts
* dbus: Log error on dbus exit
* logging: Try to improve common operations
* imservice: Return something more resembling an Error on failure
* logging: Unified to remove random eprint calls
* press_key: Use proper logging
* number: Fix keysym for Return
* build: Strip clap of optional features
* layouts: Fix segfault on switching to wide
* font: Use font from style context
* font: Only pass relevant data to label renderer
[ Sebastian Krzyszkowiak ]
* layout: terminal: Swap positions of preferences and actions button
* layout: terminal: Show actions button on all views
* layout: terminal: Replace actions button with period on symbols view
[ Dorota Czaplejewicz ]
* setup: Connect ui to the state manager
* debian: Add missing commas
[ David Boddie ]
* Tidy build file and docs
* Use pip to install recommonmark
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Fri, 31 Jan 2020 09:59:12 +0000
squeekboard (1.8.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* translations: Use gnome-desktop's xkb info database for layout names
* translations: Make the code cleaner
* overlay: Add terminal
* eek-layout: Remove unused
* pre-release: Update deps
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 14 Jan 2020 13:55:00 +0000
squeekboard (1.7.0) amber-phone; urgency=medium
* New terminal layout appearing on terminal input hint
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Wed, 08 Jan 2020 11:53:07 +0000
squeekboard (1.7.0) amber-phone; urgency=medium
* New terminal layout appearing on terminal input hint
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Wed, 08 Jan 2020 11:53:07 +0000
squeekboard (1.6.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* tools: Move entry.py
* build: Move building of squeekboard-test-layout to tools
* packaging: Install entty.py as squeekboard-entry
* Remove unused build dependencies
* Remove unused header generator
* logging: Move all facilities to one file
* logging: Described the design
* logging: Add described log levels
* popover: Install emoji layout
* popover: Show overlays as selected
* Fix old Rust woes
* emoji: Add a passable layout
* Fix g_ and stdlib allocation/free mismatches
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Thu, 02 Jan 2020 12:02:50 +0000
squeekboard (1.5.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* keycodes: Sort to eliminate runtime indeterminism
* switcher: Switch layout on menu item click
* Drop squeek_key
* renderer: Remove some unneeded vars
* renderer: Simplified outline rendering
* renderer: Drop row from button rendering
* renderer: Drop unused params
* renderer: Simplify surface rendering
* rendering: Simplify Cairo context usage, remove unneeded calls.
* rendering: Remove unneeded redraw after button release
* renderer: Remove unused locked key render function
* renderer: Simply cut off when painting outside bounds
* renderer: Render whole keyboard the same way as pressed buttons
[ Mark Müller ]
* layout: add German wide layout
[ Dorota Czaplejewicz ]
* renderer: Remove unused functions
* cleanup: Remove references to squeek_view
* cleanup: Unbox View and Row
* cleanup: Remove unused single frame draw
* positioning: Calculate sizes instead of storing, move position out of widgets
* positioning: Clean up unused code
* Fix old Rust woes
[ Mark Müller ]
* layout: add Japanese Kana wide layout
[ Dorota Czaplejewicz ]
* Entry test: Add Terminal input purpose
* readme: Add note about Cargo dependencies
* Create a library/UI module separation
* hacking: Add DCO and licensing requirement
* Fix internal .md link
[ Mark Müller ]
* squeekboard-test-layout: add argument parsing and some more output
[ Dorota Czaplejewicz ]
* Use clap in the lockfile
* parsing: Remove bounds which weren't used anyway
* layout: Respect margins
* CI: Build arm64 .deb
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 23 Dec 2019 11:58:57 +0000
squeekboard (1.4.0) amber-phone; urgency=medium squeekboard (1.4.0) amber-phone; urgency=medium
* "text" property in layouts * "text" property in layouts

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!")

20
debian/control vendored
View File

@ -9,9 +9,8 @@ Build-Depends:
ninja-build, ninja-build,
pkg-config, pkg-config,
libglib2.0-dev, libglib2.0-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,9 +24,11 @@ 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),
# for running the tests
xvfb,
xauth,
Standards-Version: 4.1.3 Standards-Version: 4.1.3
Homepage: https://source.puri.sm/Librem5/squeekboard Homepage: https://source.puri.sm/Librem5/squeekboard
@ -36,20 +37,17 @@ Architecture: linux-any
Depends: Depends:
# for the Adwaita-dark theme # for the Adwaita-dark theme
gnome-themes-extra-data, gnome-themes-extra-data,
${shlibs:Depends}, ${shlibs:Depends}
${misc:Depends}, ${misc:Depends}
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.
Package: squeekboard-devel Package: squeekboard-devel
Architecture: linux-any Architecture: linux-any
Depends: Depends:
python3, ${shlibs:Depends}
python3-gi, ${misc:Depends}
${shlibs:Depends},
${misc:Depends},
Description: Resources for making Squeekboard layouts Description: Resources for making Squeekboard layouts
Tools for creating and testing Squeekboard layouts: Tools for creating Squeekboard layouts:
. .
* squeekboard-entry
* squeekboard-test-layout * squeekboard-test-layout

7
debian/gbp.conf vendored
View File

@ -1,7 +0,0 @@
[DEFAULT]
debian-branch = pureos/byzantium
debian-tag = pureos/%(version)s
debian-tag-msg = %(pkg)s %(version)s
[tag]
sign-tags = true

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

@ -1,2 +1 @@
usr/bin/squeekboard-test-layout /usr/bin usr/bin/squeekboard-test-layout /usr/bin
usr/bin/squeekboard-entry /usr/bin

View File

@ -1,22 +0,0 @@
#!/bin/sh
# Builds the documentation and places in the selected directory,
# or the working directory.
set -e
SCRIPT_PATH="$(realpath "$0")"
DOCS_DIR="$(dirname "$SCRIPT_PATH")"
TARGET_DIR="${1:-./}"
SPHINX=sphinx-build
if [ ! -d $DOCS_DIR/_static ]; then
mkdir -p $DOCS_DIR/_static
fi
if ! which sphinx-build ; then
SPHINX=sphinx-build-3
fi
$SPHINX -b html "${DOCS_DIR}" "${TARGET_DIR}"

View File

@ -1,165 +0,0 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'squeekboard'
copyright = 'Squeekboard contributors'
author = 'Dorota Czaplejewicz'
# The short X.Y version
version = ''
# The full version, including alpha/beta/rc tags
release = ''
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'recommonmark'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.md'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'squeekboarddoc'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'squeekboard.tex', 'squeekboard Documentation',
'Dorota Czaplejewicz', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'squeekboard', 'squeekboard Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'squeekboard', 'squeekboard Documentation',
author, 'squeekboard', 'One line description of project.',
'Miscellaneous'),
]
from recommonmark.transform import AutoStructify
def setup(app):
app.add_config_value('recommonmark_config', {
'enable_auto_toc_tree': True,
'auto_toc_tree_section': 'Contents',
}, True)
app.add_transform(AutoStructify)

View File

@ -1,27 +0,0 @@
Welcome to squeekboard's documentation!
=======================================
Contents
--------
* [Tutorial](tutorial.md)
* [Contributing](hacking.md)
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).
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.
Layouts are created using a text-based format, based on YAML.
TODO: Provide a description of the format.
Contributions
-------------
Anyone is free to modify *squeekboard*. See the [contributing document](hacking.md).

View File

@ -1,55 +0,0 @@
Kareema's guide to creating layouts
===================================
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.
**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/keyboard/` in the `.yaml` files
* Take a look and try to understand them :slight_smile:
**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).
* Clone your fork locally with `git clone` and use the uri of your forked repo there
**Workflow to edit your keyboard and get it merged**
* 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
* Checkout your branch, edit your keyboard layout and commit your changes
* 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
**Compile 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)
**Running 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:
`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: )
**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>
@ -35,24 +39,24 @@
#include "eekboard/eekboard-context-service.h" #include "eekboard/eekboard-context-service.h"
#include "src/layout.h" #include "src/layout.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 LevelKeyboard *keyboard; // unowned reference; it's kept in server-context (FIXME)
struct submission *submission; // unowned reference
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,58 +85,31 @@ 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 pixel size
static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 1080) {
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));
uint32_t scale = (uint32_t)gtk_widget_get_scale_factor(self);
// check if the change would switch types
enum squeek_arrangement_kind new_type = get_type(
(uint32_t)(allocation->width - allocation->x) * scale,
(uint32_t)(allocation->height - allocation->y) * scale);
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);
@ -140,28 +117,12 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
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, priv->keyboard->manager->virtual_keyboard,
}
squeek_layout_depress(priv->keyboard->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, self); x, y, eek_renderer_get_transformation(priv->renderer), time, self);
} }
@ -169,25 +130,15 @@ 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, priv->keyboard->manager->virtual_keyboard,
return; x, y, eek_renderer_get_transformation(priv->renderer), time, self);
}
squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, 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, priv->keyboard->manager->virtual_keyboard, eek_renderer_get_transformation(priv->renderer), time, self);
}
squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission,
eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, self);
} }
static gboolean static gboolean
@ -275,8 +226,7 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
if (priv->keyboard) { if (priv->keyboard) {
squeek_layout_release_all_only( squeek_layout_release_all_only(
priv->keyboard->layout, priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
priv->submission,
gdk_event_get_time(NULL)); gdk_event_get_time(NULL));
} }
@ -304,24 +254,18 @@ 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;
} }
if (priv->keyboard) { if (priv->keyboard) {
squeek_layout_release_all_only( squeek_layout_release_all_only(
priv->keyboard->layout, priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard,
priv->submission,
gdk_event_get_time(NULL)); gdk_event_get_time(NULL));
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,76 +297,26 @@ 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);
}
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)
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->eekboard_context = eekservice; priv->keyboard = keyboard;
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

@ -28,10 +28,7 @@
#include <glib.h> #include <glib.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "eek/eek-types.h" typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs
struct submission;
struct squeek_layout_state;
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 +44,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);
G_END_DECLS G_END_DECLS
#endif /* EEK_GTK_KEYBOARD_H */ #endif /* EEK_GTK_KEYBOARD_H */

View File

@ -18,78 +18,42 @@
* 02110-1301 USA * 02110-1301 USA
*/ */
/**
* SECTION:eek-keyboard
* @short_description: Base class of a keyboard
* @see_also: #EekSection
*
* The #EekKeyboardClass class represents a keyboard, which consists
* of one or more sections of the #EekSectionClass class.
*/
#include "config.h" #include "config.h"
#include <glib/gprintf.h>
#define _XOPEN_SOURCE 500 #include "eek-enumtypes.h"
#include <errno.h> #include "eekboard/eekboard-context-service.h"
#include <fcntl.h> #include "eekboard/key-emitter.h"
#include <string.h> #include "keymap.h"
#include <sys/mman.h>
#include <sys/random.h> // TODO: this is Linux-specific
#include <xkbcommon/xkbcommon.h>
#include "eek-keyboard.h" #include "eek-keyboard.h"
void level_keyboard_free(LevelKeyboard *self) { void level_keyboard_deinit(LevelKeyboard *self) {
xkb_keymap_unref(self->keymap); xkb_keymap_unref(self->keymap);
close(self->keymap_fd); close(self->keymap_fd);
squeek_layout_free(self->layout); squeek_layout_free(self->layout);
}
void level_keyboard_free(LevelKeyboard *self) {
level_keyboard_deinit(self);
g_free(self); g_free(self);
} }
LevelKeyboard* void level_keyboard_init(LevelKeyboard *self, struct squeek_layout *layout) {
level_keyboard_new (struct squeek_layout *layout) self->layout = layout;
{ }
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout) {
LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1); LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
level_keyboard_init(keyboard, layout);
if (!keyboard) { keyboard->manager = manager;
g_error("Failed to create a keyboard");
}
keyboard->layout = layout;
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
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,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap)
g_error("Bad keymap:\n%s", keymap_str);
xkb_context_unref(context);
keyboard->keymap = keymap;
keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
keyboard->keymap_len = strlen(keymap_str) + 1;
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
char *r = &path[strlen(path) - 6];
if (getrandom(r, 6, GRND_NONBLOCK) < 0)
g_error("Failed to get random numbers: %s", strerror(errno));
for (unsigned i = 0; i < 6; i++) {
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
}
int keymap_fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
if (keymap_fd < 0) {
g_error("Failed to set up keymap fd");
}
keyboard->keymap_fd = keymap_fd;
shm_unlink(path);
if (ftruncate(keymap_fd, (off_t)keyboard->keymap_len)) {
g_error("Failed to increase keymap fd size");
}
char *ptr = mmap(NULL, keyboard->keymap_len, PROT_WRITE, MAP_SHARED,
keymap_fd, 0);
if ((void*)ptr == (void*)-1) {
g_error("Failed to set up mmap");
}
strncpy(ptr, keymap_str, keyboard->keymap_len);
munmap(ptr, keyboard->keymap_len);
return keyboard; return keyboard;
} }

View File

@ -28,6 +28,7 @@
#include <glib-object.h> #include <glib-object.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include "eek-types.h" #include "eek-types.h"
#include "eek-layout.h"
#include "src/layout.h" #include "src/layout.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -40,14 +41,16 @@ struct _LevelKeyboard {
size_t keymap_len; // length of the data inside keymap_fd size_t keymap_len; // length of the data inside keymap_fd
guint id; // as a key to layout choices guint id; // as a key to layout choices
EekboardContextService *manager; // unowned reference
}; };
typedef struct _LevelKeyboard LevelKeyboard; typedef struct _LevelKeyboard LevelKeyboard;
gchar * eek_keyboard_get_keymap gchar * eek_keyboard_get_keymap
(LevelKeyboard *keyboard); (LevelKeyboard *keyboard);
LevelKeyboard* LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout);
level_keyboard_new (struct squeek_layout *layout); void level_keyboard_deinit(LevelKeyboard *self);
void level_keyboard_free(LevelKeyboard *self); void level_keyboard_free(LevelKeyboard *self);
G_END_DECLS G_END_DECLS

47
eek/eek-layout.c Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
/**
* SECTION:eek-layout
* @short_description: Base class of a layout engine
*
* The #EekLayout class is a base class of layout engine which
* arranges keyboard elements.
*/
#include "config.h"
#include "eek-layout.h"
#include "eek-keyboard.h"
#include "eekboard/eekboard-context-service.h"
#include "eek-xml-layout.h"
G_DEFINE_ABSTRACT_TYPE (EekLayout, eek_layout, G_TYPE_OBJECT)
static void
eek_layout_class_init (EekLayoutClass *klass)
{
klass->create_keyboard = NULL;
}
void
eek_layout_init (EekLayout *self)
{
}

60
eek/eek-layout.h Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#if !defined(__EEK_H_INSIDE__) && !defined(EEK_COMPILATION)
#error "Only <eek/eek.h> can be included directly."
#endif
#ifndef EEK_LAYOUT_H
#define EEK_LAYOUT_H 1
#include <glib-object.h>
#include "eek-types.h"
#include "src/layout.h"
G_BEGIN_DECLS
#define EEK_TYPE_LAYOUT (eek_layout_get_type())
G_DECLARE_DERIVABLE_TYPE (EekLayout, eek_layout, EEK, LAYOUT, GObject)
/**
* EekLayoutClass:
* @create_keyboard: virtual function for creating a keyboard
*/
struct _EekLayoutClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
LevelKeyboard* (* create_keyboard) (EekboardContextService *manager,
EekLayout *self,
gdouble initial_width,
gdouble initial_height);
/*< private >*/
/* padding */
gpointer pdummy[24];
};
GType eek_layout_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* EEK_LAYOUT_H */

View File

@ -28,10 +28,36 @@
#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 border_width; // FIXME: border of what?
gdouble allocation_width;
gdouble allocation_height;
gint scale_factor; /* the outputs scale factor */
struct transformation widget_to_layout;
PangoFontDescription *font; // owned reference
} 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 eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, GtkStyleContext *ctx,
const gchar *label, EekBounds bounds); const struct squeek_button *button);
void eek_render_button (EekRenderer *self, void eek_render_button (EekRenderer *self,
cairo_t *cr, const struct squeek_button *button, cairo_t *cr, const struct squeek_button *button,
@ -60,7 +86,8 @@ render_outline (cairo_t *cr,
position.x, position.y, position.width, position.height); position.x, position.y, position.width, position.height);
} }
static void render_button_in_context(gint scale_factor, static void render_button_in_context(EekRenderer *self,
gint scale_factor,
cairo_t *cr, cairo_t *cr,
GtkStyleContext *ctx, GtkStyleContext *ctx,
const struct squeek_button *button) { const struct squeek_button *button) {
@ -103,11 +130,7 @@ static void render_button_in_context(gint scale_factor,
return; return;
} }
} }
eek_renderer_render_button_label (self, cr, ctx, button);
const gchar *label = squeek_button_get_label(button);
if (label) {
render_button_label (cr, ctx, label, squeek_button_get_bounds(button));
}
} }
void void
@ -117,7 +140,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 +162,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(self, 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);
@ -148,16 +173,43 @@ eek_render_button (EekRenderer *self,
} }
static void static void
render_button_label (cairo_t *cr, eek_renderer_render_button_label (EekRenderer *self,
GtkStyleContext *ctx, cairo_t *cr,
const gchar *label, GtkStyleContext *ctx,
EekBounds bounds) const struct squeek_button *button)
{ {
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
const gchar *label = squeek_button_get_label(button);
if (!label) {
return;
}
PangoFontDescription *font; PangoFontDescription *font;
gtk_style_context_get(ctx, gdouble scale;
gtk_style_context_get_state(ctx),
"font", &font,
NULL); if (!priv->font) {
const PangoFontDescription *base_font;
gdouble size;
base_font = pango_context_get_font_description (priv->pcontext);
// FIXME: Base font size on the same size unit used for button sizing,
// and make the default about 1/3 of the current row height
size = 30000.0;
priv->font = pango_font_description_copy (base_font);
pango_font_description_set_size (priv->font, (gint)round(size * 0.6));
}
EekBounds bounds = squeek_button_get_bounds(button);
scale = MIN((bounds.width - priv->border_width) / bounds.width,
(bounds.height - priv->border_width) / bounds.height);
font = pango_font_description_copy (priv->font);
pango_font_description_set_size (font,
(gint)round(pango_font_description_get_size (font) * scale));
PangoLayout *layout = pango_cairo_create_layout (cr); PangoLayout *layout = pango_cairo_create_layout (cr);
pango_layout_set_font_description (layout, font); pango_layout_set_font_description (layout, font);
pango_font_description_free (font); pango_font_description_free (font);
@ -167,7 +219,8 @@ render_button_label (cairo_t *cr,
if (line->resolved_dir == PANGO_DIRECTION_RTL) { if (line->resolved_dir == PANGO_DIRECTION_RTL) {
pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT); pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
} }
pango_layout_set_width (layout, PANGO_SCALE * bounds.width); pango_layout_set_width (layout,
PANGO_SCALE * bounds.width * scale);
PangoRectangle extents = { 0, }; PangoRectangle extents = { 0, };
pango_layout_get_extents (layout, NULL, &extents); pango_layout_get_extents (layout, NULL, &extents);
@ -191,46 +244,119 @@ 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);
pango_font_description_free (priv->font);
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 +367,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 +375,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,75 +384,83 @@ 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; priv->keyboard = NULL;
self->scale_factor = 1; priv->pcontext = NULL;
priv->border_width = 1.0;
priv->allocation_width = 0.0;
priv->allocation_height = 0.0;
priv->scale_factor = 1;
priv->font = NULL;
GtkIconTheme *theme = gtk_icon_theme_get_default (); GtkIconTheme *theme = gtk_icon_theme_get_default ();
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons"); gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
self->css_provider = squeek_load_style(); 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
} }
@ -334,7 +468,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 *
@ -363,5 +500,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

@ -38,7 +38,6 @@ G_BEGIN_DECLS
typedef struct _EekBounds EekBounds; typedef struct _EekBounds EekBounds;
typedef struct _EekboardContextService EekboardContextService; typedef struct _EekboardContextService EekboardContextService;
typedef struct _ServerContextService ServerContextService;
typedef struct _LevelKeyboard LevelKeyboard; typedef struct _LevelKeyboard LevelKeyboard;
/** /**

38
eek/eek-xml-layout.c Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2011 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:eek-xml-layout
* @short_description: Layout engine which loads layout information from XML
*/
#include "config.h"
#include "eek-keyboard.h"
#include "src/layout.h"
#include "eek-xml-layout.h"
LevelKeyboard *
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
EekboardContextService *manager,
enum squeek_arrangement_kind t)
{
struct squeek_layout *layout = squeek_load_layout(keyboard_type, t);
return level_keyboard_new(manager, layout);
}

36
eek/eek-xml-layout.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2011 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(__EEK_H_INSIDE__) && !defined(EEK_COMPILATION)
#error "Only <eek/eek.h> can be included directly."
#endif
#ifndef EEK_XML_LAYOUT_H
#define EEK_XML_LAYOUT_H 1
#include "eek-types.h"
#include "src/layout.h"
G_BEGIN_DECLS
LevelKeyboard *
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
EekboardContextService *manager,
enum squeek_arrangement_kind t);
G_END_DECLS
#endif /* EEK_XML_LAYOUT_H */

View File

@ -23,6 +23,7 @@
#define __EEK_H_INSIDE__ 1 #define __EEK_H_INSIDE__ 1
#include "eek-keyboard.h" #include "eek-keyboard.h"
#include "eek-layout.h"
void eek_init (void); void eek_init (void);

8
eek/keymap.h Normal file
View File

@ -0,0 +1,8 @@
#include <gdk/gdk.h>
#include <xkbcommon/xkbcommon.h>
gboolean
squeek_keymap_get_entries_for_keyval (struct xkb_keymap *xkb_keymap,
guint keyval,
GdkKeymapKey **keys,
guint *n_keys);

8
eek/meson.build Normal file
View File

@ -0,0 +1,8 @@
gnome = import('gnome')
enum_headers = [
'eek-types.h',
]
enums = gnome.mkenums_simple('eek-enumtypes', sources: enum_headers)

View File

@ -16,15 +16,31 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/**
* SECTION:eekboard-context-service
* @short_description: base server implementation of eekboard input
* context service
*
* The #EekboardService class provides a base server side
* implementation of eekboard input context service.
*/
#include "config.h" #include "config.h"
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#define _XOPEN_SOURCE 500
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h> // TODO: this is Linux-specific
#include <xkbcommon/xkbcommon.h>
#include <gio/gio.h> #include <gio/gio.h>
#include "eekboard/key-emitter.h"
#include "wayland.h" #include "wayland.h"
#include "eek/eek-keyboard.h" #include "eek/eek-xml-layout.h"
#include "src/server-context-service.h" #include "src/server-context-service.h"
#include "eekboard/eekboard-context-service.h" #include "eekboard/eekboard-context-service.h"
@ -32,42 +48,102 @@
enum { enum {
PROP_0, // Magic: without this, keyboard is not useable in g_object_notify PROP_0, // Magic: without this, keyboard is not useable in g_object_notify
PROP_KEYBOARD, PROP_KEYBOARD,
PROP_VISIBLE,
PROP_LAST PROP_LAST
}; };
enum { enum {
ENABLED,
DISABLED,
DESTROYED, DESTROYED,
LAST_SIGNAL LAST_SIGNAL
}; };
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. struct _EekboardContextServicePrivate {
* gboolean enabled;
* TODO: Restrict to managing keyboard layouts, and maybe button repeats, gboolean visible;
* 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
LevelKeyboard *keyboard; // currently used keyboard LevelKeyboard *keyboard; // currently used keyboard
GSettings *settings; // Owned reference GHashTable *keyboard_hash; // a table of available keyboards, per layout
// Maybe TODO: it's used only for fetching layout type. GSettings *settings;
// Maybe let UI push the type to this structure? uint32_t hint;
ServerContextService *ui; // unowned reference uint32_t purpose;
/// Needed for keymap changes after keyboard updates
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 LevelKeyboard *
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
const gchar *keyboard_type,
enum squeek_arrangement_kind t)
{
LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self, t);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
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,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap)
g_error("Bad keymap:\n%s", keymap_str);
xkb_context_unref(context);
keyboard->keymap = keymap;
keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
keyboard->keymap_len = strlen(keymap_str) + 1;
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
char *r = &path[strlen(path) - 6];
getrandom(r, 6, GRND_NONBLOCK);
for (unsigned i = 0; i < 6; i++) {
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
}
int keymap_fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
if (keymap_fd < 0) {
g_error("Failed to set up keymap fd");
}
keyboard->keymap_fd = keymap_fd;
shm_unlink(path);
if (ftruncate(keymap_fd, (off_t)keyboard->keymap_len)) {
g_error("Failed to increase keymap fd size");
}
char *ptr = mmap(NULL, keyboard->keymap_len, PROT_WRITE, MAP_SHARED,
keymap_fd, 0);
if ((void*)ptr == (void*)-1) {
g_error("Failed to set up mmap");
}
strncpy(ptr, keymap_str, keyboard->keymap_len);
munmap(ptr, keyboard->keymap_len);
return keyboard;
}
static void
eekboard_context_service_real_show_keyboard (EekboardContextService *self)
{
self->priv->visible = TRUE;
}
static void
eekboard_context_service_real_hide_keyboard (EekboardContextService *self)
{
self->priv->visible = FALSE;
}
static void static void
eekboard_context_service_set_property (GObject *object, eekboard_context_service_set_property (GObject *object,
@ -75,8 +151,17 @@ eekboard_context_service_set_property (GObject *object,
const GValue *value, const GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
(void)value; EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
switch (prop_id) { switch (prop_id) {
case PROP_KEYBOARD:
if (context->priv->keyboard)
g_object_unref (context->priv->keyboard);
context->priv->keyboard = g_value_get_object (value);
break;
case PROP_VISIBLE:
context->priv->visible = g_value_get_boolean (value);
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;
@ -93,7 +178,10 @@ 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;
case PROP_VISIBLE:
g_value_set_boolean (value, context->priv->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);
@ -104,6 +192,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 +206,42 @@ 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_type = NULL;
g_autofree gchar *keyboard_layout = NULL;
settings_get_layout(context->priv->settings, &keyboard_type, &keyboard_layout);
if (layout_name == NULL) { if (!keyboard_type) {
layout_name = state->layout_name; keyboard_type = g_strdup("us");
}
if (!keyboard_layout) {
keyboard_layout = g_strdup("undefined");
}
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;
default:
;
} }
// generic part follows // generic part follows
struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement); LevelKeyboard *keyboard = eekboard_context_service_real_create_keyboard(context, 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.
// TODO: Update submission on change event
if (context->submission) {
submission_set_keyboard(context->submission, keyboard, timestamp);
}
// 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 +250,8 @@ 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; eekboard_context_service_update_layout(context, server_context_service_get_layout_type(context));
g_autofree gchar *keyboard_type = NULL;
settings_get_layout(context->settings,
&keyboard_type, &keyboard_layout);
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);
}
} }
static gboolean static gboolean
@ -198,14 +262,21 @@ 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); 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);
context->virtual_keyboard = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
squeek_wayland->virtual_keyboard_manager,
squeek_wayland->seat);
if (!context->virtual_keyboard) {
g_error("Programmer error: Failed to receive a virtual keyboard instance");
}
update_layout_and_type(context);
} }
static void static void
@ -214,10 +285,48 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec; GParamSpec *pspec;
klass->show_keyboard = eekboard_context_service_real_show_keyboard;
klass->hide_keyboard = eekboard_context_service_real_hide_keyboard;
gobject_class->constructed = eekboard_context_service_constructed; gobject_class->constructed = eekboard_context_service_constructed;
gobject_class->set_property = eekboard_context_service_set_property; gobject_class->set_property = eekboard_context_service_set_property;
gobject_class->get_property = eekboard_context_service_get_property; gobject_class->get_property = eekboard_context_service_get_property;
gobject_class->dispose = eekboard_context_service_dispose; gobject_class->dispose = eekboard_context_service_dispose;
/**
* EekboardContextService::enabled:
* @context: an #EekboardContextService
*
* Emitted when @context is enabled.
*/
signals[ENABLED] =
g_signal_new (I_("enabled"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekboardContextServiceClass, enabled),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* EekboardContextService::disabled:
* @context: an #EekboardContextService
*
* Emitted when @context is enabled.
*/
signals[DISABLED] =
g_signal_new (I_("disabled"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekboardContextServiceClass, disabled),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/** /**
* EekboardContextService::destroyed: * EekboardContextService::destroyed:
* @context: an #EekboardContextService * @context: an #EekboardContextService
@ -228,7 +337,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,
@ -243,39 +352,100 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
pspec = g_param_spec_pointer("keyboard", pspec = g_param_spec_pointer("keyboard",
"Keyboard", "Keyboard",
"Keyboard", "Keyboard",
G_PARAM_READABLE); G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_KEYBOARD, PROP_KEYBOARD,
pspec); pspec);
/**
* EekboardContextService:visible:
*
* Flag to indicate if keyboard is visible or not.
*/
pspec = g_param_spec_boolean ("visible",
"Visible",
"Visible",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_VISIBLE,
pspec);
} }
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); /**
if (schema) { * eekboard_context_service_enable:
// Not referencing the found schema directly, * @context: an #EekboardContextService
// because it's not clear how... *
self->settings = g_settings_new (schema_name); * Enable @context. This function is called when @context is pushed
gulong conn_id = g_signal_connect(self->settings, "change-event", * by eekboard_service_push_context().
G_CALLBACK(settings_handle_layout_changed), */
self); void
if (conn_id == 0) { eekboard_context_service_enable (EekboardContextService *context)
g_warning ("Could not connect to gsettings updates, " {
"automatic layout changing unavailable"); g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
}
} else { if (!context->priv->enabled) {
g_warning("Gsettings schema %s is not installed on the system. " context->priv->enabled = TRUE;
"Layout switching unavailable", schema_name); g_signal_emit (context, signals[ENABLED], 0);
}
}
/**
* eekboard_context_service_disable:
* @context: an #EekboardContextService
*
* Disable @context. This function is called when @context is pushed
* by eekboard_service_pop_context().
*/
void
eekboard_context_service_disable (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (context->priv->enabled) {
context->priv->enabled = FALSE;
g_signal_emit (context, signals[DISABLED], 0);
}
}
void
eekboard_context_service_show_keyboard (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (!context->priv->visible) {
EEKBOARD_CONTEXT_SERVICE_GET_CLASS(context)->show_keyboard (context);
}
}
void
eekboard_context_service_hide_keyboard (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (context->priv->visible) {
EEKBOARD_CONTEXT_SERVICE_GET_CLASS(context)->hide_keyboard (context);
} }
} }
@ -289,6 +459,10 @@ 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));
if (context->priv->enabled) {
eekboard_context_service_disable (context);
}
g_signal_emit (context, signals[DESTROYED], 0); g_signal_emit (context, signals[DESTROYED], 0);
} }
@ -302,53 +476,25 @@ 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_keymap(EekboardContextService *context,
const LevelKeyboard *keyboard)
{
zwp_virtual_keyboard_v1_keymap(context->virtual_keyboard,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keyboard->keymap_fd, keyboard->keymap_len);
} }
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
eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) {
if (g_strcmp0(context->layout->overlay_name, name)) {
g_free(context->layout->overlay_name);
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*
eekboard_context_service_get_overlay(EekboardContextService *context) {
return context->layout->overlay_name;
}
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state)
{
EekboardContextService *context = 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) {
context->submission = submission;
if (context->submission) {
uint32_t time = gdk_event_get_time(NULL);
submission_set_keyboard(context->submission, context->keyboard, time);
}
}
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
context->ui = ui;
}

View File

@ -22,8 +22,7 @@
#ifndef EEKBOARD_CONTEXT_SERVICE_H #ifndef EEKBOARD_CONTEXT_SERVICE_H
#define EEKBOARD_CONTEXT_SERVICE_H 1 #define EEKBOARD_CONTEXT_SERVICE_H 1
#include "src/submission.h" #include <eek/eek.h>
#include "src/layout.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" #include "text-input-unstable-v3-client-protocol.h"
@ -34,12 +33,69 @@ 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;
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission); typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
/**
* EekboardContextService:
*
* 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;
struct zwp_virtual_keyboard_v1 *virtual_keyboard;
};
/**
* EekboardContextServiceClass:
* @create_keyboard: virtual function for create a keyboard from string
* @show_keyboard: virtual function for show a keyboard
* @hide_keyboard: virtual function for hide a keyboard
* @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);
void (*show_keyboard) (EekboardContextService *self);
void (*hide_keyboard) (EekboardContextService *self);
/* signals */
void (*enabled) (EekboardContextService *self);
void (*disabled) (EekboardContextService *self);
void (*destroyed) (EekboardContextService *self);
/*< private >*/
/* padding */
gpointer pdummy[24];
};
GType eekboard_context_service_get_type
(void) G_GNUC_CONST;
void eekboard_context_service_enable (EekboardContextService *context);
void eekboard_context_service_disable (EekboardContextService *context);
void eekboard_context_service_show_keyboard
(EekboardContextService *context);
void eekboard_context_service_hide_keyboard
(EekboardContextService *context);
void eekboard_context_service_destroy (EekboardContextService *context); void eekboard_context_service_destroy (EekboardContextService *context);
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context); LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
@ -50,6 +106,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 */

310
eekboard/eekboard-service.c Normal file
View File

@ -0,0 +1,310 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:eekboard-service
* @short_description: base implementation of eekboard service
*
* Provides a dbus object, and contains the context.
*
* The #EekboardService class provides a base server side
* implementation of eekboard service.
*/
#include "config.h"
#include "sm.puri.OSK0.h"
#include <stdio.h>
#include <gio/gio.h>
#include "eekboard/eekboard-service.h"
enum {
PROP_0,
PROP_OBJECT_PATH,
PROP_CONNECTION,
PROP_LAST
};
enum {
DESTROYED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0, };
typedef struct _EekboardServicePrivate
{
GDBusConnection *connection;
SmPuriOSK0 *dbus_interface;
GDBusNodeInfo *introspection_data;
guint registration_id;
char *object_path;
EekboardContextService *context; // unowned reference
} EekboardServicePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekboardService, eekboard_service, G_TYPE_OBJECT)
static void
eekboard_service_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EekboardService *service = EEKBOARD_SERVICE(object);
EekboardServicePrivate *priv = eekboard_service_get_instance_private (service);
GDBusConnection *connection;
switch (prop_id) {
case PROP_OBJECT_PATH:
if (priv->object_path)
g_free (priv->object_path);
priv->object_path = g_value_dup_string (value);
break;
case PROP_CONNECTION:
connection = g_value_get_object (value);
if (priv->connection)
g_object_unref (priv->connection);
priv->connection = g_object_ref (connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
eekboard_service_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EekboardService *service = EEKBOARD_SERVICE(object);
EekboardServicePrivate *priv = eekboard_service_get_instance_private (service);
switch (prop_id) {
case PROP_OBJECT_PATH:
g_value_set_string (value, priv->object_path);
break;
case PROP_CONNECTION:
g_value_set_object (value, priv->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
eekboard_service_dispose (GObject *object)
{
EekboardService *service = EEKBOARD_SERVICE(object);
EekboardServicePrivate *priv = eekboard_service_get_instance_private (service);
if (priv->connection) {
if (priv->registration_id > 0) {
g_dbus_connection_unregister_object (priv->connection,
priv->registration_id);
priv->registration_id = 0;
}
g_object_unref (priv->connection);
priv->connection = NULL;
}
if (priv->introspection_data) {
g_dbus_node_info_unref (priv->introspection_data);
priv->introspection_data = NULL;
}
if (priv->context) {
g_signal_handlers_disconnect_by_data (priv->context, service);
priv->context = NULL;
}
G_OBJECT_CLASS (eekboard_service_parent_class)->dispose (object);
}
static void
eekboard_service_finalize (GObject *object)
{
EekboardService *service = EEKBOARD_SERVICE(object);
EekboardServicePrivate *priv = eekboard_service_get_instance_private (service);
g_free (priv->object_path);
G_OBJECT_CLASS (eekboard_service_parent_class)->finalize (object);
}
static gboolean
handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
gboolean arg_visible, gpointer user_data) {
EekboardService *service = user_data;
EekboardServicePrivate *priv = eekboard_service_get_instance_private (service);
if (priv->context) {
if (arg_visible) {
eekboard_context_service_show_keyboard (priv->context);
} else {
eekboard_context_service_hide_keyboard (priv->context);
}
}
sm_puri_osk0_complete_set_visible(object, invocation);
return TRUE;
}
static void on_visible(EekboardService *service,
GParamSpec *pspec,
EekboardContextService *context)
{
gboolean visible;
EekboardServicePrivate *priv;
g_return_if_fail (EEKBOARD_IS_SERVICE (service));
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE (context));
priv = eekboard_service_get_instance_private (service);
g_object_get (context, "visible", &visible, NULL);
sm_puri_osk0_set_visible(priv->dbus_interface, visible);
}
static void
eekboard_service_constructed (GObject *object)
{
EekboardService *service = EEKBOARD_SERVICE(object);
EekboardServicePrivate *priv = eekboard_service_get_instance_private (service);
priv->dbus_interface = sm_puri_osk0_skeleton_new();
g_signal_connect(priv->dbus_interface, "handle-set-visible",
G_CALLBACK(handle_set_visible), service);
if (priv->connection && priv->object_path) {
GError *error = NULL;
if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(priv->dbus_interface),
priv->connection,
priv->object_path,
&error)) {
g_warning("Error registering dbus object: %s\n", error->message);
g_clear_error(&error);
}
}
}
static void
eekboard_service_class_init (EekboardServiceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
klass->create_context = NULL;
gobject_class->constructed = eekboard_service_constructed;
gobject_class->set_property = eekboard_service_set_property;
gobject_class->get_property = eekboard_service_get_property;
gobject_class->dispose = eekboard_service_dispose;
gobject_class->finalize = eekboard_service_finalize;
/**
* EekboardService::destroyed:
* @service: an #EekboardService
*
* The ::destroyed signal is emitted when the service is vanished.
*/
signals[DESTROYED] =
g_signal_new (I_("destroyed"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* EekboardService:object-path:
*
* D-Bus object path.
*/
pspec = g_param_spec_string ("object-path",
"Object-path",
"Object-path",
NULL,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_OBJECT_PATH,
pspec);
/**
* EekboardService:connection:
*
* D-Bus connection.
*/
pspec = g_param_spec_object ("connection",
"Connection",
"Connection",
G_TYPE_DBUS_CONNECTION,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_CONNECTION,
pspec);
}
static void
eekboard_service_init (EekboardService *self)
{
EekboardServicePrivate *priv = eekboard_service_get_instance_private (self);
priv->context = NULL;
}
/**
* eekboard_service_new:
* @connection: a #GDBusConnection
* @object_path: object path
*/
EekboardService *
eekboard_service_new (GDBusConnection *connection,
const gchar *object_path)
{
return g_object_new (EEKBOARD_TYPE_SERVICE,
"object-path", object_path,
"connection", connection,
NULL);
}
void
eekboard_service_set_context(EekboardService *service,
EekboardContextService *context)
{
EekboardServicePrivate *priv = eekboard_service_get_instance_private (service);
g_return_if_fail (!priv->context);
priv->context = context;
g_signal_connect_swapped (priv->context,
"notify::visible",
G_CALLBACK(on_visible),
service);
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EEKBOARD_SERVICE_H
#define EEKBOARD_SERVICE_H 1
#define __EEKBOARD_SERVICE_H_INSIDE__ 1
#include "eekboard/eekboard-context-service.h"
G_BEGIN_DECLS
#define EEKBOARD_SERVICE_PATH "/sm/puri/OSK0"
#define EEKBOARD_SERVICE_INTERFACE "sm.puri.OSK0"
#define EEKBOARD_TYPE_SERVICE (eekboard_service_get_type())
G_DECLARE_DERIVABLE_TYPE (EekboardService, eekboard_service, EEKBOARD, SERVICE, GObject)
/**
* EekboardServiceClass:
* @create_context: virtual function for creating a context
*/
struct _EekboardServiceClass {
/*< private >*/
GObjectClass parent_class;
/*< public >*/
EekboardContextService *(*create_context) (EekboardService *self);
/*< private >*/
/* padding */
gpointer pdummy[24];
};
GType eekboard_service_get_type (void) G_GNUC_CONST;
EekboardService * eekboard_service_new (GDBusConnection *connection,
const gchar *object_path);
void eekboard_service_set_context(EekboardService *service,
EekboardContextService *context);
G_END_DECLS
#endif /* EEKBOARD_SERVICE_H */

136
eekboard/key-emitter.c Normal file
View File

@ -0,0 +1,136 @@
/*
* Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2011 Red Hat, Inc.
* Copyright (C) 2019 Purism, SPC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file is responsible for managing keycode data and emitting keycodes. */
#include "eekboard/key-emitter.h"
#include <gdk/gdk.h>
#include <X11/XKBlib.h>
#include "eekboard/eekboard-context-service.h"
// TODO: decide whether it's this struct that carries the keyboard around in key-emitter or if the whole manager should be dragged around
// if this is the carrier, then it should be made part of the manager
// hint: check which fields need to be persisted between keypresses; which between keyboards
typedef struct {
struct zwp_virtual_keyboard_v1 *virtual_keyboard; // unowned copy
struct xkb_keymap *keymap; // unowned copy
XkbDescRec *xkb;
guint modifier_keycodes[8];
guint modifier_indices[MOD_IDX_LAST];
guint group;
} SeatEmitter;
int send_virtual_keyboard_key(
struct zwp_virtual_keyboard_v1 *keyboard,
unsigned int keycode,
unsigned is_press,
uint32_t timestamp
) {
zwp_virtual_keyboard_v1_key(keyboard, timestamp, keycode, (unsigned)is_press);
return 0;
}
/* Finds the first key code for each modifier and saves it in modifier_keycodes */
static void
update_modifier_info (SeatEmitter *client)
{
client->modifier_indices[MOD_IDX_SHIFT] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_SHIFT);
client->modifier_indices[MOD_IDX_CAPS] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_CAPS);
client->modifier_indices[MOD_IDX_CTRL] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_CTRL);
client->modifier_indices[MOD_IDX_ALT] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_ALT);
client->modifier_indices[MOD_IDX_NUM] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_NUM);
client->modifier_indices[MOD_IDX_MOD3] = xkb_keymap_mod_get_index(client->keymap, "Mod3");
client->modifier_indices[MOD_IDX_LOGO] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_LOGO);
client->modifier_indices[MOD_IDX_ALTGR] = xkb_keymap_mod_get_index(client->keymap, "Mod5");
client->modifier_indices[MOD_IDX_NUMLK] = xkb_keymap_mod_get_index(client->keymap, "NumLock");
client->modifier_indices[MOD_IDX_ALSO_ALT] = xkb_keymap_mod_get_index(client->keymap, "Alt");
client->modifier_indices[MOD_IDX_LVL3] = xkb_keymap_mod_get_index(client->keymap, "LevelThree");
client->modifier_indices[MOD_IDX_LALT] = xkb_keymap_mod_get_index(client->keymap, "LAlt");
client->modifier_indices[MOD_IDX_RALT] = xkb_keymap_mod_get_index(client->keymap, "RAlt");
client->modifier_indices[MOD_IDX_RCONTROL] = xkb_keymap_mod_get_index(client->keymap, "RControl");
client->modifier_indices[MOD_IDX_LCONTROL] = xkb_keymap_mod_get_index(client->keymap, "LControl");
client->modifier_indices[MOD_IDX_SCROLLLK] = xkb_keymap_mod_get_index(client->keymap, "ScrollLock");
client->modifier_indices[MOD_IDX_LVL5] = xkb_keymap_mod_get_index(client->keymap, "LevelFive");
client->modifier_indices[MOD_IDX_ALSO_ALTGR] = xkb_keymap_mod_get_index(client->keymap, "AltGr");
client->modifier_indices[MOD_IDX_META] = xkb_keymap_mod_get_index(client->keymap, "Meta");
client->modifier_indices[MOD_IDX_SUPER] = xkb_keymap_mod_get_index(client->keymap, "Super");
client->modifier_indices[MOD_IDX_HYPER] = xkb_keymap_mod_get_index(client->keymap, "Hyper");
/*
for (xkb_mod_index_t i = 0;
i < xkb_keymap_num_mods(client->keymap);
i++) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "%s", xkb_keymap_mod_get_name(client->keymap, i));
}*/
}
static void
send_fake_key (SeatEmitter *emitter,
LevelKeyboard *keyboard,
guint keycode,
gboolean pressed,
uint32_t timestamp)
{
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
send_virtual_keyboard_key (emitter->virtual_keyboard, keycode - 8, (unsigned)pressed, timestamp);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
}
void
emit_key_activated (EekboardContextService *manager,
LevelKeyboard *keyboard,
guint keycode,
gboolean pressed,
uint32_t timestamp)
{
/* FIXME: figure out how to deal with Client after key presses go through
if (g_strcmp0 (eek_symbol_get_name (symbol), "cycle-keyboard") == 0) {
client->keyboards_head = g_slist_next (client->keyboards_head);
if (client->keyboards_head == NULL)
client->keyboards_head = client->keyboards;
eekboard_context_set_keyboard (client->context,
GPOINTER_TO_UINT(client->keyboards_head->data),
NULL);
return;
}
if (g_strcmp0 (eek_symbol_get_name (symbol), "preferences") == 0) {
gchar *argv[2];
GError *error;
argv[0] = g_build_filename (LIBEXECDIR, "eekboard-setup", NULL);
argv[1] = NULL;
error = NULL;
if (!g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error)) {
g_warning ("can't spawn %s: %s", argv[0], error->message);
g_error_free (error);
}
g_free (argv[0]);
return;
}
*/
SeatEmitter emitter = {0};
emitter.virtual_keyboard = manager->virtual_keyboard;
update_modifier_info (&emitter);
send_fake_key (&emitter, keyboard, keycode, pressed, timestamp);
}

45
eekboard/key-emitter.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef KEYEMITTER_H
#define KEYEMITTER_H
#include <inttypes.h>
#include <glib.h>
#include "eek/eek.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
/// Indices obtained by xkb_keymap_mod_get_name
enum mod_indices {
MOD_IDX_SHIFT,
MOD_IDX_CAPS,
MOD_IDX_CTRL,
MOD_IDX_ALT,
MOD_IDX_NUM,
MOD_IDX_MOD3,
MOD_IDX_LOGO,
MOD_IDX_ALTGR,
MOD_IDX_NUMLK, // Caution, not sure which is the right one
MOD_IDX_ALSO_ALT, // Not sure why, alt emits the first alt on my setup
MOD_IDX_LVL3,
// Not sure if the next 4 are used at all
MOD_IDX_LALT,
MOD_IDX_RALT,
MOD_IDX_RCONTROL,
MOD_IDX_LCONTROL,
MOD_IDX_SCROLLLK,
MOD_IDX_LVL5,
MOD_IDX_ALSO_ALTGR, // Not used on my layout
MOD_IDX_META,
MOD_IDX_SUPER,
MOD_IDX_HYPER,
MOD_IDX_LAST,
};
void
emit_key_activated (EekboardContextService *manager, LevelKeyboard *keyboard,
guint keycode,
gboolean pressed, uint32_t timestamp);
#endif // KEYEMITTER_H

View File

@ -1,7 +1,7 @@
project( project(
'squeekboard', 'squeekboard',
'c', 'rust', 'c', 'rust',
version: '1.9.3', version: '1.4.0',
license: 'GPLv3', license: 'GPLv3',
meson_version: '>=0.51.0', meson_version: '>=0.51.0',
default_options: [ default_options: [
@ -19,14 +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',
'-Wformat-nonliteral',
'-Wformat-security',
'-Winit-self',
'-Wmaybe-uninitialized',
'-Wold-style-definition',
'-Wredundant-decls',
'-Wstrict-prototypes',
'-Wunused-function',
], ],
language: 'c' language: 'c'
) )
@ -48,9 +40,7 @@ else
endif endif
prefix = get_option('prefix') prefix = get_option('prefix')
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
@ -69,38 +59,12 @@ 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']
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')
subdir('data') subdir('data')
subdir('protocols') subdir('protocols')
subdir('eek')
subdir('src') subdir('src')
subdir('tools')
subdir('tests') subdir('tests')

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

@ -6,15 +6,12 @@ use std::ffi::CString;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct KeySym(pub String); pub struct KeySym(pub String);
/// Use to switch views /// Use to switch layouts
type View = String; type Level = 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,
} }
@ -23,40 +20,21 @@ pub enum Modifier {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Action { pub enum Action {
/// Switch to this view /// Switch to this view
SetView(View), SetLevel(Level),
/// Switch to a view and latch /// Switch to a view and latch
LockView { LockLevel {
lock: View, lock: Level,
/// When unlocked by pressing it or emitting a key /// When unlocked by pressing it or emitting a key
unlock: View, unlock: Level,
}, },
/// 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

@ -1,7 +1,5 @@
/**! The parsing of the data files for layouts */ /**! The parsing of the data files for layouts */
// TODO: find a nice way to make sure non-positive sizes don't break layouts
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{ HashMap, HashSet }; use std::collections::{ HashMap, HashSet };
use std::env; use std::env;
@ -15,24 +13,23 @@ use std::vec::Vec;
use xkbcommon::xkb; use xkbcommon::xkb;
use ::action;
use ::keyboard::{ use ::keyboard::{
KeyState, PressType, KeyState, PressType,
generate_keymap, generate_keycodes, FormattingError generate_keymap, generate_keycodes, FormattingError
}; };
use ::layout; use ::layout;
use ::layout::ArrangementKind; use ::layout::ArrangementKind;
use ::logging;
use ::resources; use ::resources;
use ::util::c::as_str; use ::util::c::as_str;
use ::util::hash_map_map; use ::util::hash_map_map;
use ::xdg; use ::xdg;
// traits, derives // traits, derives
use serde::Deserialize;
use std::io::BufReader; use std::io::BufReader;
use std::iter::FromIterator; use std::iter::FromIterator;
use ::logging::Warn; use serde::Deserialize;
use util::WarningHandler;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
@ -155,10 +152,18 @@ fn list_layout_sources(
ret ret
} }
struct PrintWarnings;
impl WarningHandler for PrintWarnings {
fn handle(&mut self, warning: &str) {
println!("{}", warning);
}
}
fn load_layout_data(source: DataSource) fn load_layout_data(source: DataSource)
-> Result<::layout::LayoutData, LoadError> -> Result<::layout::LayoutData, LoadError>
{ {
let handler = logging::Print {}; let handler = PrintWarnings{};
match source { match source {
DataSource::File(path) => { DataSource::File(path) => {
Layout::from_file(path.clone()) Layout::from_file(path.clone())
@ -191,21 +196,16 @@ fn load_layout_data_with_fallback(
( (
LoadError::BadData(Error::Missing(e)), LoadError::BadData(Error::Missing(e)),
DataSource::File(file) DataSource::File(file)
) => log_print!( ) => eprintln!( // TODO: print in debug logging level
logging::Level::Debug,
"Tried file {:?}, but it's missing: {}", "Tried file {:?}, but it's missing: {}",
file, e file, e
), ),
(e, source) => log_print!( (e, source) => eprintln!(
logging::Level::Warning,
"Failed to load layout from {}: {}, skipping", "Failed to load layout from {}: {}, skipping",
source, e source, e
), ),
}, },
Ok(layout) => { Ok(layout) => return (kind, layout),
log_print!(logging::Level::Info, "Loaded layout {}", source);
return (kind, layout);
}
} }
} }
@ -216,20 +216,21 @@ fn load_layout_data_with_fallback(
#[derive(Debug, Deserialize, PartialEq)] #[derive(Debug, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Layout { pub struct Layout {
#[serde(default)] /// FIXME: deprecate in favor of margins
margins: Margins, bounds: Bounds,
views: HashMap<String, Vec<ButtonIds>>, views: HashMap<String, Vec<ButtonIds>>,
#[serde(default)] #[serde(default)]
buttons: HashMap<String, ButtonMeta>, buttons: HashMap<String, ButtonMeta>,
outlines: HashMap<String, Outline> outlines: HashMap<String, Outline>
} }
#[derive(Debug, Clone, Deserialize, PartialEq, Default)] #[derive(Debug, Clone, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
struct Margins { struct Bounds {
top: f64, x: f64,
bottom: f64, y: f64,
side: f64, width: f64,
height: f64,
} }
/// Buttons are embedded in a single string /// Buttons are embedded in a single string
@ -240,20 +241,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,30 +266,13 @@ 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)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
struct Outline { struct Outline {
width: f64, /// FIXME: replace with Size
height: f64, bounds: Bounds,
} }
/// Errors encountered loading the layout into yaml /// Errors encountered loading the layout into yaml
@ -359,7 +337,7 @@ impl Layout {
serde_yaml::from_reader(infile).map_err(Error::Yaml) serde_yaml::from_reader(infile).map_err(Error::Yaml)
} }
pub fn build<H: logging::Handler>(self, mut warning_handler: H) pub fn build<H: WarningHandler>(self, mut warning_handler: H)
-> (Result<::layout::LayoutData, FormattingError>, H) -> (Result<::layout::LayoutData, FormattingError>, H)
{ {
let button_names = self.views.values() let button_names = self.views.values()
@ -410,16 +388,13 @@ impl Layout {
) )
}).collect() }).collect()
}, },
action::Action::Erase => vec![
*keymap.get("BackSpace")
.expect(&format!("BackSpace missing from keymap")),
],
_ => Vec::new(), _ => Vec::new(),
}; };
( (
name.into(), name.into(),
KeyState { KeyState {
pressed: PressType::Released, pressed: PressType::Released,
locked: false,
keycodes, keycodes,
action, action,
} }
@ -444,8 +419,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| {
@ -459,7 +434,8 @@ impl Layout {
&mut warning_handler, &mut warning_handler,
)) ))
}); });
layout::Row { ::layout::Row {
angle: 0,
buttons: add_offsets( buttons: add_offsets(
buttons, buttons,
|button| button.size.width, |button| button.size.width,
@ -472,25 +448,8 @@ impl Layout {
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_width()) / 2.0,
y: (total_size.height - view.get_height()) / 2.0,
},
view,
),
)))
};
( (
Ok(::layout::LayoutData { Ok(::layout::LayoutData {
@ -501,10 +460,10 @@ impl Layout {
}, },
// FIXME: use a dedicated field // FIXME: use a dedicated field
margins: layout::Margins { margins: layout::Margins {
top: self.margins.top, top: self.bounds.x,
left: self.margins.side, left: self.bounds.y,
bottom: self.margins.bottom, bottom: 0.0,
right: self.margins.side, right: self.bounds.y,
}, },
}), }),
warning_handler, warning_handler,
@ -512,7 +471,7 @@ impl Layout {
} }
} }
fn create_action<H: logging::Handler>( fn create_action<H: WarningHandler>(
button_info: &HashMap<String, ButtonMeta>, button_info: &HashMap<String, ButtonMeta>,
name: &str, name: &str,
view_names: Vec<&String>, view_names: Vec<&String>,
@ -530,35 +489,27 @@ 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(&format!(
logging::Level::Warning, "Button {} has more than one of (action, keysym, text)",
&format!( name
"Button {} has more than one of (action, keysym, text, modifier)", ));
name,
),
);
SubmitData::Text("".into()) SubmitData::Text("".into())
}, },
}; };
fn filter_view_name<H: logging::Handler>( fn filter_view_name<H: WarningHandler>(
button_name: &str, button_name: &str,
view_name: String, view_name: String,
view_names: &Vec<&String>, view_names: &Vec<&String>,
@ -567,13 +518,10 @@ fn create_action<H: logging::Handler>(
if view_names.contains(&&view_name) { if view_names.contains(&&view_name) {
view_name view_name
} else { } else {
warning_handler.handle( warning_handler.handle(&format!("Button {} switches to missing view {}",
logging::Level::Warning, button_name,
&format!("Button {} switches to missing view {}", view_name,
button_name, ));
view_name,
),
);
"base".into() "base".into()
} }
} }
@ -581,7 +529,7 @@ fn create_action<H: logging::Handler>(
match submission { match submission {
SubmitData::Action( SubmitData::Action(
Action::SetView(view_name) Action::SetView(view_name)
) => ::action::Action::SetView( ) => ::action::Action::SetLevel(
filter_view_name( filter_view_name(
name, view_name.clone(), &view_names, name, view_name.clone(), &view_names,
warning_handler, warning_handler,
@ -589,7 +537,7 @@ fn create_action<H: logging::Handler>(
), ),
SubmitData::Action(Action::Locking { SubmitData::Action(Action::Locking {
lock_view, unlock_view lock_view, unlock_view
}) => ::action::Action::LockView { }) => ::action::Action::LockLevel {
lock: filter_view_name( lock: filter_view_name(
name, name,
lock_view.clone(), lock_view.clone(),
@ -606,31 +554,33 @@ 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(
match keysym_valid(keysym.as_str()) { match keysym_valid(keysym.as_str()) {
true => keysym.clone(), true => keysym.clone(),
false => { false => {
warning_handler.handle( warning_handler.handle(&format!(
logging::Level::Warning, "Keysym name invalid: {}",
&format!( keysym,
"Keysym name invalid: {}", ));
keysym,
),
);
"space".into() // placeholder "space".into() // placeholder
}, },
} }
)), )),
}, },
SubmitData::Text(text) => ::action::Action::Submit { SubmitData::Text(text) => ::action::Action::Submit {
text: CString::new(text.clone()).or_warn( text: {
warning_handler, CString::new(text.clone())
logging::Problem::Warning, .map_err(|e| {
&format!("Text {} contains problems", text), warning_handler.handle(&format!(
), "Text {} contains problems: {:?}",
text,
e
));
e
}).ok()
},
keys: text.chars().map(|codepoint| { keys: text.chars().map(|codepoint| {
let codepoint_string = codepoint.to_string(); let codepoint_string = codepoint.to_string();
::action::KeySym(match keysym_valid(codepoint_string.as_str()) { ::action::KeySym(match keysym_valid(codepoint_string.as_str()) {
@ -638,33 +588,13 @@ 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(),
}
},
},
} }
} }
/// TODO: Since this will receive user-provided data, /// TODO: Since this will receive user-provided data,
/// all .expect() on them should be turned into soft fails /// all .expect() on them should be turned into soft fails
fn create_button<H: logging::Handler>( fn create_button<H: WarningHandler>(
button_info: &HashMap<String, ButtonMeta>, button_info: &HashMap<String, ButtonMeta>,
outlines: &HashMap<String, Outline>, outlines: &HashMap<String, Outline>,
name: &str, name: &str,
@ -688,11 +618,14 @@ fn create_button<H: logging::Handler>(
} else if let Some(text) = &button_meta.text { } else if let Some(text) = &button_meta.text {
::layout::Label::Text( ::layout::Label::Text(
CString::new(text.as_str()) CString::new(text.as_str())
.or_warn( .unwrap_or_else(|e| {
warning_handler, warning_handler.handle(&format!(
logging::Problem::Warning, "Text {} is invalid: {}",
&format!("Text {} is invalid", text), text,
).unwrap_or_else(|| CString::new("").unwrap()) e,
));
CString::new("").unwrap()
})
) )
} else { } else {
::layout::Label::Text(cname.clone()) ::layout::Label::Text(cname.clone())
@ -703,10 +636,7 @@ fn create_button<H: logging::Handler>(
if outlines.contains_key(outline) { if outlines.contains_key(outline) {
outline.clone() outline.clone()
} else { } else {
warning_handler.handle( warning_handler.handle(&format!("Outline named {} does not exist! Using default for button {}", outline, name));
logging::Level::Warning,
&format!("Outline named {} does not exist! Using default for button {}", outline, name)
);
"default".into() "default".into()
} }
} }
@ -715,19 +645,22 @@ fn create_button<H: logging::Handler>(
let outline = outlines.get(&outline_name) let outline = outlines.get(&outline_name)
.map(|outline| (*outline).clone()) .map(|outline| (*outline).clone())
.or_warn( .unwrap_or_else(|| {
warning_handler, warning_handler.handle(
logging::Problem::Warning, &format!("No default outline defined! Using 1x1!")
"No default outline defined! Using 1x1!", );
).unwrap_or(Outline { width: 1f64, height: 1f64 }); Outline {
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
}
});
layout::Button { layout::Button {
name: cname, name: cname,
outline_name: CString::new(outline_name).expect("Bad outline"), outline_name: CString::new(outline_name).expect("Bad outline"),
// TODO: do layout before creating buttons // TODO: do layout before creating buttons
size: layout::Size { size: layout::Size {
width: outline.width, width: outline.bounds.width,
height: outline.height, height: outline.bounds.height,
}, },
label: label, label: label,
state: state, state: state,
@ -739,23 +672,21 @@ mod tests {
use super::*; use super::*;
use std::error::Error as ErrorTrait; use std::error::Error as ErrorTrait;
use ::logging::ProblemPanic;
const THIS_FILE: &str = file!(); struct PanicWarn;
fn path_from_root(file: &'static str) -> PathBuf { impl WarningHandler for PanicWarn {
PathBuf::from(THIS_FILE) fn handle(&mut self, warning: &str) {
.parent().unwrap() panic!("{}", warning);
.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 }, bounds: Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 },
views: hashmap!( views: hashmap!(
"base".into() => vec!("test".into()), "base".into() => vec!("test".into()),
), ),
@ -765,13 +696,16 @@ 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,
} }
}, },
outlines: hashmap!{ outlines: hashmap!{
"default".into() => Outline { width: 0f64, height: 0f64 }, "default".into() => Outline {
bounds: Bounds {
x: 0f64, y: 0f64, width: 0f64, height: 0f64
},
}
}, },
} }
); );
@ -780,7 +714,7 @@ 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) => {
@ -798,7 +732,7 @@ 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) => {
@ -817,12 +751,12 @@ 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(PanicWarn).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.buttons[0].1 .buttons[0].1
.label, .label,
@ -832,12 +766,12 @@ 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(PanicWarn).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.buttons[0].1 .buttons[0].1
.label, .label,
@ -848,12 +782,12 @@ 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(PanicWarn).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.buttons[0].1 .buttons[0].1
.state.borrow() .state.borrow()
@ -865,7 +799,7 @@ mod tests {
#[test] #[test]
fn parsing_fallback() { fn parsing_fallback() {
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME) assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
.map(|layout| layout.build(ProblemPanic).0.unwrap()) .map(|layout| layout.build(PanicWarn).0.unwrap())
.is_ok() .is_ok()
); );
} }
@ -907,14 +841,13 @@ 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,
} }
}, },
".", ".",
Vec::new(), Vec::new(),
&mut ProblemPanic, &mut PanicWarn,
), ),
::action::Action::Submit { ::action::Action::Submit {
text: Some(CString::new(".").unwrap()), text: Some(CString::new(".").unwrap()),
@ -922,21 +855,4 @@ mod tests {
}, },
); );
} }
#[test]
fn test_layout_margins() {
let out = Layout::from_file(path_from_root("tests/layout_margins.yaml"))
.unwrap()
.build(ProblemPanic).0
.unwrap();
assert_eq!(
out.margins,
layout::Margins {
top: 1.0,
bottom: 3.0,
left: 2.0,
right: 2.0,
}
);
}
} }

View File

@ -1,124 +0,0 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "dbus.h"
#include <stdio.h>
#include <gio/gio.h>
void
dbus_handler_destroy(DBusHandler *service)
{
g_free (service->object_path);
if (service->connection) {
if (service->registration_id > 0) {
g_dbus_connection_unregister_object (service->connection,
service->registration_id);
service->registration_id = 0;
}
g_object_unref (service->connection);
service->connection = NULL;
}
if (service->introspection_data) {
g_dbus_node_info_unref (service->introspection_data);
service->introspection_data = NULL;
}
if (service->context) {
g_signal_handlers_disconnect_by_data (service->context, service);
service->context = NULL;
}
free(service);
}
static gboolean
handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
gboolean arg_visible, gpointer user_data) {
DBusHandler *service = user_data;
if (service->context) {
if (arg_visible) {
server_context_service_show_keyboard (service->context);
} else {
server_context_service_hide_keyboard (service->context);
}
}
sm_puri_osk0_complete_set_visible(object, invocation);
return TRUE;
}
static void on_visible(DBusHandler *service,
GParamSpec *pspec,
ServerContextService *context)
{
(void)pspec;
gboolean visible;
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (context));
g_object_get (context, "visible", &visible, NULL);
sm_puri_osk0_set_visible(service->dbus_interface, visible);
}
DBusHandler *
dbus_handler_new (GDBusConnection *connection,
const gchar *object_path)
{
DBusHandler *self = calloc(1, sizeof(DBusHandler));
self->object_path = g_strdup(object_path);
self->connection = connection;
self->dbus_interface = sm_puri_osk0_skeleton_new();
g_signal_connect(self->dbus_interface, "handle-set-visible",
G_CALLBACK(handle_set_visible), self);
if (self->connection && self->object_path) {
GError *error = NULL;
if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(self->dbus_interface),
self->connection,
self->object_path,
&error)) {
g_warning("Error registering dbus object: %s\n", error->message);
g_clear_error(&error);
// TODO: return an error
}
}
return self;
}
void
dbus_handler_set_ui_context(DBusHandler *service,
ServerContextService *context)
{
g_return_if_fail (!service->context);
service->context = context;
g_signal_connect_swapped (service->context,
"notify::visible",
G_CALLBACK(on_visible),
service);
}

View File

@ -1,48 +0,0 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
* Copyright (C) 2019-2020 Purism, SPC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DBUS_H_
#define DBUS_H_ 1
#include "server-context-service.h"
#include "sm.puri.OSK0.h"
G_BEGIN_DECLS
#define DBUS_SERVICE_PATH "/sm/puri/OSK0"
#define DBUS_SERVICE_INTERFACE "sm.puri.OSK0"
typedef struct _DBusHandler
{
GDBusConnection *connection;
SmPuriOSK0 *dbus_interface;
GDBusNodeInfo *introspection_data;
guint registration_id;
char *object_path;
ServerContextService *context; // unowned reference
} DBusHandler;
DBusHandler * dbus_handler_new (GDBusConnection *connection,
const gchar *object_path);
void dbus_handler_set_ui_context(DBusHandler *service,
ServerContextService *context);
void dbus_handler_destroy(DBusHandler*);
G_END_DECLS
#endif /* DBUS_H_ */

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

@ -1,17 +1,9 @@
#include "submission.h" #include "imservice.h"
#include <glib.h> #include <glib.h>
struct imservice; #include "eekboard/eekboard-context-service.h"
void imservice_handle_input_method_activate(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_input_method_deactivate(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_surrounding_text(void *data, struct zwp_input_method_v2 *input_method,
const char *text, uint32_t cursor, uint32_t anchor);
void imservice_handle_done(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_content_type(void *data, struct zwp_input_method_v2 *input_method, uint32_t hint, uint32_t purpose);
void imservice_handle_text_change_cause(void *data, struct zwp_input_method_v2 *input_method, uint32_t cause);
void imservice_handle_unavailable(void *data, struct zwp_input_method_v2 *input_method);
static const struct zwp_input_method_v2_listener input_method_listener = { static const struct zwp_input_method_v2_listener input_method_listener = {
.activate = imservice_handle_input_method_activate, .activate = imservice_handle_input_method_activate,
@ -19,51 +11,29 @@ static const struct zwp_input_method_v2_listener input_method_listener = {
.surrounding_text = imservice_handle_surrounding_text, .surrounding_text = imservice_handle_surrounding_text,
.text_change_cause = imservice_handle_text_change_cause, .text_change_cause = imservice_handle_text_change_cause,
.content_type = imservice_handle_content_type, .content_type = imservice_handle_content_type,
.done = imservice_handle_done, .done = imservice_handle_commit_state,
.unavailable = imservice_handle_unavailable, .unavailable = imservice_handle_unavailable,
}; };
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager, struct imservice* get_imservice(EekboardContextService *context,
struct zwp_virtual_keyboard_manager_v1 *vkmanager, struct zwp_input_method_manager_v2 *manager,
struct wl_seat *seat, struct wl_seat *seat) {
EekboardContextService *state) { struct zwp_input_method_v2 *im = zwp_input_method_manager_v2_get_input_method(manager, seat);
struct zwp_input_method_v2 *im = NULL; struct imservice *imservice = imservice_new(im, context);
if (immanager) {
im = zwp_input_method_manager_v2_get_input_method(immanager, seat);
}
struct zwp_virtual_keyboard_v1 *vk = NULL;
if (vkmanager) {
vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmanager, seat);
}
return submission_new(im, vk, state);
}
/// Un-inlined /* Add a listener, passing the imservice instance to make it available to
struct zwp_input_method_v2 *imservice_manager_get_input_method(struct zwp_input_method_manager_v2 *manager, callbacks. */
struct wl_seat *seat) {
return zwp_input_method_manager_v2_get_input_method(manager, seat);
}
/// Un-inlined to let Rust link to it
void imservice_connect_listeners(struct zwp_input_method_v2 *im, struct imservice* imservice) {
zwp_input_method_v2_add_listener(im, &input_method_listener, imservice); zwp_input_method_v2_add_listener(im, &input_method_listener, imservice);
return imservice;
} }
void void imservice_make_visible(EekboardContextService *context) {
eek_input_method_commit_string(struct zwp_input_method_v2 *zwp_input_method_v2, const char *text) eekboard_context_service_show_keyboard (context);
{
zwp_input_method_v2_commit_string(zwp_input_method_v2, text);
} }
void void imservice_try_hide(EekboardContextService *context) {
eek_input_method_delete_surrounding_text(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t before_length, uint32_t after_length) { eekboard_context_service_hide_keyboard (context);
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,

View File

@ -0,0 +1,25 @@
#ifndef __IMSERVICE_H
#define __IMSERVICE_H
#include "input-method-unstable-v2-client-protocol.h"
#include "eek/eek-types.h"
struct imservice;
struct imservice* get_imservice(EekboardContextService *context,
struct zwp_input_method_manager_v2 *manager,
struct wl_seat *seat);
// Defined in Rust
struct imservice* imservice_new(struct zwp_input_method_v2 *im,
EekboardContextService *context);
void imservice_handle_input_method_activate(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_input_method_deactivate(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_surrounding_text(void *data, struct zwp_input_method_v2 *input_method,
const char *text, uint32_t cursor, uint32_t anchor);
void imservice_handle_commit_state(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_content_type(void *data, struct zwp_input_method_v2 *input_method, uint32_t hint, uint32_t purpose);
void imservice_handle_text_change_cause(void *data, struct zwp_input_method_v2 *input_method, uint32_t cause);
void imservice_handle_unavailable(void *data, struct zwp_input_method_v2 *input_method);
#endif

View File

@ -1,55 +1,58 @@
/*! 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::num::Wrapping; use std::num::Wrapping;
use std::string::String; use std::string::String;
use ::logging;
use ::util::c::into_cstring; use ::util::c::into_cstring;
// Traits // Traits
use std::convert::TryFrom; use std::convert::TryFrom;
use ::logging::Warn;
/// 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::*;
use std::os::raw::{c_char, c_void}; use std::os::raw::{c_char, c_void};
pub use ::submission::c::UIManager;
pub use ::submission::c::StateManager;
// The following defined in C // The following defined in C
/// struct zwp_input_method_v2* /// struct zwp_input_method_v2*
#[repr(transparent)] #[repr(transparent)]
pub struct InputMethod(*const c_void); pub struct InputMethod(*const c_void);
/// EekboardContextService*
#[repr(transparent)]
pub struct UIManager(*const c_void);
#[no_mangle] #[no_mangle]
extern "C" { extern "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 fn eekboard_context_service_set_hint_purpose(imservice: *const UIManager, hint: u32, purpose: u32);
pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService); fn eekboard_context_service_show_keyboard(imservice: *const UIManager);
pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char); fn eekboard_context_service_hide_keyboard(imservice: *const UIManager);
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 server_context_service_show_keyboard(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
#[no_mangle]
pub unsafe extern "C"
fn imservice_new(im: *const InputMethod, ui_manager: *const UIManager) -> *mut IMService {
Box::<IMService>::into_raw(Box::new(
IMService {
im: im,
ui_manager: ui_manager,
pending: IMProtocolState::default(),
current: IMProtocolState::default(),
preedit_string: String::new(),
serial: Wrapping(0u32),
}
))
}
// TODO: is unsafe needed here? // TODO: is unsafe needed here?
#[no_mangle] #[no_mangle]
pub extern "C" pub unsafe extern "C"
fn imservice_handle_input_method_activate(imservice: *mut IMService, fn imservice_handle_input_method_activate(imservice: *mut IMService,
im: *const InputMethod) im: *const InputMethod)
{ {
@ -62,7 +65,7 @@ pub mod c {
} }
#[no_mangle] #[no_mangle]
pub extern "C" pub unsafe extern "C"
fn imservice_handle_input_method_deactivate(imservice: *mut IMService, fn imservice_handle_input_method_deactivate(imservice: *mut IMService,
im: *const InputMethod) im: *const InputMethod)
{ {
@ -74,7 +77,7 @@ pub mod c {
} }
#[no_mangle] #[no_mangle]
pub extern "C" pub unsafe extern "C"
fn imservice_handle_surrounding_text(imservice: *mut IMService, fn imservice_handle_surrounding_text(imservice: *mut IMService,
im: *const InputMethod, im: *const InputMethod,
text: *const c_char, cursor: u32, _anchor: u32) text: *const c_char, cursor: u32, _anchor: u32)
@ -90,7 +93,7 @@ pub mod c {
} }
#[no_mangle] #[no_mangle]
pub extern "C" pub unsafe extern "C"
fn imservice_handle_content_type(imservice: *mut IMService, fn imservice_handle_content_type(imservice: *mut IMService,
im: *const InputMethod, im: *const InputMethod,
hint: u32, purpose: u32) hint: u32, purpose: u32)
@ -98,27 +101,27 @@ pub mod c {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
imservice.pending = IMProtocolState { imservice.pending = IMProtocolState {
content_hint: { content_hint: {
ContentHint::from_bits(hint) ContentHint::from_bits(hint).unwrap_or_else(|| {
.or_print( let out = ContentHint::from_bits_truncate(hint);
logging::Problem::Warning, eprintln!(
"Received invalid hint flags", "Warning: received hint flags with unknown bits set: 0x{:x}",
) hint ^ out.bits(),
.unwrap_or(ContentHint::NONE) );
out
})
}, },
content_purpose: { content_purpose: {
ContentPurpose::try_from(purpose) ContentPurpose::try_from(purpose).unwrap_or_else(|_e| {
.or_print( eprintln!("Warning: Received invalid purpose value");
logging::Problem::Warning, ContentPurpose::Normal
"Received invalid purpose value", })
)
.unwrap_or(ContentPurpose::Normal)
}, },
..imservice.pending.clone() ..imservice.pending.clone()
}; };
} }
#[no_mangle] #[no_mangle]
pub extern "C" pub unsafe extern "C"
fn imservice_handle_text_change_cause(imservice: *mut IMService, fn imservice_handle_text_change_cause(imservice: *mut IMService,
im: *const InputMethod, im: *const InputMethod,
cause: u32) cause: u32)
@ -126,68 +129,71 @@ pub mod c {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
imservice.pending = IMProtocolState { imservice.pending = IMProtocolState {
text_change_cause: { text_change_cause: {
ChangeCause::try_from(cause) ChangeCause::try_from(cause).unwrap_or_else(|_e| {
.or_print( eprintln!("Warning: received invalid cause value");
logging::Problem::Warning, ChangeCause::InputMethod
"Received invalid cause value", })
)
.unwrap_or(ChangeCause::InputMethod)
}, },
..imservice.pending.clone() ..imservice.pending.clone()
}; };
} }
#[no_mangle] #[no_mangle]
pub extern "C" pub unsafe extern "C"
fn imservice_handle_done(imservice: *mut IMService, fn imservice_handle_commit_state(imservice: *mut IMService,
im: *const InputMethod) im: *const InputMethod)
{ {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
let active_changed = imservice.current.active ^ imservice.pending.active; let active_changed = imservice.current.active ^ imservice.pending.active;
fn is_visible(state: &IMProtocolState) -> bool {
state.active
&& !state.content_hint.contains(
ContentHint::ON_SCREEN_INPUT_PROVIDED
)
}
let visible_changed = is_visible(&imservice.current)
^ is_visible(&imservice.pending);
imservice.serial += Wrapping(1u32);
imservice.current = imservice.pending.clone(); imservice.current = imservice.pending.clone();
imservice.pending = IMProtocolState { imservice.pending = IMProtocolState {
active: imservice.current.active, active: imservice.current.active,
..IMProtocolState::default() ..IMProtocolState::default()
}; };
if active_changed { if active_changed && imservice.current.active {
if imservice.current.active { eekboard_context_service_set_hint_purpose(
if let Some(ui) = imservice.ui_manager { imservice.ui_manager,
unsafe { server_context_service_show_keyboard(ui); } imservice.current.content_hint.bits(),
} imservice.current.content_purpose.clone() as u32,
unsafe { );
eekboard_context_service_set_hint_purpose( }
imservice.state_manager,
imservice.current.content_hint.bits(), if visible_changed {
imservice.current.content_purpose.clone() as u32, if is_visible(&imservice.current) {
); eekboard_context_service_show_keyboard(imservice.ui_manager);
}
} else { } else {
if let Some(ui) = imservice.ui_manager { eekboard_context_service_hide_keyboard(imservice.ui_manager);
unsafe { server_context_service_hide_keyboard(ui); }
}
} }
} }
} }
// TODO: this is really untested
#[no_mangle] #[no_mangle]
pub extern "C" pub unsafe extern "C"
fn imservice_handle_unavailable(imservice: *mut IMService, fn imservice_handle_unavailable(imservice: *mut IMService,
im: *mut InputMethod) im: *mut InputMethod)
{ {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
unsafe { imservice_destroy_im(im); } imservice_destroy_im(im);
// no need to care about proper double-buffering, // no need to care about proper double-buffering,
// the keyboard is already decommissioned // the keyboard is already decommissioned
imservice.current.active = false; imservice.current.active = false;
if let Some(ui) = imservice.ui_manager { eekboard_context_service_hide_keyboard(imservice.ui_manager);
unsafe { server_context_service_hide_keyboard(ui); } }
}
}
// FIXME: destroy and deallocate // FIXME: destroy and deallocate
@ -232,6 +238,7 @@ bitflags!{
const SENSITIVE_DATA = 0x80; const SENSITIVE_DATA = 0x80;
const LATIN = 0x100; const LATIN = 0x100;
const MULTILINE = 0x200; const MULTILINE = 0x200;
const ON_SCREEN_INPUT_PROVIDED = 0x400;
} }
} }
@ -259,17 +266,10 @@ pub enum ContentPurpose {
Terminal = 13, Terminal = 13,
} }
// Utilities from ::logging need a printable error type
pub struct UnrecognizedValue;
impl fmt::Display for UnrecognizedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unrecognized value")
}
}
impl TryFrom<u32> for ContentPurpose { impl TryFrom<u32> for ContentPurpose {
type Error = UnrecognizedValue; // There's only one way to fail: number not in protocol,
// so no special error type is needed
type Error = ();
fn try_from(num: u32) -> Result<Self, Self::Error> { fn try_from(num: u32) -> Result<Self, Self::Error> {
use self::ContentPurpose::*; use self::ContentPurpose::*;
match num { match num {
@ -287,7 +287,7 @@ impl TryFrom<u32> for ContentPurpose {
11 => Ok(Time), 11 => Ok(Time),
12 => Ok(Datetime), 12 => Ok(Datetime),
13 => Ok(Terminal), 13 => Ok(Terminal),
_ => Err(UnrecognizedValue), _ => Err(()),
} }
} }
} }
@ -300,12 +300,14 @@ pub enum ChangeCause {
} }
impl TryFrom<u32> for ChangeCause { impl TryFrom<u32> for ChangeCause {
type Error = UnrecognizedValue; // There's only one way to fail: number not in protocol,
// so no special error type is needed
type Error = ();
fn try_from(num: u32) -> Result<Self, Self::Error> { fn try_from(num: u32) -> Result<Self, Self::Error> {
match num { match num {
0 => Ok(ChangeCause::InputMethod), 0 => Ok(ChangeCause::InputMethod),
1 => Ok(ChangeCause::Other), 1 => Ok(ChangeCause::Other),
_ => Err(UnrecognizedValue) _ => Err(())
} }
} }
} }
@ -336,92 +338,12 @@ 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, ui_manager: *const c::UIManager,
/// Unowned reference. Be careful, it's shared with C at large
pub ui_manager: Option<*const c::UIManager>,
pending: IMProtocolState, pending: IMProtocolState,
current: IMProtocolState, // turn current into an idiomatic representation? current: IMProtocolState, // turn current into an idiomatic representation?
preedit_string: String, preedit_string: String,
serial: Wrapping<u32>, serial: Wrapping<u32>,
} }
pub enum SubmitError {
/// The input method had not been activated
NotActive,
}
impl IMService {
pub fn new(
im: *mut c::InputMethod,
state_manager: *const c::StateManager,
) -> Box<IMService> {
// IMService will be referenced to by C,
// so it needs to stay in the same place in memory via Box
let imservice = Box::new(IMService {
im,
ui_manager: None,
state_manager,
pending: IMProtocolState::default(),
current: IMProtocolState::default(),
preedit_string: String::new(),
serial: Wrapping(0u32),
});
unsafe {
c::imservice_connect_listeners(
im,
imservice.as_ref() as *const 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,17 +1,13 @@
/*! 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::rc::Rc;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action; use ::action::Action;
use ::logging;
// Traits
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
@ -21,64 +17,16 @@ pub enum PressType {
Pressed = 1, Pressed = 1,
} }
pub type KeyCode = u32;
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,
pub keycodes: Vec<KeyCode>, /// A cache of raw keycodes derived from Action::Sumbit given a keymap
pub keycodes: Vec<u32>,
/// 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 {
#[must_use]
pub fn into_released(self) -> KeyState {
KeyState {
pressed: PressType::Released,
..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=&'a str>>( fn sorted<'a, I: Iterator<Item=&'a str>>(
iter: I iter: I
@ -94,10 +42,9 @@ fn sorted<'a, I: Iterator<Item=&'a str>>(
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>( pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C key_names: C
) -> HashMap<String, u32> { ) -> HashMap<String, u32> {
let special_keysyms = ["BackSpace", "Return"].iter().map(|&s| s);
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().chain(special_keysyms)) sorted(key_names.into_iter())
.map(|name| String::from(name)) .map(|name| String::from(name))
.zip(9..) .zip(9..)
) )
@ -124,10 +71,7 @@ impl From<io::Error> for FormattingError {
} }
} }
/// Generates a de-facto single level keymap. /// Generates a de-facto single level keymap. TODO: actually drop second level
// TODO: don't rely on keys and their order,
// but rather on what keysyms and keycodes are in use.
// Iterating actions makes it hard to deduplicate keysyms.
pub fn generate_keymap( pub fn generate_keymap(
keystates: &HashMap::<String, KeyState> keystates: &HashMap::<String, KeyState>
) -> Result<String, FormattingError> { ) -> Result<String, FormattingError> {
@ -142,40 +86,17 @@ pub fn generate_keymap(
)?; )?;
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
match &state.action { if let Action::Submit { text: _, keys } = &state.action {
Action::Submit { text: _, keys } => { if let 0 = keys.len() { eprintln!("Key {} has no keysyms", name); };
if let 0 = keys.len() { for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
log_print!(
logging::Level::Warning,
"Key {} has no keysyms", name,
);
};
for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
write!(
buf,
"
<{}> = {};",
named_keysym.0,
keycode,
)?;
}
},
Action::Erase => {
let mut keycodes = state.keycodes.iter();
write!( write!(
buf, buf,
" "
<BackSpace> = {};", <{}> = {};",
keycodes.next().expect("Erase key has no keycode"), named_keysym.0,
keycode,
)?; )?;
if let Some(_) = keycodes.next() { }
log_print!(
logging::Level::Bug,
"Erase key has multiple keycodes",
);
}
},
_ => {},
} }
} }
@ -187,12 +108,10 @@ pub fn generate_keymap(
xkb_symbols \"squeekboard\" {{ xkb_symbols \"squeekboard\" {{
name[Group1] = \"Letters\"; name[Group1] = \"Letters\";
name[Group2] = \"Numbers/Symbols\"; name[Group2] = \"Numbers/Symbols\";"
key <BackSpace> {{ [ BackSpace ] }};"
)?; )?;
for (_name, state) in keystates.iter() { for (name, state) in keystates.iter() {
if let Action::Submit { text: _, keys } = &state.action { if let Action::Submit { text: _, keys } = &state.action {
for keysym in keys.iter() { for keysym in keys.iter() {
write!( write!(
@ -247,6 +166,7 @@ mod tests {
keys: vec!(KeySym("a".into()), KeySym("c".into())), keys: vec!(KeySym("a".into()), KeySym("c".into())),
}, },
keycodes: vec!(9, 10), keycodes: vec!(9, 10),
locked: false,
pressed: PressType::Released, pressed: PressType::Released,
}, },
}).unwrap(); }).unwrap();

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*);
@ -43,26 +33,19 @@ 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*);
void squeek_layout_release(struct squeek_layout *layout, void squeek_layout_release(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
struct submission *submission,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, uint32_t timestamp,
EekboardContextService *manager,
EekGtkKeyboard *ui_keyboard); EekGtkKeyboard *ui_keyboard);
void squeek_layout_release_all_only(struct squeek_layout *layout, void squeek_layout_release_all_only(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp);
struct submission *submission, void squeek_layout_depress(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
uint32_t timestamp);
void squeek_layout_depress(struct squeek_layout *layout,
struct submission *submission,
double x_widget, double y_widget, double x_widget, double y_widget,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, EekGtkKeyboard *ui_keyboard); uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
void squeek_layout_drag(struct squeek_layout *layout, void squeek_layout_drag(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard,
struct submission *submission,
double x_widget, double y_widget, double x_widget, double y_widget,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, EekboardContextService *manager, uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
EekGtkKeyboard *ui_keyboard); void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission);
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

@ -20,21 +20,16 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{ HashMap, HashSet }; use std::collections::{ HashMap, HashSet };
use std::ffi::CString; use std::ffi::CString;
use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::vec::Vec; use std::vec::Vec;
use ::action::Action; use ::action::Action;
use ::drawing; use ::drawing;
use ::keyboard::KeyState; use ::keyboard::{ KeyState, PressType };
use ::logging; use ::submission::{ Timestamp, VirtualKeyboard };
use ::manager;
use ::submission::{ Submission, SubmitData, Timestamp };
use ::util::find_max_double; use ::util::find_max_double;
// Traits
use std::borrow::Borrow; use std::borrow::Borrow;
use ::logging::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
@ -52,14 +47,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,12 +94,12 @@ 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)
} }
} }
/// Translate and then scale /// Scale + translate
#[repr(C)] #[repr(C)]
pub struct Transformation { pub struct Transformation {
pub origin_x: f64, pub origin_x: f64,
@ -121,14 +108,6 @@ pub mod c {
} }
impl Transformation { impl Transformation {
/// Applies the new transformation after this one
pub fn chain(self, next: Transformation) -> Transformation {
Transformation {
origin_x: self.origin_x + self.scale * next.origin_x,
origin_y: self.origin_y + self.scale * next.origin_y,
scale: self.scale * next.scale,
}
}
fn forward(&self, p: Point) -> Point { fn forward(&self, p: Point) -> Point {
Point { Point {
x: (p.x - self.origin_x) / self.scale, x: (p.x - self.origin_x) / self.scale,
@ -155,11 +134,6 @@ pub mod c {
} }
} }
} }
// This is constructed only in C, no need for warnings
#[allow(dead_code)]
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
@ -221,8 +195,7 @@ pub mod c {
println!("{:?}", button); println!("{:?}", button);
} }
/// Positions the layout contents within the available space. /// Positions the layout within the available space
/// The origin of the transformation is the point inside the margins.
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_calculate_transformation( fn squeek_layout_calculate_transformation(
@ -231,10 +204,15 @@ pub mod c {
allocation_height: f64, allocation_height: f64,
) -> Transformation { ) -> Transformation {
let layout = unsafe { &*layout }; let layout = unsafe { &*layout };
layout.calculate_transformation(Size { let size = layout.calculate_size();
width: allocation_width, let h_scale = allocation_width / size.width;
height: allocation_height, let v_scale = allocation_height / size.height;
}) let scale = if h_scale < v_scale { h_scale } else { v_scale };
Transformation {
origin_x: (allocation_width - (scale * size.width)) / 2.0,
origin_y: (allocation_height - (scale * size.height)) / 2.0,
scale: scale,
}
} }
#[no_mangle] #[no_mangle]
@ -257,66 +235,64 @@ 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::*;
use ::submission::c::ZwpVirtualKeyboardV1;
// This is constructed only in C, no need for warnings
#[allow(dead_code)]
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
/// Release pointer in the specified position /// Release pointer in the specified position
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_release( fn squeek_layout_release(
layout: *mut Layout, layout: *mut Layout,
submission: *mut Submission, virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
manager: manager::c::Manager,
ui_keyboard: EekGtkKeyboard, ui_keyboard: EekGtkKeyboard,
) { ) {
let time = Timestamp(time); let time = Timestamp(time);
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = unsafe { &mut *submission }; let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
let ui_backend = UIBackend {
widget_to_layout,
keyboard: ui_keyboard,
};
// The list must be copied, // The list must be copied,
// because it will be mutated in the loop // because it will be mutated in the loop
for key in layout.pressed_keys.clone() { for key in layout.pressed_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
seat::handle_release_key( seat::handle_release_key(
layout, layout,
submission, &virtual_keyboard,
Some(&ui_backend), &widget_to_layout,
time, time,
Some(manager), ui_keyboard,
key, key,
); );
} }
drawing::queue_redraw(ui_keyboard); drawing::queue_redraw(ui_keyboard);
} }
/// Release all buttons but don't redraw /// Release all buittons but don't redraw
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_release_all_only( fn squeek_layout_release_all_only(
layout: *mut Layout, layout: *mut Layout,
submission: *mut Submission, virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
time: u32, time: u32,
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = unsafe { &mut *submission }; let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
// The list must be copied, // The list must be copied,
// because it will be mutated in the loop // because it will be mutated in the loop
for key in layout.pressed_keys.clone() { for key in layout.pressed_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
seat::handle_release_key( layout.release_key(
layout, &virtual_keyboard,
submission,
None, // don't update UI
Timestamp(time),
None, // don't switch layouts
&mut key.clone(), &mut key.clone(),
Timestamp(time)
); );
} }
} }
@ -325,33 +301,31 @@ pub mod c {
pub extern "C" pub extern "C"
fn squeek_layout_depress( fn squeek_layout_depress(
layout: *mut Layout, layout: *mut Layout,
submission: *mut Submission, virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
x_widget: f64, y_widget: f64, x_widget: f64, y_widget: f64,
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
ui_keyboard: EekGtkKeyboard, ui_keyboard: EekGtkKeyboard,
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = unsafe { &mut *submission };
let point = widget_to_layout.forward( let point = widget_to_layout.forward(
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(mut state) = state {
seat::handle_press_key( layout.press_key(
layout, &VirtualKeyboard(virtual_keyboard),
submission, &mut state,
Timestamp(time), Timestamp(time),
&state,
); );
// 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);
}
}; };
} }
@ -362,27 +336,24 @@ pub mod c {
pub extern "C" pub extern "C"
fn squeek_layout_drag( fn squeek_layout_drag(
layout: *mut Layout, layout: *mut Layout,
submission: *mut Submission, virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
x_widget: f64, y_widget: f64, x_widget: f64, y_widget: f64,
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
manager: manager::c::Manager,
ui_keyboard: EekGtkKeyboard, ui_keyboard: EekGtkKeyboard,
) { ) {
let time = Timestamp(time); let time = Timestamp(time);
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = unsafe { &mut *submission }; let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
let ui_backend = UIBackend {
widget_to_layout, let point = widget_to_layout.forward(
keyboard: ui_keyboard,
};
let point = ui_backend.widget_to_layout.forward(
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
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(),
@ -390,7 +361,7 @@ pub mod c {
)}) )})
}; };
if let Some((state, _button, _view_position)) = button_info { if let Some((mut state, _button, _view_position)) = button_info {
let mut found = false; let mut found = false;
for wrapped_key in pressed { for wrapped_key in pressed {
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow(); let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
@ -399,35 +370,27 @@ pub mod c {
} else { } else {
seat::handle_release_key( seat::handle_release_key(
layout, layout,
submission, &virtual_keyboard,
Some(&ui_backend), &widget_to_layout,
time, time,
Some(manager), ui_keyboard,
key, key,
); );
} }
} }
if !found { if !found {
seat::handle_press_key( layout.press_key(&virtual_keyboard, &mut state, time);
layout,
submission,
time,
&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 {
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow(); let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
seat::handle_release_key( seat::handle_release_key(
layout, layout,
submission, &virtual_keyboard,
Some(&ui_backend), &widget_to_layout,
time, time,
Some(manager), ui_keyboard,
key, key,
); );
} }
@ -464,7 +427,7 @@ pub struct ButtonPlace<'a> {
offset: c::Point, offset: c::Point,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct Size { pub struct Size {
pub width: f64, pub width: f64,
pub height: f64, pub height: f64,
@ -496,6 +459,8 @@ pub struct Button {
pub struct Row { pub struct Row {
/// Buttons together with their offset from the left /// Buttons together with their offset from the left
pub buttons: Vec<(f64, Box<Button>)>, pub buttons: Vec<(f64, Box<Button>)>,
/// Angle is not really used anywhere...
pub angle: i32,
} }
impl Row { impl Row {
@ -562,14 +527,14 @@ impl View {
}) })
} }
pub fn get_width(&self) -> f64 { fn get_width(&self) -> f64 {
// No need to call `get_rows()`, // No need to call `get_rows()`,
// as the biggest row is the most far reaching in both directions // as the biggest row is the most far reaching in both directions
// because they are all centered. // because they are all centered.
find_max_double(self.rows.iter(), |(_offset, row)| row.get_width()) find_max_double(self.rows.iter(), |(_offset, row)| row.get_width())
} }
pub fn get_height(&self) -> f64 { fn get_height(&self) -> f64 {
self.rows.iter().next_back() self.rows.iter().next_back()
.map(|(y_offset, row)| row.get_height() + y_offset) .map(|(y_offset, row)| row.get_height() + y_offset)
.unwrap_or(0.0) .unwrap_or(0.0)
@ -586,21 +551,6 @@ impl View {
row, row,
)}).collect() )}).collect()
} }
/// Returns a size which contains all the views
/// if they are all centered on the same point.
pub fn calculate_super_size(views: Vec<&View>) -> Size {
Size {
height: find_max_double(
views.iter(),
|view| view.get_height(),
),
width: find_max_double(
views.iter(),
|view| view.get_width(),
),
}
}
} }
/// The physical characteristic of layout for the purpose of styling /// The physical characteristic of layout for the purpose of styling
@ -610,7 +560,6 @@ pub enum ArrangementKind {
Wide = 1, Wide = 1,
} }
#[derive(Debug, PartialEq)]
pub struct Margins { pub struct Margins {
pub top: f64, pub top: f64,
pub bottom: f64, pub bottom: f64,
@ -628,8 +577,7 @@ 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 keymap applicable to the contained keys. Unchangeable /// xkb keymap applicable to the contained keys. Unchangeable
@ -643,25 +591,18 @@ 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 keymap_str: CString,
pub margins: Margins, pub margins: Margins,
} }
#[derive(Debug)]
struct NoSuchView; struct NoSuchView;
impl fmt::Display for NoSuchView {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "No such view")
}
}
// Unfortunately, changes are not atomic due to mutability :( // Unfortunately, changes are not atomic due to mutability :(
// An error will not be recoverable // An error will not be recoverable
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special. // The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
@ -674,17 +615,13 @@ impl Layout {
views: data.views, views: data.views,
keymap_str: data.keymap_str, 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> {
@ -696,80 +633,97 @@ impl Layout {
} }
} }
/// Calculates size without margins fn release_key(
fn calculate_inner_size(&self) -> Size { &mut self,
View::calculate_super_size( virtual_keyboard: &VirtualKeyboard,
self.views.iter().map(|(_, (_offset, v))| v).collect() mut key: &mut Rc<RefCell<KeyState>>,
) time: Timestamp,
} ) {
if !self.pressed_keys.remove(&::util::Pointer(key.clone())) {
/// Size including margins eprintln!("Warning: key {:?} was not pressed", key);
fn calculate_size(&self) -> Size {
let inner_size = self.calculate_inner_size();
Size {
width: self.margins.left + inner_size.width + self.margins.right,
height: (
self.margins.top
+ inner_size.height
+ self.margins.bottom
),
} }
virtual_keyboard.switch(
&mut key.borrow_mut(),
PressType::Released,
time,
);
self.set_level_from_press(&mut key);
} }
pub fn calculate_transformation( fn press_key(
&self, &mut self,
available: Size, virtual_keyboard: &VirtualKeyboard,
) -> c::Transformation { key: &mut Rc<RefCell<KeyState>>,
let size = self.calculate_size(); time: Timestamp,
let h_scale = available.width / size.width; ) {
let v_scale = available.height / size.height; if !self.pressed_keys.insert(::util::Pointer(key.clone())) {
let scale = if h_scale < v_scale { h_scale } else { v_scale }; eprintln!("Warning: key {:?} was already pressed", key);
let outside_margins = c::Transformation { }
origin_x: (available.width - (scale * size.width)) / 2.0, virtual_keyboard.switch(
origin_y: (available.height - (scale * size.height)) / 2.0, &mut key.borrow_mut(),
scale: scale, PressType::Pressed,
}; time,
outside_margins.chain(c::Transformation { );
origin_x: self.margins.left,
origin_y: self.margins.top,
scale: 1.0,
})
} }
fn find_button_by_position(&self, point: c::Point) -> Option<ButtonPlace> { fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
let (offset, layout) = self.get_current_view_position(); let keys = self.locked_keys.clone();
layout.find_button_by_position(point - offset) for key in &keys {
} self.locked_keys.remove(key);
self.set_state_from_press(key.borrow());
}
pub fn foreach_visible_button<F>(&self, mut f: F) // Don't handle the same key twice, but handle it at least once,
where F: FnMut(c::Point, &Box<Button>) // because its press is the reason we're here
{ if !keys.contains(&::util::Pointer(key.clone())) {
let (view_offset, view) = self.get_current_view_position(); self.set_state_from_press(key);
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>>> { fn set_state_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
let mut out = Vec::new(); // Action should not hold a reference to key,
let view = self.get_current_view(); // because key is later borrowed for mutation. So action is cloned.
for (_, row) in &view.get_rows() { // RefCell::borrow() is covered up by (dyn Borrow)::borrow()
for (_, button) in &row.buttons { // if used like key.borrow() :(
let action = RefCell::borrow(key).action.clone();
let view_name = match action {
Action::SetLevel(name) => {
Some(name.clone())
},
Action::LockLevel { lock, unlock } => {
let locked = { let locked = {
let state = RefCell::borrow(&button.state).clone(); let mut key = key.borrow_mut();
state.action.is_locked(&self.current_view) key.locked ^= true;
key.locked
}; };
if locked { if locked {
out.push(button.state.clone()); self.locked_keys.insert(::util::Pointer(key.clone()));
} }
}
Some(if locked { lock } else { unlock }.clone())
},
_ => None,
};
if let Some(view_name) = view_name {
if let Err(_e) = self.set_view(view_name.clone()) {
eprintln!("No such view: {}, ignoring switch", view_name)
};
};
}
fn calculate_size(&self) -> Size {
Size {
height: find_max_double(
self.views.iter(),
|(_name, view)| view.get_height(),
),
width: find_max_double(
self.views.iter(),
|(_name, view)| view.get_width(),
),
} }
out
} }
} }
@ -822,6 +776,7 @@ mod procedures {
let row = Row { let row = Row {
buttons: vec!((0.1, button)), buttons: vec!((0.1, button)),
angle: 0,
}; };
let view = View { let view = View {
@ -848,207 +803,42 @@ mod procedures {
} }
} }
pub struct UIBackend {
widget_to_layout: c::Transformation,
keyboard: c::EekGtkKeyboard,
}
/// Top level procedures, dispatching to everything /// Top level procedures, dispatching to everything
mod seat { mod seat {
use super::*; use super::*;
fn try_set_view(layout: &mut Layout, view_name: String) { // TODO: turn into release_button
layout.set_view(view_name.clone())
.or_print(
logging::Problem::Bug,
&format!("Bad view {}, ignoring", view_name),
);
}
/// A vessel holding an obligation to switch view.
/// Use with #[must_use]
struct ViewChange<'a> {
layout: &'a mut Layout,
view_name: Option<String>,
}
impl<'a> ViewChange<'a> {
fn choose_view(self, view_name: String) -> ViewChange<'a> {
ViewChange {
view_name: Some(view_name),
..self
}
}
fn apply(self) {
if let Some(name) = self.view_name {
try_set_view(self.layout, name);
}
}
}
/// Find all impermanent view changes and undo them in an arbitrary order.
/// Return an obligation to actually switch the view.
/// The final view is the "unlock" view
/// from one of the currently stuck keys.
// As long as only one stuck button is used, this should be fine.
// This is guaranteed because pressing a lock button unlocks all others.
// TODO: Make some broader guarantee about the resulting view,
// e.g. by maintaining a stack of stuck keys.
#[must_use]
fn unstick_locks(layout: &mut Layout) -> ViewChange {
let mut new_view = None;
for key in layout.get_locked_keys().clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow();
let key = RefCell::borrow(key);
match &key.action {
Action::LockView { lock: _, unlock: view } => {
new_view = Some(view.clone());
},
a => log_print!(
logging::Level::Bug,
"Non-locking action {:?} was found inside locked keys",
a,
),
};
}
ViewChange {
layout,
view_name: new_view,
}
}
pub fn handle_press_key(
layout: &mut Layout,
submission: &mut Submission,
time: Timestamp,
rckey: &Rc<RefCell<KeyState>>,
) {
if !layout.pressed_keys.insert(::util::Pointer(rckey.clone())) {
log_print!(
logging::Level::Bug,
"Key {:?} was already pressed", rckey,
);
}
let key: KeyState = {
RefCell::borrow(rckey).clone()
};
let action = key.action.clone();
match action {
Action::Submit {
text: Some(text),
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(
layout: &mut Layout, layout: &mut Layout,
submission: &mut Submission, virtual_keyboard: &VirtualKeyboard,
ui: Option<&UIBackend>, widget_to_layout: &c::Transformation,
time: Timestamp, time: Timestamp,
manager: Option<manager::c::Manager>, ui_keyboard: c::EekGtkKeyboard,
rckey: &Rc<RefCell<KeyState>>, key: &Rc<RefCell<KeyState>>,
) { ) {
let key: KeyState = { layout.release_key(virtual_keyboard, &mut key.clone(), time);
RefCell::borrow(rckey).clone()
};
let action = key.action.clone();
// update let view = layout.get_current_view();
let key = key.into_released(); let action = RefCell::borrow(key).action.clone();
if let Action::ShowPreferences = action {
// process changes let places = ::layout::procedures::find_key_places(
match action { view, key
Action::Submit { text: _, keys: _ } );
| Action::Erase // getting first item will cause mispositioning
=> { // with more than one button with the same key
unstick_locks(layout).apply(); // on the keyboard
submission.handle_release(KeyState::get_id(rckey), time); if let Some((offset, button)) = places.get(0) {
}, let bounds = c::Bounds {
Action::SetView(view) => { x: offset.x, y: offset.y,
try_set_view(layout, view) width: button.size.width,
}, height: button.size.height,
Action::LockView { lock, unlock } => { };
let gets_locked = !key.action.is_locked(&layout.current_view); ::popover::show(
unstick_locks(layout) ui_keyboard,
// It doesn't matter what the resulting view should be, widget_to_layout.reverse_bounds(bounds)
// it's getting changed anyway. );
.choose_view(
match gets_locked {
true => lock.clone(),
false => unlock.clone(),
}
)
.apply()
},
Action::ApplyModifier(modifier) => {
// FIXME: key id is unneeded with stateless locks
let key_id = KeyState::get_id(rckey);
let gets_locked = !submission.is_modifier_active(modifier.clone());
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 }
Action::ShowPreferences => if let Some(ui) = &ui {
// only show when layout manager is available
if let Some(manager) = manager {
let view = layout.get_current_view();
let places = ::layout::procedures::find_key_places(
view, &rckey,
);
// Getting first item will cause mispositioning
// with more than one button with the same key
// on the keyboard.
if let Some((position, button)) = places.get(0) {
let bounds = c::Bounds {
x: position.x,
y: position.y,
width: button.size.width,
height: button.size.height,
};
::popover::show(
ui.keyboard,
ui.widget_to_layout.reverse_bounds(bounds),
manager,
);
}
}
},
};
let pointer = ::util::Pointer(rckey.clone());
// Apply state changes
layout.pressed_keys.remove(&pointer);
// Commit activated button state changes
RefCell::replace(rckey, key);
} }
} }
@ -1057,13 +847,13 @@ 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::SetLevel("default".into()),
})) }))
} }
@ -1088,6 +878,7 @@ mod test {
( (
0.0, 0.0,
Row { Row {
angle: 0,
buttons: vec![( buttons: vec![(
0.0, 0.0,
Box::new(Button { Box::new(Button {
@ -1100,6 +891,7 @@ mod test {
( (
10.0, 10.0,
Row { Row {
angle: 0,
buttons: vec![( buttons: vec![(
0.0, 0.0,
Box::new(Button { Box::new(Button {
@ -1115,56 +907,4 @@ mod test {
.is_none() .is_none()
); );
} }
#[test]
fn check_bottom_margin() {
// just one button
let view = View::new(vec![
(
0.0,
Row {
buttons: vec![(
0.0,
Box::new(Button {
size: Size { width: 1.0, height: 1.0 },
..*make_button_with_state("foo".into(), make_state())
}),
)]
},
),
]);
let layout = Layout {
current_view: String::new(),
keymap_str: CString::new("").unwrap(),
kind: ArrangementKind::Base,
pressed_keys: HashSet::new(),
// Lots of bottom margin
margins: Margins {
top: 0.0,
left: 0.0,
right: 0.0,
bottom: 1.0,
},
views: hashmap! {
String::new() => (c::Point { x: 0.0, y: 0.0 }, view),
},
};
assert_eq!(
layout.calculate_inner_size(),
Size { width: 1.0, height: 1.0 }
);
assert_eq!(
layout.calculate_size(),
Size { width: 1.0, height: 2.0 }
);
// Don't change those values randomly!
// They take advantage of incidental precise float representation
// to even be comparable.
let transformation = layout.calculate_transformation(
Size { width: 2.0, height: 2.0 }
);
assert_eq!(transformation.scale, 1.0);
assert_eq!(transformation.origin_x, 0.5);
assert_eq!(transformation.origin_y, 0.0);
}
} }

View File

@ -15,9 +15,6 @@ extern crate regex;
extern crate serde; extern crate serde;
extern crate xkbcommon; extern crate xkbcommon;
#[macro_use]
mod logging;
mod action; mod action;
pub mod data; pub mod data;
mod drawing; mod drawing;
@ -27,14 +24,11 @@ mod keyboard;
mod layout; mod layout;
mod locale; mod locale;
mod locale_config; mod locale_config;
mod manager;
mod outputs; mod outputs;
mod popover; mod popover;
mod resources; mod resources;
mod style;
mod submission; mod submission;
mod style;
pub mod tests; pub mod tests;
pub mod util; pub mod util;
mod ui_manager;
mod vkeyboard;
mod xdg; mod xdg;

View File

@ -1,106 +1,21 @@
/*! Locale-specific functions. /*! Locale-specific functions */
*
* This file is intended as a library:
* it must pass errors upwards
* and panicking is allowed only when
* this code encounters an internal inconsistency.
*/
use std::cmp; use std::cmp;
use std::ffi::{ CStr, CString }; use std::ffi::CString;
use std::fmt;
use std::os::raw::c_char;
use std::ptr;
use std::str::Utf8Error;
mod c { mod c {
use super::*; use std::os::raw::c_char;
use std::os::raw::c_void;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type c_int = i32; pub type c_int = i32;
#[derive(Clone, Copy)]
#[repr(C)]
pub struct GnomeXkbInfo(*const c_void);
#[no_mangle] #[no_mangle]
extern "C" { extern "C" {
// from libc // from libc
pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int; pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;
// from gnome-desktop3
pub fn gnome_xkb_info_new() -> GnomeXkbInfo;
pub fn gnome_xkb_info_get_layout_info (
info: GnomeXkbInfo,
id: *const c_char,
display_name: *mut *const c_char,
short_name: *const *const c_char,
xkb_layout: *const *const c_char,
xkb_variant: *const *const c_char
) -> c_int;
pub fn g_object_unref(o: GnomeXkbInfo);
} }
} }
#[derive(Debug)]
pub enum Error {
StringConversion(Utf8Error),
NoInfo,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self, f)
}
}
pub struct XkbInfo(c::GnomeXkbInfo);
impl XkbInfo {
pub fn new() -> XkbInfo {
XkbInfo(unsafe { c::gnome_xkb_info_new() })
}
pub fn get_display_name(&self, id: &str) -> Result<String, Error> {
let id = cstring_safe(id);
let id_ref = id.as_ptr();
let mut display_name: *const c_char = ptr::null();
let found = unsafe {
c::gnome_xkb_info_get_layout_info(
self.0,
id_ref,
&mut display_name as *mut *const c_char,
ptr::null(), ptr::null(), ptr::null(),
)
};
if found != 0 && !display_name.is_null() {
let display_name = unsafe { CStr::from_ptr(display_name) };
display_name.to_str()
.map(str::to_string)
.map_err(Error::StringConversion)
} else {
Err(Error::NoInfo)
}
}
}
impl Drop for XkbInfo {
fn drop(&mut self) {
unsafe { c::g_object_unref(self.0) }
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Translation<'a>(pub &'a str);
impl<'a> Translation<'a> {
pub fn to_owned(&'a self) -> OwnedTranslation {
OwnedTranslation(self.0.to_owned())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedTranslation(pub String);
fn cstring_safe(s: &str) -> CString { fn cstring_safe(s: &str) -> CString {
CString::new(s) CString::new(s)
.unwrap_or(CString::new("").unwrap()) .unwrap_or(CString::new("").unwrap())

View File

@ -1,201 +0,0 @@
/*! Logging library.
*
* This is probably the only part of squeekboard
* that should be doing any direct printing.
*
* There are several approaches to logging,
* in the order of increasing flexibility and/or purity:
*
* 1. `println!` directly
*
* It can't be easily replaced by a different solution
*
* 2. simple `log!` macro
*
* Replacing the destination at runtime other than globally would be awkward,
* so no easy way to suppress errors for things that don't matter,
* but formatting is still easy.
*
* 3. logging to a mutable destination type
*
* Can be easily replaced, but logging `Result` types,
* which should be done by calling a method on the result,
* can't be formatted directly.
* Cannot be parallelized.
*
* 4. logging to an immutable destination type
*
* Same as above, except it can be parallelized.
* Logs being outputs, they get returned
* instead of being misleadingly passed back through arguments.
* It seems more difficult to pass the logger around,
* but this may be a solved problem from the area of functional programming.
*
* This library generally aims at the approach in 3.
* */
use std::fmt::Display;
/// Levels are not in order.
pub enum Level {
// Levels for reporting violated constraints
/// The program violated a self-imposed constraint,
/// ended up in an inconsistent state, and cannot recover.
/// Handlers must not actually panic. (should they?)
Panic,
/// The program violated a self-imposed constraint,
/// ended up in an inconsistent state, but some state can be recovered.
Bug,
/// Invalid data given by an external source,
/// some state of the program must be dropped.
Error,
// Still violated constraints, but harmless
/// Invalid data given by an external source, parts of data are ignored.
/// No previous program state needs to be dropped.
Warning,
/// External source not in an expected state,
/// but not violating any protocols (including no relevant protocol).
Surprise,
// Informational
/// A change in internal state that results in a change of behaviour
/// that a user can observe, and a tinkerer might find useful.
/// E.g. selection of external sources, like loading user's UI files,
/// language switch, overrides.
Info,
/// Information useful for application developer only.
/// Should be limited to information gotten from external sources,
/// and more tricky parts of internal state.
Debug,
}
impl Level {
fn as_str(&self) -> &'static str {
match self {
Level::Panic => "Panic",
Level::Bug => "Bug",
Level::Error => "Error",
Level::Warning => "Warning",
Level::Surprise => "Surprise",
Level::Info => "Info",
Level::Debug => "Debug",
}
}
}
impl From<Problem> for Level {
fn from(problem: Problem) -> Level {
use self::Level::*;
match problem {
Problem::Panic => Panic,
Problem::Bug => Bug,
Problem::Error => Error,
Problem::Warning => Warning,
Problem::Surprise => Surprise,
}
}
}
/// Only levels which indicate problems
/// To use with `Result::Err` handlers,
/// which are needed only when something went off the optimal path.
/// A separate type ensures that `Err`
/// can't end up misclassified as a benign event like `Info`.
pub enum Problem {
Panic,
Bug,
Error,
Warning,
Surprise,
}
/// Sugar for approach 2
// TODO: avoid, deprecate.
// Handler instances should be long lived, not one per call.
macro_rules! log_print {
($level:expr, $($arg:tt)*) => (::logging::print($level, &format!($($arg)*)))
}
/// Approach 2
pub fn print(level: Level, message: &str) {
Print{}.handle(level, message)
}
/// Sugar for logging errors in results.
pub trait Warn where Self: Sized {
type Value;
/// Approach 2.
fn or_print(self, level: Problem, message: &str) -> Option<Self::Value> {
self.or_warn(&mut Print {}, level.into(), message)
}
/// Approach 3.
fn or_warn<H: Handler>(
self,
handler: &mut H,
level: Problem,
message: &str,
) -> Option<Self::Value>;
}
impl<T, E: Display> Warn for Result<T, E> {
type Value = T;
fn or_warn<H: Handler>(
self,
handler: &mut H,
level: Problem,
message: &str,
) -> Option<T> {
self.map_err(|e| {
handler.handle(level.into(), &format!("{}: {}", message, e));
e
}).ok()
}
}
impl<T> Warn for Option<T> {
type Value = T;
fn or_warn<H: Handler>(
self,
handler: &mut H,
level: Problem,
message: &str,
) -> Option<T> {
self.or_else(|| {
handler.handle(level.into(), message);
None
})
}
}
/// A mutable handler for text warnings.
/// Approach 3.
pub trait Handler {
/// Handle a log message
fn handle(&mut self, level: Level, message: &str);
}
/// Prints info to stdout, everything else to stderr
pub struct Print;
impl Handler for Print {
fn handle(&mut self, level: Level, message: &str) {
match level {
Level::Info => println!("Info: {}", message),
l => eprintln!("{}: {}", l.as_str(), message),
}
}
}
/// Warning handler that will panic
/// at any warning, error, surprise, bug, or panic.
/// Don't use except in tests
pub struct ProblemPanic;
impl Handler for ProblemPanic {
fn handle(&mut self, level: Level, message: &str) {
use self::Level::*;
match level {
Panic | Bug | Error | Warning | Surprise => panic!("{}", message),
l => Print{}.handle(l, message),
}
}
}

View File

@ -1,34 +0,0 @@
/*! Procedures relating to the management of the switching of layouts */
use ::util;
pub mod c {
use std::os::raw::{c_char, c_void};
/// EekboardContextService*
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Manager(*const c_void);
#[no_mangle]
extern "C" {
pub fn eekboard_context_service_set_overlay(
manager: Manager,
name: *const c_char,
);
pub fn eekboard_context_service_get_overlay(
manager: Manager,
) -> *const c_char;
}
}
/// Returns the overlay name.
/// The result lifetime is "as long as the C copy lives"
pub fn get_overlay(manager: c::Manager) -> Option<String> {
let raw_str = unsafe {
c::eekboard_context_service_get_overlay(manager)
};
// this string is generated from Rust, should never be invalid
util::c::as_str(&raw_str).unwrap()
.map(String::from)
}

View File

@ -12,7 +12,6 @@ config_h = configure_file(
sources = [ sources = [
config_h, config_h,
'dbus.c',
'imservice.c', 'imservice.c',
'server-context-service.c', 'server-context-service.c',
'wayland.c', 'wayland.c',
@ -20,11 +19,16 @@ sources = [
'../eek/eek-element.c', '../eek/eek-element.c',
'../eek/eek-gtk-keyboard.c', '../eek/eek-gtk-keyboard.c',
'../eek/eek-keyboard.c', '../eek/eek-keyboard.c',
'../eek/eek-layout.c',
'../eek/eek-renderer.c', '../eek/eek-renderer.c',
'../eek/eek-types.c', '../eek/eek-types.c',
'../eek/eek-xml-layout.c',
'../eek/layersurface.c', '../eek/layersurface.c',
dbus_src, dbus_src,
enums,
'../eekboard/key-emitter.c',
'../eekboard/eekboard-context-service.c', '../eekboard/eekboard-context-service.c',
'../eekboard/eekboard-service.c',
# '../eekboard/eekboard-xklutil.c', # '../eekboard/eekboard-xklutil.c',
squeekboard_resources, squeekboard_resources,
wl_proto_sources, wl_proto_sources,
@ -36,10 +40,8 @@ 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('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'),
@ -56,8 +58,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(
@ -71,17 +72,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',
@ -111,3 +112,16 @@ squeekboard = executable('squeekboard',
'-DEEK_COMPILATION=1'], '-DEEK_COMPILATION=1'],
) )
bindir = join_paths(prefix, get_option('bindir'))
test_layout = custom_target('squeekboard-test-layout',
build_by_default: true,
# meson doesn't track all inputs, cargo does
build_always_stale: true,
output: ['squeekboard-test-layout'],
console: true,
command: [cargo_build] + cargo_build_flags
+ ['--rename', 'test_layout', '@OUTPUT@', '--bin', 'test_layout'],
install: true,
install_dir: bindir,
)

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

@ -1,10 +1,7 @@
/*! Managing Wayland outputs */ /*! Managing Wayland outputs */
use std::vec::Vec; use std::vec::Vec;
use ::logging;
// traits
use ::logging::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
@ -17,7 +14,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 +102,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(
@ -134,11 +113,14 @@ pub mod c {
_make: *const c_char, _model: *const c_char, _make: *const c_char, _model: *const c_char,
transform: i32, transform: i32,
) { ) {
let transform = Transform::from_u32(transform as u32) let transform = Transform::from_u32(transform as u32).unwrap_or_else(
.or_print( || {
logging::Problem::Warning, eprintln!(
"Received invalid wl_output.transform value", "Warning: received invalid wl_output.transform value"
).unwrap_or(Transform::Normal); );
Transform::Normal
}
);
let outputs = outputs.clone_ref(); let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut(); let mut collection = outputs.borrow_mut();
@ -147,10 +129,7 @@ pub mod c {
.map(|o| &mut o.pending); .map(|o| &mut o.pending);
match output_state { match output_state {
Some(state) => { state.transform = Some(transform) }, Some(state) => { state.transform = Some(transform) },
None => log_print!( None => eprintln!("Wayland error: Got mode on unknown output"),
logging::Level::Warning,
"Got geometry on unknown output",
),
}; };
} }
@ -162,12 +141,10 @@ pub mod c {
height: i32, height: i32,
_refresh: i32, _refresh: i32,
) { ) {
let flags = Mode::from_bits(flags) let flags = Mode::from_bits(flags).unwrap_or_else(|| {
.or_print( eprintln!("Warning: received invalid wl_output.mode flags");
logging::Problem::Warning, Mode::NONE
"Received invalid wl_output.mode flags", });
).unwrap_or(Mode::NONE);
let outputs = outputs.clone_ref(); let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut(); let mut collection = outputs.borrow_mut();
let output_state: Option<&mut OutputState> let output_state: Option<&mut OutputState>
@ -179,10 +156,7 @@ pub mod c {
state.current_mode = Some(super::Mode { width, height}); state.current_mode = Some(super::Mode { width, height});
} }
}, },
None => log_print!( None => eprintln!("Wayland error: Got mode on unknown output"),
logging::Level::Warning,
"Got mode on unknown output",
),
}; };
} }
@ -195,10 +169,7 @@ pub mod c {
let output = find_output_mut(&mut collection, wl_output); let output = find_output_mut(&mut collection, wl_output);
match output { match output {
Some(output) => { output.current = output.pending.clone(); } Some(output) => { output.current = output.pending.clone(); }
None => log_print!( None => eprintln!("Wayland error: Got done on unknown output"),
logging::Level::Warning,
"Got done on unknown output",
),
}; };
} }
@ -214,10 +185,7 @@ pub mod c {
.map(|o| &mut o.pending); .map(|o| &mut o.pending);
match output_state { match output_state {
Some(state) => { state.scale = factor; } Some(state) => { state.scale = factor; }
None => log_print!( None => eprintln!("Wayland error: Got done on unknown output"),
logging::Level::Warning,
"Got scale on unknown output",
),
}; };
} }
@ -258,15 +226,43 @@ 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,
}
},
_ => {
eprintln!("Not enough info registered on output");
0
},
}
}
// TODO: handle unregistration // TODO: handle unregistration
fn find_output( fn find_output(
@ -292,14 +288,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 +298,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 +309,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

@ -2,28 +2,19 @@
use gio; use gio;
use gtk; use gtk;
use std::ffi::CString; use ::layout::c::EekGtkKeyboard;
use ::layout::c::{ Bounds, EekGtkKeyboard }; use ::locale::compare_current_locale;
use ::locale;
use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
use ::locale_config::system_locale; use ::locale_config::system_locale;
use ::logging;
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;
mod variants { mod variants {
use glib; use glib;
@ -33,7 +24,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
@ -96,13 +86,13 @@ 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)
} }
} }
} }
} }
fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder { fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
let mut xml: Vec<u8> = Vec::new(); let mut xml: Vec<u8> = Vec::new();
writeln!( writeln!(
xml, xml,
@ -111,7 +101,7 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
<menu id=\"app-menu\"> <menu id=\"app-menu\">
<section>" <section>"
).unwrap(); ).unwrap();
for (input_name, translation) in inputs { for (input_name, human_name) in inputs {
writeln!( writeln!(
xml, xml,
" "
@ -120,7 +110,7 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
<attribute name=\"action\">layout</attribute> <attribute name=\"action\">layout</attribute>
<attribute name=\"target\">{}</attribute> <attribute name=\"target\">{}</attribute>
</item>", </item>",
translation.0, human_name,
input_name, input_name,
).unwrap(); ).unwrap();
} }
@ -136,230 +126,62 @@ 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 pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
#[derive(PartialEq, Clone, Debug)] unsafe { gtk::set_initialized() };
enum LayoutId { let window = unsafe { gtk::Widget::from_glib_none(window.0) };
/// Affects the layout in system settings
System {
kind: String,
name: String,
},
/// Only affects what this input method presents
Local(String),
}
impl LayoutId { let settings = gio::Settings::new("org.gnome.desktop.input-sources");
fn get_name(&self) -> &str { let inputs = settings.get_value("sources").unwrap();
match &self { let inputs = variants::get_tuples(inputs);
LayoutId::System { kind: _, name } => name.as_str(),
LayoutId::Local(name) => name.as_str(), let input_names: Vec<&str> = inputs.iter()
} .map(|(_kind, name)| name.as_str())
} .collect();
}
fn set_visible_layout( let translations = system_locale()
manager: manager::c::Manager,
layout_id: LayoutId,
) {
match layout_id {
LayoutId::System { kind, name } => set_layout(kind, name),
LayoutId::Local(name) => {
let name = CString::new(name.as_str()).unwrap();
let name_ptr = name.as_ptr();
unsafe {
manager::c::eekboard_context_service_set_overlay(
manager,
name_ptr,
)
}
},
}
}
/// Takes into account first any overlays, then system layouts from the list
fn get_current_layout(
manager: manager::c::Manager,
system_layouts: &Vec<LayoutId>,
) -> Option<LayoutId> {
match manager::get_overlay(manager) {
Some(name) => Some(LayoutId::Local(name)),
None => system_layouts.get(0).map(LayoutId::clone),
}
}
/// Translates all provided layout names according to current locale,
/// for the purpose of display (i.e. errors will be caught and reported)
fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
// This procedure is rather ugly...
// Xkb lookup *must not* be applied to non-system layouts,
// so both translators can't be merged into one lookup table,
// therefore must be done in two steps.
// `XkbInfo` being temporary also means
// that its return values must be copied,
// forcing the use of `OwnedTranslation`.
enum Status {
/// xkb names should get all translated here
Translated(OwnedTranslation),
/// Builtin names need builtin translations
Remaining(String),
}
// Attempt to take all xkb names from gnome-desktop's xkb info.
let xkb_translator = locale::XkbInfo::new();
let translated_names = layouts.iter()
.map(|id| match id {
LayoutId::System { name, kind: _ } => {
xkb_translator.get_display_name(name)
.map(|s| Status::Translated(OwnedTranslation(s)))
.or_print(
logging::Problem::Surprise,
&format!("No display name for xkb layout {}", name),
).unwrap_or_else(|| Status::Remaining(name.clone()))
},
LayoutId::Local(name) => Status::Remaining(name.clone()),
});
// Non-xkb layouts and weird xkb layouts
// still need to be looked up in the internal database.
let builtin_translations = system_locale()
.map(|locale| .map(|locale|
locale.tags_for("messages") locale.tags_for("messages")
.next().unwrap() // guaranteed to exist .next().unwrap() // guaranteed to exist
.as_ref() .as_ref()
.to_owned() .to_owned()
) )
.or_print(logging::Problem::Surprise, "No locale detected") .and_then(|lang| resources::get_layout_names(lang.as_str()));
.and_then(|lang| {
resources::get_layout_names(lang.as_str())
.or_print(
logging::Problem::Surprise,
&format!("No translations for locale {}", lang),
)
});
match builtin_translations {
Some(translations) => {
translated_names
.map(|status| match status {
Status::Remaining(name) => {
translations.get(name.as_str())
.unwrap_or(&Translation(name.as_str()))
.to_owned()
},
Status::Translated(t) => t,
})
.collect()
},
None => {
translated_names
.map(|status| match status {
Status::Remaining(name) => OwnedTranslation(name),
Status::Translated(t) => t,
})
.collect()
},
}
}
pub fn show(
window: EekGtkKeyboard,
position: Bounds,
manager: manager::c::Manager,
) {
unsafe { gtk::set_initialized() };
let window = unsafe { gtk::Widget::from_glib_none(window.0) };
let overlay_layouts = resources::get_overlays().into_iter()
.map(|name| LayoutId::Local(name.to_string()));
let settings = get_settings("org.gnome.desktop.input-sources");
let inputs = settings
.map(|settings| {
let inputs = settings.get_value("sources").unwrap();
variants::get_tuples(inputs)
})
.unwrap_or_else(|| Vec::new());
let system_layouts: Vec<LayoutId> = inputs.into_iter()
.map(|(kind, name)| LayoutId::System { kind, name })
.collect();
let all_layouts: Vec<LayoutId> = system_layouts.clone()
.into_iter()
.chain(overlay_layouts)
.collect();
let translated_names = translate_layout_names(&all_layouts);
// sorted collection of human and machine names // sorted collection of human and machine names
let mut human_names: Vec<(OwnedTranslation, LayoutId)> = translated_names let mut human_names: Vec<(&str, &str)> = match translations {
.into_iter() Some(translations) => {
.zip(all_layouts.clone().into_iter()) input_names.iter()
.collect(); .map(|name| (*name, *translations.get(name).unwrap_or(name)))
.collect()
},
// display bare codes
None => {
input_names.iter()
.map(|n| (*n, *n)) // turns &&str into &str
.collect()
}
};
human_names.sort_unstable_by(|(tr_a, _), (tr_b, _)| { human_names.sort_unstable_by(|(_, human_label_a), (_, human_label_b)| {
compare_current_locale(&tr_a.0, &tr_b.0) compare_current_locale(human_label_a, human_label_b)
}); });
// GVariant doesn't natively support `enum`s, let builder = make_menu_builder(human_names);
// so the `choices` vector will serve as a lookup table.
let choices_with_translations: Vec<(String, (OwnedTranslation, LayoutId))>
= human_names.into_iter()
.enumerate()
.map(|(i, human_entry)| {(
format!("{}_{}", i, human_entry.1.get_name()),
human_entry,
)}).collect();
let builder = make_menu_builder(
choices_with_translations.iter()
.map(|(id, (translation, _))| (id.as_str(), (*translation).clone()))
.collect()
);
let choices: Vec<(String, LayoutId)>
= choices_with_translations.into_iter()
.map(|(id, (_tr, layout))| (id, layout))
.collect();
// Much more debuggable to populate the model & menu // Much more debuggable to populate the model & menu
// from a string representation // from a string representation
// than add items imperatively // than add items imperatively
@ -373,45 +195,27 @@ pub fn show(
height: position.width.floor() as i32, height: position.width.floor() as i32,
}); });
if let Some(current_layout) = get_current_layout(manager, &system_layouts) { if let Some(current_name) = input_names.get(0) {
let current_name_variant = choices.iter() let current_name = current_name.to_variant();
.find(
|(_id, layout)| layout == &current_layout
).unwrap()
.0.to_variant();
let layout_action = gio::SimpleAction::new_stateful( let layout_action = gio::SimpleAction::new_stateful(
"layout", "layout",
Some(current_name_variant.type_()), Some(current_name.type_()),
&current_name_variant, &current_name,
); );
let menu_inner = menu.clone(); layout_action.connect_change_state(|_action, state| {
layout_action.connect_change_state(move |_action, state| {
match state { match state {
Some(v) => { Some(v) => {
v.get::<String>() v.get::<String>()
.or_print( .or_else(|| {
logging::Problem::Bug, eprintln!("Variant is not string: {:?}", v);
&format!("Variant is not string: {:?}", v) None
) })
.map(|state| { .map(|state| set_layout("xkb".into(), state));
let (_id, layout) = choices.iter()
.find(
|choices| state == choices.0
).unwrap();
set_visible_layout(
manager,
layout.clone(),
)
});
}, },
None => log_print!( None => eprintln!("No variant selected"),
logging::Level::Debug,
"No variant selected",
),
}; };
menu_inner.popdown();
}); });
let action_group = gio::SimpleActionGroup::new(); let action_group = gio::SimpleActionGroup::new();

View File

@ -3,7 +3,6 @@
*/ */
use std::collections::HashMap; use std::collections::HashMap;
use ::locale::Translation;
use std::iter::FromIterator; use std::iter::FromIterator;
@ -11,32 +10,19 @@ 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,
// 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")),
("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")),
("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
("terminal", include_str!("../data/keyboards/terminal.yaml")),
// Overlays
("emoji", include_str!("../data/keyboards/emoji.yaml")),
]; ];
pub fn get_keyboard(needle: &str) -> Option<&'static str> { pub fn get_keyboard(needle: &str) -> Option<&'static str> {
@ -53,19 +39,6 @@ pub fn get_keyboard(needle: &str) -> Option<&'static str> {
}) })
} }
const OVERLAY_NAMES: &[*const str] = &[
"emoji",
"terminal",
];
pub fn get_overlays() -> Vec<&'static str> {
OVERLAY_NAMES.iter()
.map(|name| {
let name: *const str = *name;
unsafe { &*name }
}).collect()
}
/// Translations of the layout identifier strings /// Translations of the layout identifier strings
const LAYOUT_NAMES: &[(*const str, *const str)] = &[ 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")),
@ -73,11 +46,10 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
("es-ES", include_str!("../data/langs/es-ES.txt")), ("es-ES", include_str!("../data/langs/es-ES.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)
-> Option<HashMap<&'static str, Translation<'static>>> -> Option<HashMap<&'static str, &'static str>>
{ {
let translations = LAYOUT_NAMES.iter() let translations = LAYOUT_NAMES.iter()
.find(|(name, _data)| { .find(|(name, _data)| {
@ -91,7 +63,7 @@ pub fn get_layout_names(lang: &str)
translations.map(make_mapping) translations.map(make_mapping)
} }
fn parse_line(line: &str) -> Option<(&str, Translation)> { fn parse_line(line: &str) -> Option<(&str, &str)> {
let comment = line.trim().starts_with("#"); let comment = line.trim().starts_with("#");
if comment { if comment {
None None
@ -99,11 +71,11 @@ fn parse_line(line: &str) -> Option<(&str, Translation)> {
let mut iter = line.splitn(2, " "); let mut iter = line.splitn(2, " ");
let name = iter.next().unwrap(); let name = iter.next().unwrap();
// will skip empty and unfinished lines // will skip empty and unfinished lines
iter.next().map(|tr| (name, Translation(tr.trim()))) iter.next().map(|tr| (name, tr.trim()))
} }
} }
fn make_mapping(data: &str) -> HashMap<&str, Translation> { fn make_mapping(data: &str) -> HashMap<&str, &str> {
HashMap::from_iter( HashMap::from_iter(
data.split("\n") data.split("\n")
.filter_map(parse_line) .filter_map(parse_line)
@ -114,17 +86,10 @@ fn make_mapping(data: &str) -> HashMap<&str, Translation> {
mod test { mod test {
use super::*; use super::*;
#[test]
fn check_overlays_present() {
for name in get_overlays() {
assert!(get_keyboard(name).is_some());
}
}
#[test] #[test]
fn mapping_line() { fn mapping_line() {
assert_eq!( assert_eq!(
Some(("name", Translation("translation"))), Some(("name", "translation")),
parse_line("name translation") parse_line("name translation")
); );
} }

View File

@ -23,63 +23,97 @@
#include "eek/eek.h" #include "eek/eek.h"
#include "eek/eek-gtk-keyboard.h" #include "eek/eek-gtk-keyboard.h"
#include "eek/layersurface.h" #include "eek/layersurface.h"
#include "eekboard/eekboard-context-service.h"
#include "submission.h"
#include "wayland.h" #include "wayland.h"
#include "server-context-service.h" #include "server-context-service.h"
enum { enum {
PROP_0, PROP_0,
PROP_VISIBLE, PROP_SIZE_CONSTRAINT_LANDSCAPE,
PROP_SIZE_CONSTRAINT_PORTRAIT,
PROP_LAST PROP_LAST
}; };
typedef struct _ServerContextServiceClass ServerContextServiceClass;
struct _ServerContextService { struct _ServerContextService {
GObject parent; EekboardContextService parent;
EekboardContextService *state; // unowned
/// Needed for instantiating the widget
struct submission *submission; // unowned
struct squeek_layout_state *layout;
struct ui_manager *manager; // unowned
gboolean visible;
PhoshLayerSurface *window; PhoshLayerSurface *window;
GtkWidget *widget; // nullable GtkWidget *widget;
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];
}; };
G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT); struct _ServerContextServiceClass {
EekboardContextServiceClass parent_class;
};
G_DEFINE_TYPE (ServerContextService, server_context_service, EEKBOARD_TYPE_CONTEXT_SERVICE);
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)
{
const LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (EEKBOARD_CONTEXT_SERVICE(context));
if (!keyboard)
g_error("Programmer error: keyboard layout was unset!");
// The keymap will get set even if the window is hidden.
// It's not perfect,
// but simpler than adding a check in the window showing procedure
eekboard_context_service_set_keymap(EEKBOARD_CONTEXT_SERVICE(context),
keyboard);
/* 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) {
eekboard_context_service_hide_keyboard(EEKBOARD_CONTEXT_SERVICE(context));
eekboard_context_service_show_keyboard(EEKBOARD_CONTEXT_SERVICE(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
@ -94,26 +128,31 @@ calculate_height(int32_t width)
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)
{ {
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
enum squeek_arrangement_kind new_type = get_type((uint32_t)width, (uint32_t)height);
if (context->last_type != new_type) {
context->last_type = new_type;
eekboard_context_service_update_layout(EEKBOARD_CONTEXT_SERVICE(context), context->last_type);
}
// When the geometry event comes after surface.configure,
// this entire height calculation does nothing.
// guint desired_height = squeek_uiman_get_perceptual_height(context->manager);
// Temporarily use old method, until the size manager is complete.
guint desired_height = calculate_height(width); guint desired_height = calculate_height(width);
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),
@ -121,8 +160,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);
@ -131,19 +170,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
@ -155,11 +194,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.
@ -167,88 +206,82 @@ 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); LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (EEKBOARD_CONTEXT_SERVICE(context));
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
gtk_widget_show_all(self->widget); context->widget = eek_gtk_keyboard_new (keyboard);
gtk_widget_set_has_tooltip (context->widget, TRUE);
gtk_container_add (GTK_CONTAINER(context->window), context->widget);
gtk_widget_show (context->widget);
}
static void
server_context_service_real_show_keyboard (EekboardContextService *_context)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
if (context->hiding) {
g_source_remove (context->hiding);
context->hiding = 0;
}
if (!context->window)
make_window (context);
if (!context->widget)
make_widget (context);
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
show_keyboard (_context);
gtk_widget_show (GTK_WIDGET(context->window));
} }
static gboolean static gboolean
on_hide (ServerContextService *self) on_hide (ServerContextService *context)
{ {
gtk_widget_hide (GTK_WIDGET(self->window)); gtk_widget_hide (GTK_WIDGET(context->window));
self->hiding = 0; context->hiding = 0;
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
static void static void
server_context_service_real_show_keyboard (ServerContextService *self) server_context_service_real_hide_keyboard (EekboardContextService *_context)
{ {
if (self->hiding) { ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
g_source_remove (self->hiding);
self->hiding = 0;
}
if (!self->window) if (!context->hiding)
make_window (self); context->hiding = g_timeout_add (200, (GSourceFunc) on_hide, context);
if (!self->widget)
make_widget (self);
self->visible = TRUE; EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
gtk_widget_show (GTK_WIDGET(self->window)); hide_keyboard (_context);
} }
static void static void
server_context_service_real_hide_keyboard (ServerContextService *self) server_context_service_real_destroyed (EekboardContextService *_context)
{ {
if (!self->hiding)
self->hiding = g_timeout_add (200, (GSourceFunc) on_hide, self);
self->visible = FALSE;
}
void
server_context_service_show_keyboard (ServerContextService *self)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
if (!self->visible) {
server_context_service_real_show_keyboard (self);
}
}
void
server_context_service_hide_keyboard (ServerContextService *self)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
if (self->visible) {
server_context_service_real_hide_keyboard (self);
}
} }
static void static void
@ -257,11 +290,21 @@ 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_VISIBLE: case PROP_SIZE_CONSTRAINT_LANDSCAPE:
self->visible = g_value_get_boolean (value); 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; break;
default: default:
@ -270,30 +313,13 @@ server_context_service_set_property (GObject *object,
} }
} }
static void
server_context_service_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
switch (prop_id) {
case PROP_VISIBLE:
g_value_set_boolean (value, self->visible);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
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);
} }
@ -301,38 +327,54 @@ server_context_service_dispose (GObject *object)
static void static void
server_context_service_class_init (ServerContextServiceClass *klass) server_context_service_class_init (ServerContextServiceClass *klass)
{ {
EekboardContextServiceClass *context_class = EEKBOARD_CONTEXT_SERVICE_CLASS(klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec; GParamSpec *pspec;
context_class->show_keyboard = server_context_service_real_show_keyboard;
context_class->hide_keyboard = server_context_service_real_hide_keyboard;
context_class->destroyed = server_context_service_real_destroyed;
gobject_class->set_property = server_context_service_set_property; gobject_class->set_property = server_context_service_set_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",
* Flag to indicate if keyboard is visible or not. "Size constraint landscape",
*/ "Size constraint landscape",
pspec = g_param_spec_boolean ("visible", G_VARIANT_TYPE("(dd)"),
"Visible", NULL,
"Visible", G_PARAM_WRITABLE);
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_VISIBLE, 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); pspec);
} }
static void static void
server_context_service_init (ServerContextService *self) { server_context_service_init (ServerContextService *context)
(void)self; {
g_signal_connect (context,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
context);
} }
ServerContextService * EekboardContextService *
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman) server_context_service_new ()
{ {
ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL); return EEKBOARD_CONTEXT_SERVICE(g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL));
ui->submission = submission; }
ui->state = self;
ui->layout = layout; enum squeek_arrangement_kind server_context_service_get_layout_type(EekboardContextService *service)
ui->manager = uiman; {
return ui; return SERVER_CONTEXT_SERVICE(service)->last_type;
} }

View File

@ -18,21 +18,24 @@
#ifndef SERVER_CONTEXT_SERVICE_H #ifndef SERVER_CONTEXT_SERVICE_H
#define SERVER_CONTEXT_SERVICE_H 1 #define SERVER_CONTEXT_SERVICE_H 1
#include "eekboard/eekboard-service.h"
#include "src/layout.h" #include "src/layout.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 liecycle of the window displaying layouts. */
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject) typedef struct _ServerContextService ServerContextService;
EekboardContextService *server_context_service_new ();
enum squeek_arrangement_kind server_context_service_get_layout_type(EekboardContextService*);
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
void server_context_service_show_keyboard (ServerContextService *self);
void server_context_service_hide_keyboard (ServerContextService *self);
G_END_DECLS G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */ #endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -25,14 +25,11 @@
#include "config.h" #include "config.h"
#include "eekboard/eekboard-service.h"
#include "eek/eek.h" #include "eek/eek.h"
#include "eekboard/eekboard-context-service.h" #include "imservice.h"
#include "dbus.h"
#include "layout.h"
#include "outputs.h" #include "outputs.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 +37,9 @@
/// 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. EekboardContextService *context;
EekboardContextService *settings_context; // Gsettings hooks. struct imservice *imservice;
ServerContextService *ui_context; // mess, includes the entire UI
struct submission *submission; // Wayland text input handling.
struct squeek_layout_state layout_choice; // Currently wanted layout.
struct ui_manager *ui_manager; // UI shape tracker/chooser. TODO: merge with layuot choice
}; };
@ -71,14 +64,31 @@ on_name_lost (GDBusConnection *connection,
gpointer user_data) gpointer user_data)
{ {
// TODO: could conceivable continue working // TODO: could conceivable continue working
// if intrnal changes stop sending dbus changes
(void)connection; (void)connection;
(void)name; (void)name;
(void)user_data; (void)user_data;
g_error("DBus unavailable, unclear how to continue.");
exit (1); exit (1);
} }
static void
on_destroyed (EekboardService *service,
gpointer user_data)
{
(void)service;
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
static EekboardContextService *create_context() {
EekboardContextService *context = server_context_service_new ();
g_object_set_data_full (G_OBJECT(context),
"owner", g_strdup ("sender"),
(GDestroyNotify)g_free);
eekboard_context_service_enable (context);
return context;
}
// Wayland // Wayland
static void static void
@ -199,13 +209,7 @@ main (int argc, char **argv)
exit(1); exit(1);
} }
if (!instance.wayland.input_method_manager) { instance.context = create_context();
g_warning("Wayland input method interface not available");
}
instance.ui_manager = squeek_uiman_new();
instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
// set up dbus // set up dbus
@ -228,9 +232,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:
@ -252,70 +256,51 @@ 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) { EekboardService *service = eekboard_service_new (connection, EEKBOARD_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, } else {
on_name_lost, eekboard_service_set_context(service, instance.context);
NULL, }
NULL);
if (owner_id == 0) { guint owner_id = g_bus_own_name_on_connection (connection,
g_printerr ("Can't own the name\n"); EEKBOARD_SERVICE_INTERFACE,
exit (1); G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired,
on_name_lost,
NULL,
NULL);
if (owner_id == 0) {
g_printerr ("Can't own the name\n");
exit (1);
}
struct imservice *imservice = NULL;
if (instance.wayland.input_method_manager) {
imservice = get_imservice(instance.context,
instance.wayland.input_method_manager,
instance.wayland.seat);
if (imservice) {
instance.imservice = imservice;
} else {
g_warning("Failed to register as an input method");
} }
} }
instance.submission = get_submission(instance.wayland.input_method_manager,
instance.wayland.virtual_keyboard_manager,
instance.wayland.seat,
instance.settings_context);
eekboard_context_service_set_submission(instance.settings_context, instance.submission);
ServerContextService *ui_context = server_context_service_new(
instance.settings_context,
instance.submission,
&instance.layout_choice,
instance.ui_manager);
if (!ui_context) {
g_error("Could not initialize GUI");
exit(1);
}
instance.ui_context = ui_context;
if (instance.submission) {
submission_set_ui(instance.submission, instance.ui_context);
}
if (instance.dbus_handler) {
dbus_handler_set_ui_context(instance.dbus_handler, instance.ui_context);
}
eekboard_context_service_set_ui(instance.settings_context, instance.ui_context);
session_register(); session_register();
GMainLoop *loop = g_main_loop_new (NULL, FALSE); GMainLoop *loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (service, "destroyed", G_CALLBACK(on_destroyed), loop);
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

@ -16,13 +16,12 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.Free * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
*/ */
/*! CSS data loading. */ /*! CSS data loading */
use std::env; use std::env;
use ::logging;
use glib::object::ObjectExt; use glib::object::ObjectExt;
use logging::Warn; use util::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
@ -84,11 +83,7 @@ fn get_theme_name(settings: &gtk::Settings) -> GtkTheme {
.map_err(|e| { .map_err(|e| {
match &e { match &e {
env::VarError::NotPresent => {}, env::VarError::NotPresent => {},
// maybe TODO: forward this warning? e => eprintln!("GTK_THEME variable invalid: {}", e),
e => log_print!(
logging::Level::Surprise,
"GTK_THEME variable invalid: {}", e,
),
}; };
e e
}).ok(); }).ok();
@ -98,13 +93,13 @@ fn get_theme_name(settings: &gtk::Settings) -> GtkTheme {
None => GtkTheme { None => GtkTheme {
name: { name: {
settings.get_property("gtk-theme-name") settings.get_property("gtk-theme-name")
.or_print(logging::Problem::Surprise, "No theme name") .ok_warn("No theme name")
.and_then(|value| value.get::<String>()) .and_then(|value| value.get::<String>())
.unwrap_or(DEFAULT_THEME_NAME.into()) .unwrap_or(DEFAULT_THEME_NAME.into())
}, },
variant: { variant: {
settings.get_property("gtk-application-prefer-dark-theme") settings.get_property("gtk-application-prefer-dark-theme")
.or_print(logging::Problem::Surprise, "No settings key") .ok_warn("No settings key")
.and_then(|value| value.get::<bool>()) .and_then(|value| value.get::<bool>())
.and_then(|dark_preferred| match dark_preferred { .and_then(|dark_preferred| match dark_preferred {
true => Some("dark".into()), true => Some("dark".into()),

View File

@ -1,19 +0,0 @@
#ifndef __SUBMISSION_H
#define __SUBMISSION_H
#include "input-method-unstable-v2-client-protocol.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "eek/eek-types.h"
struct submission;
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
struct wl_seat *seat,
EekboardContextService *state);
// Defined in Rust
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_keyboard(struct submission *self, LevelKeyboard *keyboard, uint32_t time);
#endif

View File

@ -1,291 +1,69 @@
/*! Managing the state of text input in the application. /*! Managing the events belonging to virtual-keyboard interface. */
*
* This is a library module.
*
* It needs to combine text-input and virtual-keyboard protocols
* to achieve a consistent view of the text-input state,
* and to submit exactly what the user wanted.
*
* It must also not get tripped up by sudden disappearances of interfaces.
*
* The virtual-keyboard interface is always present.
*
* The text-input interface may not be presented,
* and, for simplicity, no further attempt to claim it is made.
*
* The text-input interface may be enabled and disabled at arbitrary times,
* and those events SHOULD NOT cause any lost events.
* */
use std::collections::HashSet; use ::keyboard::{ KeyState, PressType };
use std::ffi::CString;
use ::action::Modifier;
use ::imservice;
use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
use ::layout::c::LevelKeyboard;
use ::util::vec_remove;
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 std::os::raw::c_void; use std::os::raw::c_void;
use ::imservice::c::InputMethod;
use ::vkeyboard::c::ZwpVirtualKeyboardV1;
// The following defined in C
/// ServerContextService*
#[repr(transparent)] #[repr(transparent)]
pub struct UIManager(*const c_void); #[derive(Clone, Copy)]
pub struct ZwpVirtualKeyboardV1(*const c_void);
/// EekboardContextService*
#[repr(transparent)]
pub struct StateManager(*const c_void);
#[no_mangle] #[no_mangle]
pub extern "C" extern "C" {
fn submission_new( /// Checks if point falls within bounds,
im: *mut InputMethod, /// which are relative to origin and rotated by angle (I think)
vk: ZwpVirtualKeyboardV1, pub fn eek_virtual_keyboard_v1_key(
state_manager: *const StateManager virtual_keyboard: ZwpVirtualKeyboardV1,
) -> *mut Submission { timestamp: u32,
let imservice = if im.is_null() { keycode: u32,
None press: u32,
} else { );
Some(IMService::new(im, state_manager))
};
// TODO: add vkeyboard too
Box::<Submission>::into_raw(Box::new(
Submission {
imservice,
modifiers_active: Vec::new(),
virtual_keyboard: VirtualKeyboard(vk),
pressed: Vec::new(),
}
))
}
/// Use to initialize the UI reference
#[no_mangle]
pub extern "C"
fn submission_set_ui(submission: *mut Submission, ui_manager: *const UIManager) {
if submission.is_null() {
panic!("Null submission pointer");
}
let submission: &mut Submission = unsafe { &mut *submission };
if let Some(ref mut imservice) = &mut submission.imservice {
imservice.ui_manager = if ui_manager.is_null() {
None
} else {
Some(ui_manager)
}
};
}
#[no_mangle]
pub extern "C"
fn submission_set_keyboard(
submission: *mut Submission,
keyboard: LevelKeyboard,
time: u32,
) {
if submission.is_null() {
panic!("Null submission pointer");
}
let submission: &mut Submission = unsafe { &mut *submission };
submission.update_keymap(keyboard, Timestamp(time));
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Timestamp(pub u32); pub struct Timestamp(pub u32);
#[derive(Clone)] /// Layout-independent backend. TODO: Have one instance per program or seat
enum SubmittedAction { pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1);
/// A collection of keycodes that were pressed
VirtualKeyboard(Vec<KeyCode>),
IMService,
}
pub struct Submission { impl VirtualKeyboard {
imservice: Option<Box<IMService>>, // TODO: split out keyboard state management
virtual_keyboard: VirtualKeyboard, pub fn switch(
modifiers_active: Vec<(KeyStateId, Modifier)>, &self,
pressed: Vec<(KeyStateId, SubmittedAction)>, key: &mut KeyState,
} action: PressType,
timestamp: Timestamp,
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(); key.pressed = action.clone();
let was_committed_as_text = match (&mut self.imservice, mods_are_on) { let keycodes_count = key.keycodes.len();
(Some(imservice), false) => { for keycode in key.keycodes.iter() {
enum Outcome { let keycode = keycode - 8;
Submitted(Result<(), imservice::SubmitError>), match (&key.pressed, keycodes_count) {
NotSubmitted, // Pressing a key made out of a single keycode is simple:
}; // press on press, release on release.
(_, 1) => unsafe {
let submit_outcome = match data { c::eek_virtual_keyboard_v1_key(
SubmitData::Text(text) => { self.0, timestamp.0, keycode, action.clone() as u32
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 => {
self.virtual_keyboard.switch(
keycodes,
PressType::Pressed,
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) => {
self.virtual_keyboard.switch(
&keycodes,
PressType::Released,
time,
)
}, },
// 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 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.
pub fn update_keymap(&mut self, keyboard: LevelKeyboard, time: Timestamp) {
self.clear_all_modifiers();
self.release_all_virtual_keys(time);
self.virtual_keyboard.update_keymap(keyboard);
} }
} }

Some files were not shown because too many files have changed in this diff Show More