Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f5c5ef10f | |||
| 6e183ccb13 | |||
| af0137a4fc | |||
| bb3c26b0d8 | |||
| 6dcea4599f | |||
| d32749d533 | |||
| 36306f2eea | |||
| 34a4c6ffb5 | |||
| 74e75d2dae | |||
| a3e421db3d | |||
| 241e5c0fc6 | |||
| 28e0c26671 | |||
| 7d0070a155 | |||
| 9093226abe | |||
| 45dc51f08f | |||
| b486dc8afd | |||
| e70a64a47e | |||
| 225b243446 | |||
| db994da531 | |||
| d47aff357b | |||
| 96c2c2dd1f | |||
| c8cc5b1997 | |||
| edb28cb859 | |||
| b07689939b | |||
| 6072e5768a | |||
| fe8d66a635 | |||
| c8658b00e3 | |||
| 0989771a3b | |||
| 9c2acde826 | |||
| 2352e31f01 | |||
| 8e654346a2 | |||
| 015ba79f65 | |||
| d6aa54f30c | |||
| fd0d8d4244 | |||
| c725cd7f14 | |||
| 0922d4a87a | |||
| 51562d5185 | |||
| ed8b6eec28 | |||
| e9c236a682 | |||
| 04a47ad0af | |||
| 99c577be60 | |||
| db8340181f | |||
| 4f18ffd34e | |||
| 4306ec9c1e | |||
| 31c12e5182 | |||
| 521796a46d | |||
| a187221d3f | |||
| 583b546e81 | |||
| aa9523338f | |||
| 0ed66e0eab | |||
| 6523275b6a | |||
| b9e9ca368a | |||
| c4886e362a | |||
| fc5f671e57 | |||
| 035ecd6df1 | |||
| 26d1a6047c | |||
| 60a89b6c3f | |||
| b84c402c4a | |||
| 2579d2fea9 | |||
| c75ed9b230 | |||
| 789e8b6bff | |||
| 633d15c438 | |||
| baabcb1400 | |||
| c16bbb9e7f | |||
| 623181cc34 | |||
| 76b5104fb7 | |||
| 6c0a642abf | |||
| 132435a9c8 | |||
| 521bcfc484 | |||
| 3413021d30 | |||
| ffc64c6d56 | |||
| 79672f3a2d | |||
| e1d5731466 | |||
| 09deef2d6c | |||
| 83907af456 | |||
| 878b7ed18e | |||
| e6f3b9e5be | |||
| 75992ff13f | |||
| 2d7dddd505 | |||
| afe0ed1674 | |||
| b9ab4288d7 | |||
| d816cc261a | |||
| 2e2ae96114 | |||
| ec7e7c3f8b | |||
| 5551ed2bd2 | |||
| 0da02aab21 | |||
| 1ae8d072a6 | |||
| e61a3a6fe8 | |||
| 087da5cd9e |
@ -9,6 +9,10 @@ stages:
|
|||||||
- librem5
|
- librem5
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
- apt-get -y update
|
||||||
|
- apt-get -y install wget ca-certificates gnupg
|
||||||
|
- 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 -
|
||||||
- apt-get -y update
|
- apt-get -y update
|
||||||
- apt-get -y build-dep .
|
- apt-get -y build-dep .
|
||||||
|
|
||||||
@ -22,6 +26,26 @@ build_meson:
|
|||||||
- meson . _build/ -Ddepdatadir=/usr/share
|
- meson . _build/ -Ddepdatadir=/usr/share
|
||||||
- ninja -C _build install
|
- ninja -C _build install
|
||||||
|
|
||||||
|
build_deb:
|
||||||
|
<<: *tags
|
||||||
|
stage: build
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- "*.deb"
|
||||||
|
script:
|
||||||
|
- apt-get -y install devscripts
|
||||||
|
- debuild -i -us -uc -b
|
||||||
|
- cp ../*.deb .
|
||||||
|
|
||||||
|
test_lintian:
|
||||||
|
<<: *tags
|
||||||
|
stage: test
|
||||||
|
dependencies:
|
||||||
|
- build_deb
|
||||||
|
script:
|
||||||
|
- apt-get -y install lintian
|
||||||
|
- lintian *.deb
|
||||||
|
|
||||||
test:
|
test:
|
||||||
<<: *tags
|
<<: *tags
|
||||||
stage: test
|
stage: test
|
||||||
|
|||||||
162
Cargo.lock
generated
Normal file
162
Cargo.lock
generated
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtoa"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.62"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maplit"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memmap"
|
||||||
|
version = "0.7.0"
|
||||||
|
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]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"maplit 1.0.2 (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.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.4 (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.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"dtoa 0.4.4 (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.101 (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 = "syn"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.4 (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)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xkbcommon"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"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)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
|
||||||
|
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
|
||||||
|
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||||
|
"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 memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
||||||
|
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
|
||||||
|
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||||
|
"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
|
||||||
|
"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
|
||||||
|
"checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582"
|
||||||
|
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||||
|
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||||
|
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||||
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
"checksum xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fda0ea5f7ddabd51deeeda7799bee06274112f577da7dd3d954b8eda731b2fce"
|
||||||
|
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
|
||||||
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "1.0"
|
||||||
|
maplit = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_yaml = "0.8"
|
||||||
|
xkbcommon = { version = "0.4", features = ["wayland"] }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "rs"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
crate-type = ["staticlib", "rlib"]
|
||||||
58
HACKING.md
Normal file
58
HACKING.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
Hacking
|
||||||
|
=======
|
||||||
|
|
||||||
|
This document describes the standards for modifying and maintaining the *squeekboard* project.
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
|
||||||
|
Most common testing is done in CI. Occasionally, and for each release, do perform manual tests to make sure that
|
||||||
|
|
||||||
|
- the application draws correctly
|
||||||
|
- it shows when relevant
|
||||||
|
- it changes layouts
|
||||||
|
- it changes levels
|
||||||
|
|
||||||
|
Testing with an application:
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 tests/entry.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Testing visibility:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
|
||||||
|
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b false
|
||||||
|
```
|
||||||
|
|
||||||
|
Testing layouts:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
|
||||||
|
$ gsettings set org.gnome.desktop.input-sources current 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Maintenance
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Squeekboard uses Rust & Cargo for some of its dependencies.
|
||||||
|
|
||||||
|
Use the `cargo.sh` script for maintaining the Cargo part of the build. The script takes the usual Cargo commands, after the first 2 positionsl arguments: source directory, and output artifact. So, `cargo test` becomes:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd build_dir
|
||||||
|
sh /source_path/cargo.sh /source_path '' test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cargo dependencies
|
||||||
|
|
||||||
|
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 should be updated often, preferably with each bugfix revision, and in a commit on its own:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd build_dir
|
||||||
|
sh /source_path/cargo.sh /source_path '' update
|
||||||
|
ninja test
|
||||||
|
```
|
||||||
29
README.md
29
README.md
@ -3,14 +3,18 @@
|
|||||||
|
|
||||||
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
|
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
|
||||||
|
|
||||||
|
It squeaks because some Rust got inside.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
### Present
|
### Present
|
||||||
|
|
||||||
- GTK3
|
- GTK3
|
||||||
- Custom xml-defined keyboards
|
- Custom yaml-defined keyboards
|
||||||
- DBus interface to show and hide
|
- DBus interface to show and hide
|
||||||
|
- Use Wayland input method protocol to show and hide
|
||||||
|
- Use Wayland virtual keyboard protocol
|
||||||
|
|
||||||
### Temporarily dropped
|
### Temporarily dropped
|
||||||
|
|
||||||
@ -18,8 +22,6 @@ Features
|
|||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
- Use Wayland virtual keyboard protocol
|
|
||||||
- Use Wayland text input protocol
|
|
||||||
- Use Wayland input method protocol
|
- Use Wayland input method protocol
|
||||||
- Pick up DBus interface files from /usr/share
|
- Pick up DBus interface files from /usr/share
|
||||||
|
|
||||||
@ -38,31 +40,20 @@ $ cd squeekboard
|
|||||||
$ mkdir ../build
|
$ mkdir ../build
|
||||||
$ meson ../build/
|
$ meson ../build/
|
||||||
$ cd ../build
|
$ cd ../build
|
||||||
|
$ ninja test
|
||||||
$ ninja install
|
$ ninja install
|
||||||
```
|
```
|
||||||
|
|
||||||
For development, alter the `meson` call:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ meson ../build/ --prefix=../install
|
|
||||||
```
|
|
||||||
|
|
||||||
and don't skip `ninja install` before running. The last step is necessary in order to find the keyboard definition files.
|
|
||||||
|
|
||||||
Running
|
Running
|
||||||
-------
|
-------
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rootston
|
$ phoc # if no compatible Wayland compositor is running yet
|
||||||
$ cd ../build/
|
$ cd ../build/
|
||||||
$ src/squeekboard
|
$ src/squeekboard
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing
|
Developing
|
||||||
|
----------
|
||||||
|
|
||||||
```
|
See `HACKING.md`
|
||||||
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
|
|
||||||
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b false
|
|
||||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
|
|
||||||
$ gsettings set org.gnome.desktop.input-sources current 1
|
|
||||||
```
|
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
Eek cheader_filename="eek/eek.h"
|
|
||||||
@ -1 +0,0 @@
|
|||||||
EekGtk cheader_filename="eek/eek-gtk.h"
|
|
||||||
@ -1 +0,0 @@
|
|||||||
EekXkl cheader_filename="eek/eek-xkl.h"
|
|
||||||
@ -1 +0,0 @@
|
|||||||
gio-2.0
|
|
||||||
@ -1 +0,0 @@
|
|||||||
eek-0.90
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
eek-0.90
|
|
||||||
x11
|
|
||||||
23
cargo.sh
Normal file
23
cargo.sh
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script manages Cargo operations
|
||||||
|
# while keeping the artifact directory within the build tree
|
||||||
|
# instead of the source tree
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SOURCE_DIR="$1"
|
||||||
|
|
||||||
|
export CARGO_TARGET_DIR=`pwd`
|
||||||
|
if [ ! -z ${2} ]; then
|
||||||
|
OUT_PATH=`realpath "${2}"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $SOURCE_DIR
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
cargo $BUILD_ARG $@
|
||||||
|
|
||||||
|
if [ ! -z ${OUT_PATH} ]; then
|
||||||
|
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
|
||||||
|
fi
|
||||||
@ -1,64 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<geometry version="0.90">
|
|
||||||
<bounds x="10" y="10" width="410.0000" height="229"/>
|
|
||||||
|
|
||||||
<outline id="default" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="37.46341" y="0.000000"/>
|
|
||||||
<point x="37.46341" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="altline" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="48.39024" y="0.000000"/>
|
|
||||||
<point x="48.39024" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline7" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="spaceline" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="150.5853" y="0.000000"/>
|
|
||||||
<point x="150.5853" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
|
|
||||||
<button name="Shift_L" oref="altline" />
|
|
||||||
<button name="BackSpace" oref="altline" />
|
|
||||||
<button name="preferences" oref="altline" />
|
|
||||||
<button name="show_numbers" oref="altline" keycode="0" />
|
|
||||||
<button name="show_letters" oref="altline" keycode="0" />
|
|
||||||
<button name="show_symbols" oref="altline" keycode="0" />
|
|
||||||
<button name="period" oref="altline" />
|
|
||||||
<button name="space" oref="spaceline" />
|
|
||||||
<button name="Return" oref="outline7" />
|
|
||||||
|
|
||||||
<view>
|
|
||||||
<section angle="0">q w e r t y u i o p</section>
|
|
||||||
<section angle="0">a s d f g h j k l</section>
|
|
||||||
<section angle="0"> Shift_L z x c v b n m BackSpace </section>
|
|
||||||
<section angle="0"> show_numbers preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
<section angle="0">Q W E R T Y U I O P</section>
|
|
||||||
<section angle="0">A S D F G H J K L</section>
|
|
||||||
<section angle="0"> Shift_L Z X C V B N M BackSpace </section>
|
|
||||||
<section angle="0"> show_numbers preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
<section angle="0">1 2 3 4 5 6 7 8 9 0</section>
|
|
||||||
<section angle="0">at numbersign dollar percent ampersand minus underscore plus parenleft parenright</section>
|
|
||||||
<section angle="0"> show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace </section>
|
|
||||||
<section angle="0"> show_letters preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
<section angle="0">asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph</section>
|
|
||||||
<section angle="0">copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright</section>
|
|
||||||
<section angle="0"> show_numbers backslash slash less greater equal bracketleft bracketright BackSpace </section>
|
|
||||||
<section angle="0"> show_letters preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
</geometry>
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<geometry version="0.90">
|
|
||||||
<bounds x="0" y="10.000000" width="426.0000" height="296.5853"/>
|
|
||||||
|
|
||||||
<outline id="outline2" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="32" y="0.000000"/>
|
|
||||||
<point x="32" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="altline" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="48.39024" y="0.000000"/>
|
|
||||||
<point x="48.39024" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline4" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="59.31707" y="0.000000"/>
|
|
||||||
<point x="59.31707" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline5" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="59.31707" y="0.000000"/>
|
|
||||||
<point x="59.31707" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline6" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="68.68292" y="0.000000"/>
|
|
||||||
<point x="68.68292" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline7" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline8" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline9" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="109.2682" y="0.000000"/>
|
|
||||||
<point x="109.2682" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline10" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="37.46341" y="0.000000"/>
|
|
||||||
<point x="37.46341" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline13" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="79.60975" y="0.000000"/>
|
|
||||||
<point x="79.60975" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="spaceline" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="150.5853" y="0.000000"/>
|
|
||||||
<point x="150.5853" y="52"/>
|
|
||||||
<point x="0.000000" y="52"/>
|
|
||||||
</outline>
|
|
||||||
|
|
||||||
<button name="Shift_L" oref="altline" />
|
|
||||||
<button name="BackSpace" oref="altline" />
|
|
||||||
<button name="preferences" oref="altline" />
|
|
||||||
<button name="show_numbers" oref="altline" keycode="0" />
|
|
||||||
<button name="show_letters" oref="altline" keycode="0" />
|
|
||||||
<button name="show_symbols" oref="altline" keycode="0" />
|
|
||||||
<button name="space" oref="spaceline" />
|
|
||||||
<button name="return" oref="outline7" />
|
|
||||||
|
|
||||||
<view>
|
|
||||||
<section angle="0">q w e r t y u i o p aring</section>
|
|
||||||
<section angle="0">a s d f g h j k l oslash ae</section>
|
|
||||||
<section angle="0"> Shift_L z x c v b n m BackSpace </section>
|
|
||||||
<section angle="0"> show_numbers preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
<section angle="0">Q W E R T Y U I O P Aring</section>
|
|
||||||
<section angle="0">A S D F G H J K L Oslash AE</section>
|
|
||||||
<section angle="0"> Shift_L Z X C V B N M BackSpace </section>
|
|
||||||
<section angle="0"> show_numbers preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
<section angle="0">1 2 3 4 5 6 7 8 9 0</section>
|
|
||||||
<section angle="0">at numbersign dollar percent ampersand minus underscore plus parenleft parenright</section>
|
|
||||||
<section angle="0"> show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace </section>
|
|
||||||
<section angle="0"> show_letters preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
<section angle="0">asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph</section>
|
|
||||||
<section angle="0">copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright</section>
|
|
||||||
<section angle="0"> show_numbers backslash slash less greater equal bracketleft bracketright BackSpace </section>
|
|
||||||
<section angle="0"> show_letters preferences space period Return </section>
|
|
||||||
</view>
|
|
||||||
</geometry>
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<geometry version="0.90">
|
|
||||||
<bounds x="0" y="10.000000" width="426.0000" height="296.5853"/>
|
|
||||||
|
|
||||||
<outline id="default" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="37.46341" y="0.000000"/>
|
|
||||||
<point x="37.46341" y="52.44877"/>
|
|
||||||
<point x="0.000000" y="52.44877"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="altline" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="48.39024" y="0.000000"/>
|
|
||||||
<point x="48.39024" y="52.44877"/>
|
|
||||||
<point x="0.000000" y="52.44877"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="outline7" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="0.000000"/>
|
|
||||||
<point x="88.97561" y="52.44877"/>
|
|
||||||
<point x="0.000000" y="52.44877"/>
|
|
||||||
</outline>
|
|
||||||
<outline id="spaceline" corner-radius="1.000000">
|
|
||||||
<point x="0.000000" y="0.000000"/>
|
|
||||||
<point x="120.5853" y="0.000000"/>
|
|
||||||
<point x="120.5853" y="52.44877"/>
|
|
||||||
<point x="0.000000" y="52.44877"/>
|
|
||||||
</outline>
|
|
||||||
|
|
||||||
<button name="BackSpace" oref="altline" />
|
|
||||||
<button name="space" oref="spaceline" />
|
|
||||||
<button name="Return" oref="outline7" />
|
|
||||||
|
|
||||||
<view>
|
|
||||||
<section angle="0">1 2 3 parenleft parenright</section>
|
|
||||||
<section angle="0">4 5 6 numbersign asterisk</section>
|
|
||||||
<section angle="0">7 8 9 plus minus</section>
|
|
||||||
<section angle="0">BackSpace 0 space Return</section>
|
|
||||||
</view>
|
|
||||||
</geometry>
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<keyboards version="0.90">
|
|
||||||
<keyboard id="ar" name="ar"
|
|
||||||
geometry="compact" symbols="ar"
|
|
||||||
longname="Arabic" language="ar"/>
|
|
||||||
<keyboard id="be" name="be"
|
|
||||||
geometry="compact" symbols="be"
|
|
||||||
longname="Belarusian" language="be"/>
|
|
||||||
<keyboard id="fa" name="fa"
|
|
||||||
geometry="compact" symbols="fa"
|
|
||||||
longname="Farsi (ISIRI 2901-1994)" language="fa"/>
|
|
||||||
<keyboard id="he" name="he"
|
|
||||||
geometry="compact" symbols="he"
|
|
||||||
longname="Hebrew" language="he"/>
|
|
||||||
<keyboard id="ja" name="ja"
|
|
||||||
geometry="compact" symbols="ja-kana"
|
|
||||||
longname="Japanese (Kana)" language="ja"/>
|
|
||||||
<keyboard id="kk" name="kk"
|
|
||||||
geometry="compact" symbols="kk"
|
|
||||||
longname="Kazakh" language="kk"/>
|
|
||||||
<keyboard id="ks" name="ks"
|
|
||||||
geometry="compact" symbols="ks"
|
|
||||||
longname="Kashmiri" language="ks"/>
|
|
||||||
<keyboard id="my" name="my"
|
|
||||||
geometry="compact" symbols="my"
|
|
||||||
longname="Myanmar" language="my"/>
|
|
||||||
<keyboard id="nb" name="nb"
|
|
||||||
geometry="extended" symbols="nb"
|
|
||||||
longname="Norwegian" language="nb"/>
|
|
||||||
<keyboard id="ru" name="ru"
|
|
||||||
geometry="compact" symbols="us"
|
|
||||||
longname="Russian" language="ru"/>
|
|
||||||
<keyboard id="th" name="th"
|
|
||||||
geometry="compact" symbols="th"
|
|
||||||
longname="Thai" language="th"/>
|
|
||||||
<keyboard id="ua" name="ua"
|
|
||||||
geometry="compact" symbols="ua"
|
|
||||||
longname="Ukrainian" language="ua"/>
|
|
||||||
<keyboard id="ug" name="ug"
|
|
||||||
geometry="compact" symbols="ug"
|
|
||||||
longname="Uyghur" language="ug"/>
|
|
||||||
<keyboard id="us" name="us"
|
|
||||||
geometry="compact" symbols="us"
|
|
||||||
longname="US" language="en"/>
|
|
||||||
<keyboard id="zh-bopomofo" name="zh-bopomofo"
|
|
||||||
geometry="compact" symbols="zh-bopomofo"
|
|
||||||
longname="Chinese (Bopomofo)" language="zh"/>
|
|
||||||
<!-- Indic Inscript keyboards converted from m17n-lib -->
|
|
||||||
<keyboard id="as-inscript" name="as-inscript"
|
|
||||||
geometry="compact" symbols="as-inscript"
|
|
||||||
longname="Assamese (Inscript)" language="as"/>
|
|
||||||
<keyboard id="bn-inscript" name="bn-inscript"
|
|
||||||
geometry="compact" symbols="bn-inscript"
|
|
||||||
longname="Bengali (Inscript)" language="bn"/>
|
|
||||||
<keyboard id="gu-inscript" name="gu-inscript"
|
|
||||||
geometry="compact" symbols="gu-inscript"
|
|
||||||
longname="Gujarati (Inscript)" language="gu"/>
|
|
||||||
<keyboard id="hi-inscript" name="hi-inscript"
|
|
||||||
geometry="compact" symbols="hi-inscript"
|
|
||||||
longname="Hindi (Inscript)" language="hi"/>
|
|
||||||
<keyboard id="kn-inscript" name="kn-inscript"
|
|
||||||
geometry="compact" symbols="kn-inscript"
|
|
||||||
longname="Kannada (Inscript)" language="kn"/>
|
|
||||||
<keyboard id="ks-inscript" name="ks-inscript"
|
|
||||||
geometry="compact" symbols="ks-inscript"
|
|
||||||
longname="Kashmiri Devanagari (Inscript)" language="ks"/>
|
|
||||||
<keyboard id="mai-inscript" name="mai-inscript"
|
|
||||||
geometry="compact" symbols="mai-inscript"
|
|
||||||
longname="Maithili (Inscript)" language="mai"/>
|
|
||||||
<keyboard id="ml-inscript" name="ml-inscript"
|
|
||||||
geometry="compact" symbols="ml-inscript"
|
|
||||||
longname="Malayalam (Inscript)" language="ml-inscript"/>
|
|
||||||
<keyboard id="mr-inscript" name="mr-inscript"
|
|
||||||
geometry="compact" symbols="mr-inscript"
|
|
||||||
longname="Marathi (Inscript)" language="mr"/>
|
|
||||||
<keyboard id="or-inscript" name="or-inscript"
|
|
||||||
geometry="compact" symbols="or-inscript"
|
|
||||||
longname="Oriya (Inscript)" language="or"/>
|
|
||||||
<keyboard id="pa-inscript" name="pa-inscript"
|
|
||||||
geometry="compact" symbols="pa-inscript"
|
|
||||||
longname="Punjabi (Inscript)" language="pa"/>
|
|
||||||
<keyboard id="sd-inscript" name="sd-inscript"
|
|
||||||
geometry="compact" symbols="sd-inscript"
|
|
||||||
longname="Sindhi (Inscript)" language="sd"/>
|
|
||||||
<keyboard id="ta-inscript" name="ta-inscript"
|
|
||||||
geometry="compact" symbols="ta-inscript"
|
|
||||||
longname="Tamil (Inscript)" language="ta"/>
|
|
||||||
<keyboard id="te-inscript" name="te-inscript"
|
|
||||||
geometry="compact" symbols="te-inscript"
|
|
||||||
longname="Telugu (Inscript)" language="te"/>
|
|
||||||
|
|
||||||
<!-- Common keyboards -->
|
|
||||||
<keyboard id="number" name="number"
|
|
||||||
geometry="number-keypad" symbols="special/number"
|
|
||||||
longname="Numeric keypad" language="all"/>
|
|
||||||
<keyboard id="phone" name="phone"
|
|
||||||
geometry="number-keypad" symbols="special/number"
|
|
||||||
longname="Phone keypad" language="all"/>
|
|
||||||
|
|
||||||
</keyboards>
|
|
||||||
179
data/keyboards/nb.yaml
Normal file
179
data/keyboards/nb.yaml
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
---
|
||||||
|
bounds: { x: 0, y: 10, width: 426, height: 229 }
|
||||||
|
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 32, height: 52 }
|
||||||
|
altline:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
||||||
|
outline7:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
||||||
|
spaceline:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
|
||||||
|
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "q w e r t y u i o p aring"
|
||||||
|
- "a s d f g h j k l oslash ae"
|
||||||
|
- "Shift_L z x c v b n m BackSpace"
|
||||||
|
- "show_numbers preferences space period Return"
|
||||||
|
upper:
|
||||||
|
- "Q W E R T Y U I O P Aring"
|
||||||
|
- "A S D F G H J K L Oslash AE"
|
||||||
|
- "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"
|
||||||
|
- "at numbersign dollar percent ampersand minus underscore plus parenleft parenright"
|
||||||
|
- "show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace"
|
||||||
|
- "show_letters preferences space period Return"
|
||||||
|
symbols:
|
||||||
|
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
|
||||||
|
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
|
||||||
|
- "show_numbers backslash slash less greater equal bracketleft bracketright 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"
|
||||||
|
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: "*/="
|
||||||
|
period:
|
||||||
|
outline: altline
|
||||||
|
label: "."
|
||||||
|
space:
|
||||||
|
outline: spaceline
|
||||||
|
label: " "
|
||||||
|
Return:
|
||||||
|
outline: outline7
|
||||||
|
icon: "key-enter"
|
||||||
|
aring:
|
||||||
|
label: "å"
|
||||||
|
Aring:
|
||||||
|
label: "Å"
|
||||||
|
oslash:
|
||||||
|
label: "ø"
|
||||||
|
Oslash:
|
||||||
|
label: "Ø"
|
||||||
|
ae:
|
||||||
|
label: "æ"
|
||||||
|
AE:
|
||||||
|
label: "Æ"
|
||||||
|
asterisk:
|
||||||
|
label: "*"
|
||||||
|
asciitilde:
|
||||||
|
label: "~"
|
||||||
|
quoteleft:
|
||||||
|
label: "`"
|
||||||
|
bar:
|
||||||
|
label: "|"
|
||||||
|
U00B7:
|
||||||
|
label: "·"
|
||||||
|
squareroot:
|
||||||
|
label: "√"
|
||||||
|
Greek_pi:
|
||||||
|
label: "π"
|
||||||
|
division:
|
||||||
|
label: "÷"
|
||||||
|
multiply:
|
||||||
|
label: "×"
|
||||||
|
paragraph:
|
||||||
|
label: "¶"
|
||||||
|
Greek_tau:
|
||||||
|
label: "τ"
|
||||||
|
copyright:
|
||||||
|
label: "©"
|
||||||
|
numbersign:
|
||||||
|
label: "#"
|
||||||
|
U00AE:
|
||||||
|
label: "®"
|
||||||
|
at:
|
||||||
|
label: "@"
|
||||||
|
dollar:
|
||||||
|
label: "$"
|
||||||
|
U00A3:
|
||||||
|
label: "£"
|
||||||
|
percent:
|
||||||
|
label: "%"
|
||||||
|
EuroSign:
|
||||||
|
label: "€"
|
||||||
|
ampersand:
|
||||||
|
label: "&"
|
||||||
|
U00A5:
|
||||||
|
label: "¥"
|
||||||
|
minus:
|
||||||
|
label: "-"
|
||||||
|
asciicircum:
|
||||||
|
label: "^"
|
||||||
|
underscore:
|
||||||
|
label: "_"
|
||||||
|
degree:
|
||||||
|
label: "°"
|
||||||
|
plus:
|
||||||
|
label: "+"
|
||||||
|
equal:
|
||||||
|
label: "="
|
||||||
|
parenleft:
|
||||||
|
label: "("
|
||||||
|
parenright:
|
||||||
|
label: ")"
|
||||||
|
braceleft:
|
||||||
|
label: "{"
|
||||||
|
braceright:
|
||||||
|
label: "}"
|
||||||
|
comma:
|
||||||
|
label: ","
|
||||||
|
backslash:
|
||||||
|
label: "\\"
|
||||||
|
slash:
|
||||||
|
label: "/"
|
||||||
|
quotedbl:
|
||||||
|
label: "\""
|
||||||
|
quoteright:
|
||||||
|
label: "'"
|
||||||
|
less:
|
||||||
|
label: "<"
|
||||||
|
greater:
|
||||||
|
label: ">"
|
||||||
|
colon:
|
||||||
|
label: ":"
|
||||||
|
semicolon:
|
||||||
|
label: ";"
|
||||||
|
exclam:
|
||||||
|
label: "!"
|
||||||
|
question:
|
||||||
|
label: "?"
|
||||||
|
bracketleft:
|
||||||
|
label: "["
|
||||||
|
bracketright:
|
||||||
|
label: "]"
|
||||||
|
|
||||||
47
data/keyboards/number.yaml
Normal file
47
data/keyboards/number.yaml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
bounds: { x: 0, y: 10, width: 410, height: 229 }
|
||||||
|
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
|
||||||
|
altline:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
||||||
|
outline7:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
||||||
|
spaceline:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 120.5853, height: 52 }
|
||||||
|
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "1 2 3 parenleft parenright"
|
||||||
|
- "4 5 6 numbersign asterisk"
|
||||||
|
- "7 8 9 plus minus"
|
||||||
|
- "BackSpace 0 space Return"
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
BackSpace:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "edit-clear-symbolic"
|
||||||
|
space:
|
||||||
|
outline: spaceline
|
||||||
|
label: " "
|
||||||
|
Return:
|
||||||
|
outline: outline7
|
||||||
|
icon: "key-enter"
|
||||||
|
asterisk:
|
||||||
|
label: "*"
|
||||||
|
numbersign:
|
||||||
|
label: "#"
|
||||||
|
minus:
|
||||||
|
label: "-"
|
||||||
|
plus:
|
||||||
|
label: "+"
|
||||||
|
parenleft:
|
||||||
|
label: "("
|
||||||
|
parenright:
|
||||||
|
label: ")"
|
||||||
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
|
||||||
<symbols version="0.90">
|
|
||||||
<symbol label="*">asterisk</symbol>
|
|
||||||
<symbol label="+/=">show_symbols</symbol>
|
|
||||||
<symbol label="τ">Greek_tau</symbol>
|
|
||||||
<symbol label="å">aring</symbol>
|
|
||||||
<symbol label="ø">oslash</symbol>
|
|
||||||
<symbol label="æ">ae</symbol>
|
|
||||||
<symbol label="Å">Aring</symbol>
|
|
||||||
<symbol label="Ø">Oslash</symbol>
|
|
||||||
<symbol label="Æ">AE</symbol>
|
|
||||||
<symbol label="q">q</symbol>
|
|
||||||
<symbol label="Q">Q</symbol>
|
|
||||||
<symbol label="1">1</symbol>
|
|
||||||
<symbol label="~">asciitilde</symbol>
|
|
||||||
<symbol label="w">w</symbol>
|
|
||||||
<symbol label="W">W</symbol>
|
|
||||||
<symbol label="2">2</symbol>
|
|
||||||
<symbol label="`">quoteleft</symbol>
|
|
||||||
<symbol label="e">e</symbol>
|
|
||||||
<symbol label="E">E</symbol>
|
|
||||||
<symbol label="3">3</symbol>
|
|
||||||
<symbol label="|">bar</symbol>
|
|
||||||
<symbol label="r">r</symbol>
|
|
||||||
<symbol label="R">R</symbol>
|
|
||||||
<symbol label="4">4</symbol>
|
|
||||||
<symbol label="·">U00B7</symbol>
|
|
||||||
<symbol label="t">t</symbol>
|
|
||||||
<symbol label="T">T</symbol>
|
|
||||||
<symbol label="5">5</symbol>
|
|
||||||
<symbol label="√">squareroot</symbol>
|
|
||||||
<symbol label="y">y</symbol>
|
|
||||||
<symbol label="Y">Y</symbol>
|
|
||||||
<symbol label="6">6</symbol>
|
|
||||||
<symbol label="π">Greek_pi</symbol>
|
|
||||||
<symbol label="u">u</symbol>
|
|
||||||
<symbol label="U">U</symbol>
|
|
||||||
<symbol label="7">7</symbol>
|
|
||||||
<symbol label="÷">division</symbol>
|
|
||||||
<symbol label="i">i</symbol>
|
|
||||||
<symbol label="I">I</symbol>
|
|
||||||
<symbol label="8">8</symbol>
|
|
||||||
<symbol label="×">multiply</symbol>
|
|
||||||
<symbol label="o">o</symbol>
|
|
||||||
<symbol label="O">O</symbol>
|
|
||||||
<symbol label="9">9</symbol>
|
|
||||||
<symbol label="¶">paragraph</symbol>
|
|
||||||
<symbol label="p">p</symbol>
|
|
||||||
<symbol label="P">P</symbol>
|
|
||||||
<symbol label="0">0</symbol>
|
|
||||||
<symbol label="△">U25B3</symbol>
|
|
||||||
<symbol keyval="229" label="å">aring</symbol>
|
|
||||||
<symbol keyval="197" label="Å">Aring</symbol>
|
|
||||||
<symbol label="a">a</symbol>
|
|
||||||
<symbol label="A">A</symbol>
|
|
||||||
<symbol label="@">at</symbol>
|
|
||||||
<symbol label="©">copyright</symbol>
|
|
||||||
<symbol label="s">s</symbol>
|
|
||||||
<symbol label="S">S</symbol>
|
|
||||||
<symbol label="#">numbersign</symbol>
|
|
||||||
<symbol label="®">U00AE</symbol>
|
|
||||||
<symbol label="d">d</symbol>
|
|
||||||
<symbol label="D">D</symbol>
|
|
||||||
<symbol label="$">dollar</symbol>
|
|
||||||
<symbol label="£">U00A3</symbol>
|
|
||||||
<symbol label="f">f</symbol>
|
|
||||||
<symbol label="F">F</symbol>
|
|
||||||
<symbol label="%">percent</symbol>
|
|
||||||
<symbol label="€">EuroSign</symbol>
|
|
||||||
<symbol label="g">g</symbol>
|
|
||||||
<symbol label="G">G</symbol>
|
|
||||||
<symbol label="&">ampersand</symbol>
|
|
||||||
<symbol label="¥">U00A5</symbol>
|
|
||||||
<symbol label="h">h</symbol>
|
|
||||||
<symbol label="H">H</symbol>
|
|
||||||
<symbol label="-">minus</symbol>
|
|
||||||
<symbol label="^">asciicircum</symbol>
|
|
||||||
<symbol label="j">j</symbol>
|
|
||||||
<symbol label="J">J</symbol>
|
|
||||||
<symbol label="_">underscore</symbol>
|
|
||||||
<symbol label="°">degree</symbol>
|
|
||||||
<symbol label="k">k</symbol>
|
|
||||||
<symbol label="K">K</symbol>
|
|
||||||
<symbol label="+">plus</symbol>
|
|
||||||
<symbol label="=">equal</symbol>
|
|
||||||
<symbol label="l">l</symbol>
|
|
||||||
<symbol label="L">L</symbol>
|
|
||||||
<symbol label="(">parenleft</symbol>
|
|
||||||
<symbol label="{">braceleft</symbol>
|
|
||||||
<symbol keyval="248" label="ø">oslash</symbol>
|
|
||||||
<symbol keyval="216" label="Ø">Oslash</symbol>
|
|
||||||
<symbol label=")">parenright</symbol>
|
|
||||||
<symbol label="}">braceright</symbol>
|
|
||||||
<symbol keyval="230" label="æ">ae</symbol>
|
|
||||||
<symbol keyval="198" label="Æ">AE</symbol>
|
|
||||||
<symbol keyval="65293" icon="key-enter">Return</symbol>
|
|
||||||
<symbol keyval="65505" icon="key-shift">Shift_L</symbol>
|
|
||||||
<symbol label="z">z</symbol>
|
|
||||||
<symbol label="Z">Z</symbol>
|
|
||||||
<symbol label=",">comma</symbol>
|
|
||||||
<symbol label="\">backslash</symbol>
|
|
||||||
<symbol label="x">x</symbol>
|
|
||||||
<symbol label="X">X</symbol>
|
|
||||||
<symbol label=""">quotedbl</symbol>
|
|
||||||
<symbol label="/">slash</symbol>
|
|
||||||
<symbol label="c">c</symbol>
|
|
||||||
<symbol label="C">C</symbol>
|
|
||||||
<symbol label="'">quoteright</symbol>
|
|
||||||
<symbol label="<">less</symbol>
|
|
||||||
<symbol label="v">v</symbol>
|
|
||||||
<symbol label="V">V</symbol>
|
|
||||||
<symbol label=":">colon</symbol>
|
|
||||||
<symbol label=">">greater</symbol>
|
|
||||||
<symbol label="b">b</symbol>
|
|
||||||
<symbol label="B">B</symbol>
|
|
||||||
<symbol label=";">semicolon</symbol>
|
|
||||||
<symbol label="=">equal</symbol>
|
|
||||||
<symbol label="n">n</symbol>
|
|
||||||
<symbol label="N">N</symbol>
|
|
||||||
<symbol label="!">exclam</symbol>
|
|
||||||
<symbol label="[">bracketleft</symbol>
|
|
||||||
<symbol label="m">m</symbol>
|
|
||||||
<symbol label="M">M</symbol>
|
|
||||||
<symbol label="?">question</symbol>
|
|
||||||
<symbol label="]">bracketright</symbol>
|
|
||||||
<symbol label=".">period</symbol>
|
|
||||||
<symbol label="123">show_numbers</symbol>
|
|
||||||
<symbol label="ABC">show_letters</symbol>
|
|
||||||
<symbol label="☺" icon="keyboard-mode-symbolic" tooltip="Setup">preferences</symbol>
|
|
||||||
<symbol label=" ">space</symbol>
|
|
||||||
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
|
|
||||||
</symbols>
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='ASCII' standalone='yes'?>
|
|
||||||
<symbols version="0.90">
|
|
||||||
<symbol label="1">1</symbol>
|
|
||||||
<symbol label="2">2</symbol>
|
|
||||||
<symbol label="3">3</symbol>
|
|
||||||
<symbol label="(">parenleft</symbol>
|
|
||||||
<symbol label=")">parenright</symbol>
|
|
||||||
<symbol label="4">4</symbol>
|
|
||||||
<symbol label="5">5</symbol>
|
|
||||||
<symbol label="6">6</symbol>
|
|
||||||
<symbol label="#">numbersign</symbol>
|
|
||||||
<symbol label="*">asterisk</symbol>
|
|
||||||
<symbol label="7">7</symbol>
|
|
||||||
<symbol label="8">8</symbol>
|
|
||||||
<symbol label="9">9</symbol>
|
|
||||||
<symbol label="+">plus</symbol>
|
|
||||||
<symbol label="-">minus</symbol>
|
|
||||||
<symbol label="0">0</symbol>
|
|
||||||
<symbol keyval="65293" icon="key-enter">Return</symbol>
|
|
||||||
<symbol label=" ">space</symbol>
|
|
||||||
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
|
|
||||||
</symbols>
|
|
||||||
@ -1,118 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='ASCII' standalone='yes'?>
|
|
||||||
<symbols version="0.90">
|
|
||||||
<symbol label="*">asterisk</symbol>
|
|
||||||
<symbol label="+/=">show_symbols</symbol>
|
|
||||||
<symbol label="q">q</symbol>
|
|
||||||
<symbol label="Q">Q</symbol>
|
|
||||||
<symbol label="1">1</symbol>
|
|
||||||
<symbol label="~">asciitilde</symbol>
|
|
||||||
<symbol label="w">w</symbol>
|
|
||||||
<symbol label="W">W</symbol>
|
|
||||||
<symbol label="2">2</symbol>
|
|
||||||
<symbol label="`">quoteleft</symbol>
|
|
||||||
<symbol label="e">e</symbol>
|
|
||||||
<symbol label="E">E</symbol>
|
|
||||||
<symbol label="3">3</symbol>
|
|
||||||
<symbol label="|">bar</symbol>
|
|
||||||
<symbol label="r">r</symbol>
|
|
||||||
<symbol label="R">R</symbol>
|
|
||||||
<symbol label="4">4</symbol>
|
|
||||||
<symbol label="·">U00B7</symbol>
|
|
||||||
<symbol label="t">t</symbol>
|
|
||||||
<symbol label="T">T</symbol>
|
|
||||||
<symbol label="5">5</symbol>
|
|
||||||
<symbol label="√">squareroot</symbol>
|
|
||||||
<symbol label="y">y</symbol>
|
|
||||||
<symbol label="Y">Y</symbol>
|
|
||||||
<symbol label="6">6</symbol>
|
|
||||||
<symbol label="π">Greek_pi</symbol>
|
|
||||||
<symbol label="u">u</symbol>
|
|
||||||
<symbol label="U">U</symbol>
|
|
||||||
<symbol label="7">7</symbol>
|
|
||||||
<symbol label="÷">division</symbol>
|
|
||||||
<symbol label="i">i</symbol>
|
|
||||||
<symbol label="I">I</symbol>
|
|
||||||
<symbol label="8">8</symbol>
|
|
||||||
<symbol label="×">multiply</symbol>
|
|
||||||
<symbol label="o">o</symbol>
|
|
||||||
<symbol label="O">O</symbol>
|
|
||||||
<symbol label="9">9</symbol>
|
|
||||||
<symbol label="¶">paragraph</symbol>
|
|
||||||
<symbol label="p">p</symbol>
|
|
||||||
<symbol label="P">P</symbol>
|
|
||||||
<symbol label="0">0</symbol>
|
|
||||||
<symbol label="τ">Greek_tau</symbol>
|
|
||||||
<symbol label="a">a</symbol>
|
|
||||||
<symbol label="A">A</symbol>
|
|
||||||
<symbol label="@">at</symbol>
|
|
||||||
<symbol label="©">copyright</symbol>
|
|
||||||
<symbol label="s">s</symbol>
|
|
||||||
<symbol label="S">S</symbol>
|
|
||||||
<symbol label="#">numbersign</symbol>
|
|
||||||
<symbol label="®">U00AE</symbol>
|
|
||||||
<symbol label="d">d</symbol>
|
|
||||||
<symbol label="D">D</symbol>
|
|
||||||
<symbol label="$">dollar</symbol>
|
|
||||||
<symbol label="£">U00A3</symbol>
|
|
||||||
<symbol label="f">f</symbol>
|
|
||||||
<symbol label="F">F</symbol>
|
|
||||||
<symbol label="%">percent</symbol>
|
|
||||||
<symbol label="€">EuroSign</symbol>
|
|
||||||
<symbol label="g">g</symbol>
|
|
||||||
<symbol label="G">G</symbol>
|
|
||||||
<symbol label="&">ampersand</symbol>
|
|
||||||
<symbol label="¥">U00A5</symbol>
|
|
||||||
<symbol label="h">h</symbol>
|
|
||||||
<symbol label="H">H</symbol>
|
|
||||||
<symbol label="-">minus</symbol>
|
|
||||||
<symbol label="^">asciicircum</symbol>
|
|
||||||
<symbol label="j">j</symbol>
|
|
||||||
<symbol label="J">J</symbol>
|
|
||||||
<symbol label="_">underscore</symbol>
|
|
||||||
<symbol label="°">degree</symbol>
|
|
||||||
<symbol label="k">k</symbol>
|
|
||||||
<symbol label="K">K</symbol>
|
|
||||||
<symbol label="+">plus</symbol>
|
|
||||||
<symbol label="=">equal</symbol>
|
|
||||||
<symbol label="l">l</symbol>
|
|
||||||
<symbol label="L">L</symbol>
|
|
||||||
<symbol label="(">parenleft</symbol>
|
|
||||||
<symbol label="{">braceleft</symbol>
|
|
||||||
<symbol label=")">parenright</symbol>
|
|
||||||
<symbol label="}">braceright</symbol>
|
|
||||||
<symbol keyval="65293" icon="key-enter">Return</symbol>
|
|
||||||
<symbol keyval="65505" icon="key-shift">Shift_L</symbol>
|
|
||||||
<symbol label="z">z</symbol>
|
|
||||||
<symbol label="Z">Z</symbol>
|
|
||||||
<symbol label=",">comma</symbol>
|
|
||||||
<symbol label="\">backslash</symbol>
|
|
||||||
<symbol label="x">x</symbol>
|
|
||||||
<symbol label="X">X</symbol>
|
|
||||||
<symbol label=""">quotedbl</symbol>
|
|
||||||
<symbol label="/">slash</symbol>
|
|
||||||
<symbol label="c">c</symbol>
|
|
||||||
<symbol label="C">C</symbol>
|
|
||||||
<symbol label="'">quoteright</symbol>
|
|
||||||
<symbol label="<">less</symbol>
|
|
||||||
<symbol label="v">v</symbol>
|
|
||||||
<symbol label="V">V</symbol>
|
|
||||||
<symbol label=":">colon</symbol>
|
|
||||||
<symbol label=">">greater</symbol>
|
|
||||||
<symbol label="b">b</symbol>
|
|
||||||
<symbol label="B">B</symbol>
|
|
||||||
<symbol label=";">semicolon</symbol>
|
|
||||||
<symbol label="n">n</symbol>
|
|
||||||
<symbol label="N">N</symbol>
|
|
||||||
<symbol label="!">exclam</symbol>
|
|
||||||
<symbol label="[">bracketleft</symbol>
|
|
||||||
<symbol label="m">m</symbol>
|
|
||||||
<symbol label="M">M</symbol>
|
|
||||||
<symbol label="?">question</symbol>
|
|
||||||
<symbol label="]">bracketright</symbol>
|
|
||||||
<symbol label=".">period</symbol>
|
|
||||||
<symbol label="123">show_numbers</symbol>
|
|
||||||
<symbol label="ABC">show_letters</symbol>
|
|
||||||
<symbol label="☺" icon="keyboard-mode-symbolic" tooltip="Setup">preferences</symbol>
|
|
||||||
<symbol label=" ">space</symbol>
|
|
||||||
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
|
|
||||||
</symbols>
|
|
||||||
81
data/keyboards/us.yaml
Normal file
81
data/keyboards/us.yaml
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
bounds: { x: 10, y: 10, width: 410, height: 229 }
|
||||||
|
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
|
||||||
|
altline:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
||||||
|
outline7:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
||||||
|
spaceline:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, 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 , \" ' colon ; ! ? BackSpace"
|
||||||
|
- "show_letters preferences space . Return"
|
||||||
|
symbols:
|
||||||
|
- "~ ` | · √ π τ ÷ × ¶"
|
||||||
|
- "© ® £ € ¥ ^ ° * { }"
|
||||||
|
- "show_numbers \\ / < > = [ ] BackSpace"
|
||||||
|
- "show_letters preferences space . Return"
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
Shift_L:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "upper"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-shift"
|
||||||
|
BackSpace:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "edit-clear-symbolic"
|
||||||
|
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
|
||||||
|
label: " "
|
||||||
|
Return:
|
||||||
|
outline: outline7
|
||||||
|
icon: "key-enter"
|
||||||
|
colon:
|
||||||
|
label: ":"
|
||||||
|
"\"":
|
||||||
|
keysym: "quotedbl"
|
||||||
@ -6,4 +6,5 @@ Exec=squeekboard
|
|||||||
Icon=squeekboard
|
Icon=squeekboard
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
|
NoDisplay=true
|
||||||
Categories=GTK;Utility;
|
Categories=GTK;Utility;
|
||||||
|
|||||||
@ -2,10 +2,6 @@
|
|||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/sm/puri/squeekboard">
|
<gresource prefix="/sm/puri/squeekboard">
|
||||||
<file compressed="true">style.css</file>
|
<file compressed="true">style.css</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/compact.xml</file>
|
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/extended.xml</file>
|
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/number-keypad.xml</file>
|
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/keyboards.xml</file>
|
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ar.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ar.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/as-inscript.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/as-inscript.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/be.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/be.xml</file>
|
||||||
@ -23,7 +19,6 @@
|
|||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ml-inscript.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ml-inscript.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/mr-inscript.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/mr-inscript.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/my.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/my.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/nb.xml</file>
|
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/or-inscript.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/or-inscript.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/pa-inscript.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/pa-inscript.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ru.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ru.xml</file>
|
||||||
@ -33,9 +28,7 @@
|
|||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/th.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/th.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ua.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ua.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ug.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ug.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/us.xml</file>
|
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/zh-bopomofo.xml</file>
|
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/zh-bopomofo.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/special/number.xml</file>
|
|
||||||
<file>icons/key-enter.svg</file>
|
<file>icons/key-enter.svg</file>
|
||||||
<file>icons/key-shift.svg</file>
|
<file>icons/key-shift.svg</file>
|
||||||
<file>icons/keyboard-mode-symbolic.svg</file>
|
<file>icons/keyboard-mode-symbolic.svg</file>
|
||||||
|
|||||||
@ -17,3 +17,23 @@
|
|||||||
background: #1c71d8;
|
background: #1c71d8;
|
||||||
border-color: #3584e4;
|
border-color: #3584e4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Return {
|
||||||
|
background: #1c71d8;
|
||||||
|
border-color: #1a5fb4
|
||||||
|
}
|
||||||
|
|
||||||
|
#Return:active {
|
||||||
|
background: #1c71d8;
|
||||||
|
border-color: #3584e4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Shift_L {
|
||||||
|
background: #2b292f;
|
||||||
|
border-color: #3e3a44
|
||||||
|
}
|
||||||
|
|
||||||
|
#Shift_L:active {
|
||||||
|
background: #1c71d8;
|
||||||
|
border-color: #3584e4;
|
||||||
|
}
|
||||||
|
|||||||
12
debian/cargo/config
vendored
Normal file
12
debian/cargo/config
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# When modifying this file, consider instead
|
||||||
|
# to take advantage of the method that Cargo packagers use
|
||||||
|
# to set up all the necessary stuff automatically:
|
||||||
|
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=907629#30
|
||||||
|
|
||||||
|
[source.crates-io]
|
||||||
|
registry = 'https://github.com/rust-lang/crates.io-index'
|
||||||
|
replace-with = 'vendored-sources'
|
||||||
|
|
||||||
|
[source.vendored-sources]
|
||||||
|
directory = '/usr/share/cargo/registry'
|
||||||
|
|
||||||
13
debian/changelog
vendored
13
debian/changelog
vendored
@ -1,3 +1,16 @@
|
|||||||
|
squeekboard (1.2.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Use Cargo-based dependencies
|
||||||
|
|
||||||
|
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 24 Sep 2019 10:42:15 +0000
|
||||||
|
|
||||||
|
squeekboard (1.1.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Use new keyboard layout format
|
||||||
|
|
||||||
|
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 02 Sep 2019 10:12:02 +0000
|
||||||
|
|
||||||
|
|
||||||
squeekboard (1.0.10) unstable; urgency=medium
|
squeekboard (1.0.10) unstable; urgency=medium
|
||||||
|
|
||||||
* Use a shared DBus definition
|
* Use a shared DBus definition
|
||||||
|
|||||||
9
debian/control
vendored
9
debian/control
vendored
@ -3,12 +3,19 @@ Section: x11
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
|
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
|
||||||
Build-Depends:
|
Build-Depends:
|
||||||
|
cargo,
|
||||||
debhelper (>= 10),
|
debhelper (>= 10),
|
||||||
meson (>=0.43.0),
|
meson (>=0.51.0),
|
||||||
|
ninja-build,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
libglib2.0-dev,
|
libglib2.0-dev,
|
||||||
libgtk-3-dev,
|
libgtk-3-dev,
|
||||||
libcroco3-dev,
|
libcroco3-dev,
|
||||||
|
librust-bitflags-1-dev (>= 1.0),
|
||||||
|
librust-maplit-1-dev (>= 1.0),
|
||||||
|
librust-serde-derive-1-dev (>= 1.0),
|
||||||
|
librust-serde-yaml-0.8-dev (>= 0.8),
|
||||||
|
librust-xkbcommon-0.4+wayland-dev (>= 0.4),
|
||||||
libwayland-dev (>= 1.16),
|
libwayland-dev (>= 1.16),
|
||||||
rustc,
|
rustc,
|
||||||
wayland-protocols (>= 1.14),
|
wayland-protocols (>= 1.14),
|
||||||
|
|||||||
7
debian/rules
vendored
7
debian/rules
vendored
@ -1,8 +1,15 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
export CARGO_HOME = $(CURDIR)/debian/cargo
|
||||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@ --builddirectory=_build --buildsystem=meson
|
dh $@ --builddirectory=_build --buildsystem=meson
|
||||||
|
|
||||||
|
# The Debian version of linked-hash-map doesn't provide any hash,
|
||||||
|
# causing Cargo to refuse to build with a crates.io copy
|
||||||
|
build:
|
||||||
|
rm Cargo.lock
|
||||||
|
dh $@ --builddirectory=_build --buildsystem=meson
|
||||||
|
|
||||||
override_dh_autoreconf:
|
override_dh_autoreconf:
|
||||||
|
|||||||
2
debian/squeekboard.lintian-overrides
vendored
Normal file
2
debian/squeekboard.lintian-overrides
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# yaml-rust 0.4.3 shares some roots with libyaml, including the string which lintian checks, creating a false positive
|
||||||
|
squeekboard binary: embedded-library usr/bin/squeekboard: libyaml
|
||||||
@ -34,7 +34,6 @@
|
|||||||
|
|
||||||
#include "eek-renderer.h"
|
#include "eek-renderer.h"
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
#include "src/symbol.h"
|
|
||||||
|
|
||||||
#include "eek-gtk-keyboard.h"
|
#include "eek-gtk-keyboard.h"
|
||||||
|
|
||||||
@ -62,15 +61,15 @@ typedef struct _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)
|
||||||
|
|
||||||
static void on_button_pressed (struct squeek_button *button, struct squeek_view *view,
|
static void on_button_pressed (struct squeek_button *button, struct squeek_view *view,
|
||||||
EekGtkKeyboard *self);
|
EekGtkKeyboard *self);
|
||||||
static void on_button_released (struct squeek_button *button,
|
static void on_button_released (const struct squeek_button *button,
|
||||||
struct squeek_view *view,
|
struct squeek_view *view,
|
||||||
EekGtkKeyboard *self);
|
EekGtkKeyboard *self);
|
||||||
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
|
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
|
||||||
static void render_locked_button (GtkWidget *widget,
|
static void render_locked_button (GtkWidget *widget,
|
||||||
struct button_place *place);
|
struct button_place *place);
|
||||||
static void render_released_button (GtkWidget *widget,
|
static void render_released_button (GtkWidget *widget,
|
||||||
struct squeek_button *button);
|
const struct squeek_button *button);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
eek_gtk_keyboard_real_realize (GtkWidget *self)
|
eek_gtk_keyboard_real_realize (GtkWidget *self)
|
||||||
@ -81,7 +80,8 @@ eek_gtk_keyboard_real_realize (GtkWidget *self)
|
|||||||
GDK_KEY_RELEASE_MASK |
|
GDK_KEY_RELEASE_MASK |
|
||||||
GDK_BUTTON_PRESS_MASK |
|
GDK_BUTTON_PRESS_MASK |
|
||||||
GDK_BUTTON_RELEASE_MASK |
|
GDK_BUTTON_RELEASE_MASK |
|
||||||
GDK_BUTTON_MOTION_MASK);
|
GDK_BUTTON_MOTION_MASK |
|
||||||
|
GDK_TOUCH_MASK);
|
||||||
|
|
||||||
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
|
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
|
||||||
}
|
}
|
||||||
@ -109,26 +109,26 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
|
|||||||
|
|
||||||
eek_renderer_render_keyboard (priv->renderer, cr);
|
eek_renderer_render_keyboard (priv->renderer, cr);
|
||||||
|
|
||||||
struct squeek_view *view = priv->keyboard->views[priv->keyboard->level];
|
struct squeek_view *view = squeek_layout_get_current_view(priv->keyboard->layout);
|
||||||
|
|
||||||
/* redraw pressed key */
|
/* redraw pressed key */
|
||||||
const GList *list = priv->keyboard->pressed_buttons;
|
const GList *list = priv->keyboard->pressed_keys;
|
||||||
for (const GList *head = list; head; head = g_list_next (head)) {
|
for (const GList *head = list; head; head = g_list_next (head)) {
|
||||||
struct button_place place = squeek_view_find_key(
|
struct button_place place = squeek_view_find_key(
|
||||||
view, squeek_button_get_key(head->data)
|
view, head->data
|
||||||
);
|
);
|
||||||
render_pressed_button (self, &place);
|
if (place.button)
|
||||||
|
render_pressed_button (self, &place);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* redraw locked key */
|
/* redraw locked key */
|
||||||
list = priv->keyboard->locked_buttons;
|
list = priv->keyboard->locked_keys;
|
||||||
for (const GList *head = list; head; head = g_list_next (head)) {
|
for (const GList *head = list; head; head = g_list_next (head)) {
|
||||||
struct button_place place = squeek_view_find_key(
|
struct button_place place = squeek_view_find_key(
|
||||||
view, squeek_button_get_key(
|
view, ((EekModifierKey *)head->data)->key
|
||||||
((EekModifierKey *)head->data)->button
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
render_locked_button (self, &place);
|
if (place.button)
|
||||||
|
render_locked_button (self, &place);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -158,56 +158,78 @@ static void depress(EekGtkKeyboard *self,
|
|||||||
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
eek_keyboard_press_button(priv->keyboard, button, time);
|
eek_keyboard_press_key(
|
||||||
|
priv->keyboard,
|
||||||
|
squeek_button_get_key(button),
|
||||||
|
time
|
||||||
|
);
|
||||||
on_button_pressed(button, view, self);
|
on_button_pressed(button, view, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drag(EekGtkKeyboard *self,
|
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);
|
||||||
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
||||||
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
||||||
GList *list, *head;
|
GList *list, *head;
|
||||||
|
|
||||||
list = g_list_copy(priv->keyboard->pressed_buttons);
|
list = g_list_copy(priv->keyboard->pressed_keys);
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
gboolean found = FALSE;
|
gboolean found = FALSE;
|
||||||
|
|
||||||
for (head = list; head; head = g_list_next (head)) {
|
for (head = list; head; head = g_list_next (head)) {
|
||||||
if (head->data == button) {
|
struct squeek_key *key = head->data;
|
||||||
|
if (squeek_button_has_key(button, key)) {
|
||||||
found = TRUE;
|
found = TRUE;
|
||||||
} else {
|
} else {
|
||||||
eek_keyboard_release_button(priv->keyboard, head->data, time);
|
eek_keyboard_release_key(priv->keyboard, key, time);
|
||||||
on_button_released(button, view, self);
|
// The released handler proceeds to ignore this info...
|
||||||
|
// let's do this for consistency nevertheless
|
||||||
|
struct button_place place = squeek_view_find_key(view, key);
|
||||||
|
on_button_released(place.button, view, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
g_list_free (list);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
eek_keyboard_press_button(priv->keyboard, button, time);
|
eek_keyboard_press_key(
|
||||||
|
priv->keyboard,
|
||||||
|
squeek_button_get_key(button),
|
||||||
|
time
|
||||||
|
);
|
||||||
|
|
||||||
on_button_pressed(button, view, self);
|
on_button_pressed(button, view, self);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (head = list; head; head = g_list_next (head)) {
|
for (head = list; head; head = g_list_next (head)) {
|
||||||
eek_keyboard_release_button(priv->keyboard, head->data, time);
|
struct squeek_key *key = head->data;
|
||||||
on_button_released(head->data, view, self);
|
eek_keyboard_release_key(priv->keyboard, key, time);
|
||||||
|
// The released handler proceeds to ignore this info...
|
||||||
|
// let's do this for consistency nevertheless
|
||||||
|
struct button_place place = squeek_view_find_key(view, key);
|
||||||
|
on_button_released(place.button, view, self);
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
g_list_free (list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
||||||
|
|
||||||
GList *list = g_list_copy(priv->keyboard->pressed_buttons);
|
GList *list = g_list_copy(priv->keyboard->pressed_keys);
|
||||||
for (GList *head = list; head; head = g_list_next (head)) {
|
for (GList *head = list; head; head = g_list_next (head)) {
|
||||||
struct squeek_button *button = head->data;
|
struct squeek_key *key = head->data;
|
||||||
eek_keyboard_release_button(priv->keyboard, button, time);
|
eek_keyboard_release_key(priv->keyboard, key, time);
|
||||||
on_button_released(button, view, self);
|
// The released handler proceeds to ignore this info...
|
||||||
|
// let's do this for consistency nevertheless
|
||||||
|
struct button_place place = squeek_view_find_key(view, key);
|
||||||
|
on_button_released(place.button, view, self);
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
g_list_free (list);
|
||||||
}
|
}
|
||||||
@ -252,27 +274,27 @@ handle_touch_event (GtkWidget *widget,
|
|||||||
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
||||||
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
||||||
|
|
||||||
|
/* For each new touch, release the previous one and record the new event
|
||||||
|
sequence. */
|
||||||
if (event->type == GDK_TOUCH_BEGIN) {
|
if (event->type == GDK_TOUCH_BEGIN) {
|
||||||
if (priv->sequence) {
|
release(self, event->time);
|
||||||
// Ignore second and following touch points
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
priv->sequence = event->sequence;
|
priv->sequence = event->sequence;
|
||||||
depress(self, event->x, event->y, event->time);
|
depress(self, event->x, event->y, event->time);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->sequence != event->sequence) {
|
/* Only allow the latest touch point to be dragged. */
|
||||||
return FALSE;
|
if (event->type == GDK_TOUCH_UPDATE && event->sequence == priv->sequence) {
|
||||||
}
|
|
||||||
|
|
||||||
if (event->type == GDK_TOUCH_UPDATE) {
|
|
||||||
drag(self, event->x, event->y, event->time);
|
drag(self, event->x, event->y, event->time);
|
||||||
}
|
}
|
||||||
if (event->type == GDK_TOUCH_END || event->type == GDK_TOUCH_CANCEL) {
|
else if (event->type == GDK_TOUCH_END || event->type == GDK_TOUCH_CANCEL) {
|
||||||
// TODO: can the event have different coords than the previous update event?
|
// TODO: can the event have different coords than the previous update event?
|
||||||
release(self, event->time);
|
/* Only respond to the release of the latest touch point. Previous
|
||||||
priv->sequence = NULL;
|
touches have already been released. */
|
||||||
|
if (event->sequence == priv->sequence) {
|
||||||
|
release(self, event->time);
|
||||||
|
priv->sequence = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -284,49 +306,20 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
|
|||||||
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
|
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
|
||||||
|
|
||||||
if (priv->keyboard) {
|
if (priv->keyboard) {
|
||||||
GList *list, *head;
|
GList *head;
|
||||||
|
|
||||||
/* Make a copy of HEAD before sending "released" signal on
|
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
|
||||||
elements, so that the default handler of
|
/* Unlike other places where we call this, we don't call
|
||||||
EekKeyboard::key-released signal can remove elements from its
|
on_button_released afterwards since we don't want to queue a
|
||||||
internal copy */
|
redraw. */
|
||||||
list = g_list_copy(priv->keyboard->pressed_buttons);
|
eek_keyboard_release_key(priv->keyboard, head->data,
|
||||||
for (head = list; head; head = g_list_next (head)) {
|
gdk_event_get_time(NULL));
|
||||||
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey released");
|
|
||||||
g_signal_emit_by_name (head->data, "released");
|
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->unmap (self);
|
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->unmap (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
eek_gtk_keyboard_real_query_tooltip (GtkWidget *widget,
|
|
||||||
gint x,
|
|
||||||
gint y,
|
|
||||||
gboolean keyboard_tooltip,
|
|
||||||
GtkTooltip *tooltip)
|
|
||||||
{
|
|
||||||
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
|
||||||
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
|
||||||
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
|
||||||
|
|
||||||
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer,
|
|
||||||
view,
|
|
||||||
(gdouble)x,
|
|
||||||
(gdouble)y);
|
|
||||||
if (button) {
|
|
||||||
//struct squeek_symbol *symbol = eek_key_get_symbol_at_index(key, 0, priv->keyboard->level);
|
|
||||||
const gchar *text = NULL; // FIXME
|
|
||||||
if (text) {
|
|
||||||
gtk_tooltip_set_text (tooltip, text);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
eek_gtk_keyboard_set_property (GObject *object,
|
eek_gtk_keyboard_set_property (GObject *object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
@ -349,17 +342,16 @@ eek_gtk_keyboard_dispose (GObject *object)
|
|||||||
if (priv->renderer) {
|
if (priv->renderer) {
|
||||||
g_object_unref (priv->renderer);
|
g_object_unref (priv->renderer);
|
||||||
priv->renderer = NULL;
|
priv->renderer = NULL;
|
||||||
|
priv->renderer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->keyboard) {
|
if (priv->keyboard) {
|
||||||
GList *list, *head;
|
GList *head;
|
||||||
|
|
||||||
list = g_list_copy(priv->keyboard->pressed_buttons);
|
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
|
||||||
for (head = list; head; head = g_list_next (head)) {
|
eek_keyboard_release_key(priv->keyboard, head->data,
|
||||||
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey pressed");
|
gdk_event_get_time(NULL));
|
||||||
g_signal_emit_by_name (head->data, "released", level_keyboard_current(priv->keyboard));
|
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
|
||||||
|
|
||||||
priv->keyboard = NULL;
|
priv->keyboard = NULL;
|
||||||
}
|
}
|
||||||
@ -383,8 +375,6 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
|
|||||||
eek_gtk_keyboard_real_button_release_event;
|
eek_gtk_keyboard_real_button_release_event;
|
||||||
widget_class->motion_notify_event =
|
widget_class->motion_notify_event =
|
||||||
eek_gtk_keyboard_real_motion_notify_event;
|
eek_gtk_keyboard_real_motion_notify_event;
|
||||||
widget_class->query_tooltip =
|
|
||||||
eek_gtk_keyboard_real_query_tooltip;
|
|
||||||
widget_class->touch_event = handle_touch_event;
|
widget_class->touch_event = handle_touch_event;
|
||||||
|
|
||||||
gobject_class->set_property = eek_gtk_keyboard_set_property;
|
gobject_class->set_property = eek_gtk_keyboard_set_property;
|
||||||
@ -465,9 +455,10 @@ render_locked_button (GtkWidget *widget, struct button_place *place)
|
|||||||
cairo_region_destroy (region);
|
cairo_region_destroy (region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: does it really redraw the entire keyboard?
|
||||||
static void
|
static void
|
||||||
render_released_button (GtkWidget *widget,
|
render_released_button (GtkWidget *widget,
|
||||||
struct squeek_button *button)
|
const struct squeek_button *button)
|
||||||
{
|
{
|
||||||
(void)button;
|
(void)button;
|
||||||
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
||||||
@ -487,8 +478,8 @@ render_released_button (GtkWidget *widget,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
on_button_pressed (struct squeek_button *button,
|
on_button_pressed (struct squeek_button *button,
|
||||||
struct squeek_view *view,
|
struct squeek_view *view,
|
||||||
EekGtkKeyboard *self)
|
EekGtkKeyboard *self)
|
||||||
{
|
{
|
||||||
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
||||||
|
|
||||||
@ -516,9 +507,9 @@ on_button_pressed (struct squeek_button *button,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_button_released (struct squeek_button *button,
|
on_button_released (const struct squeek_button *button,
|
||||||
struct squeek_view *view,
|
struct squeek_view *view,
|
||||||
EekGtkKeyboard *self)
|
EekGtkKeyboard *self)
|
||||||
{
|
{
|
||||||
(void)view;
|
(void)view;
|
||||||
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
||||||
|
|||||||
@ -34,7 +34,6 @@
|
|||||||
#include "eekboard/key-emitter.h"
|
#include "eekboard/key-emitter.h"
|
||||||
#include "keymap.h"
|
#include "keymap.h"
|
||||||
#include "src/keyboard.h"
|
#include "src/keyboard.h"
|
||||||
#include "src/symbol.h"
|
|
||||||
|
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
|
|
||||||
@ -50,76 +49,52 @@ eek_modifier_key_free (EekModifierKey *modkey)
|
|||||||
g_slice_free (EekModifierKey, modkey);
|
g_slice_free (EekModifierKey, modkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the state of locked keys based on the key that was activated
|
void
|
||||||
/// FIXME: make independent of what the key are named,
|
eek_keyboard_set_key_locked (LevelKeyboard *keyboard,
|
||||||
/// and instead refer to the contained symbols
|
struct squeek_key *key)
|
||||||
static guint
|
|
||||||
set_key_states (LevelKeyboard *keyboard,
|
|
||||||
struct squeek_button *button,
|
|
||||||
guint new_level)
|
|
||||||
{
|
{
|
||||||
struct squeek_key *key = squeek_button_get_key(button);
|
EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
|
||||||
// Keys locking rules hardcoded for the time being...
|
modifier_key->modifiers = 0;
|
||||||
const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(key));
|
modifier_key->key = key;
|
||||||
// Lock the shift whenever it's pressed on the baselevel
|
keyboard->locked_keys =
|
||||||
// TODO: need to lock shift on the destination level
|
g_list_prepend (keyboard->locked_keys, modifier_key);
|
||||||
if (g_strcmp0(name, "Shift_L") == 0 && keyboard->level == 0) {
|
}
|
||||||
EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
|
|
||||||
modifier_key->modifiers = 0;
|
/// Unlock all locked keys.
|
||||||
modifier_key->button = button;
|
/// All locked keys will unlock at the next keypress (should be called "stuck")
|
||||||
keyboard->locked_buttons =
|
/// Returns the number of handled keys
|
||||||
g_list_prepend (keyboard->locked_buttons, modifier_key);
|
/// TODO: may need to check key type in order to chain locks
|
||||||
squeek_key_set_locked(key, true);
|
/// before pressing an "emitting" key
|
||||||
}
|
static int unlock_keys(LevelKeyboard *keyboard) {
|
||||||
if (keyboard->level == 1) {
|
int handled = 0;
|
||||||
// Only shift is locked in this state, unlock on any key press
|
for (GList *head = keyboard->locked_keys; head; ) {
|
||||||
for (GList *head = keyboard->locked_buttons; head; ) {
|
EekModifierKey *modifier_key = head->data;
|
||||||
EekModifierKey *modifier_key = head->data;
|
GList *next = g_list_next (head);
|
||||||
GList *next = g_list_next (head);
|
keyboard->locked_keys =
|
||||||
keyboard->locked_buttons =
|
g_list_remove_link (keyboard->locked_keys, head);
|
||||||
g_list_remove_link (keyboard->locked_buttons, head);
|
//squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
|
||||||
squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
|
|
||||||
g_list_free1 (head);
|
squeek_layout_set_state_from_press(keyboard->layout, keyboard, modifier_key->key);
|
||||||
head = next;
|
g_list_free1 (head);
|
||||||
}
|
head = next;
|
||||||
return 0;
|
handled++;
|
||||||
}
|
}
|
||||||
return new_level;
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: unhardcode, parse some user information as to which key triggers which view (level)
|
|
||||||
static void
|
static void
|
||||||
set_level_from_press (LevelKeyboard *keyboard, struct squeek_button *button)
|
set_level_from_press (LevelKeyboard *keyboard, struct squeek_key *key)
|
||||||
{
|
{
|
||||||
/* The levels are: 0 Letters, 1 Upper case letters, 2 Numbers, 3 Symbols */
|
// If the currently locked key was already handled in the unlock phase,
|
||||||
guint level = keyboard->level;
|
// then skip
|
||||||
/* Handle non-emitting keys */
|
if (unlock_keys(keyboard) == 0) {
|
||||||
if (button) {
|
squeek_layout_set_state_from_press(keyboard->layout, keyboard, key);
|
||||||
const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(squeek_button_get_key(button)));
|
|
||||||
if (g_strcmp0(name, "show_numbers") == 0) {
|
|
||||||
level = 2;
|
|
||||||
} else if (g_strcmp0(name, "show_letters") == 0) {
|
|
||||||
level = 0;
|
|
||||||
} else if (g_strcmp0(name, "show_symbols") == 0) {
|
|
||||||
level = 3;
|
|
||||||
} else if (g_strcmp0(name, "Shift_L") == 0) {
|
|
||||||
level ^= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboard->level = set_key_states(keyboard, button, level);
|
|
||||||
|
|
||||||
eek_layout_update_layout(keyboard);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void eek_keyboard_press_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp) {
|
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp) {
|
||||||
struct squeek_key *key = squeek_button_get_key(button);
|
|
||||||
squeek_key_set_pressed(key, TRUE);
|
squeek_key_set_pressed(key, TRUE);
|
||||||
keyboard->pressed_buttons = g_list_prepend (keyboard->pressed_buttons, button);
|
keyboard->pressed_keys = g_list_prepend (keyboard->pressed_keys, key);
|
||||||
|
|
||||||
struct squeek_symbol *symbol = squeek_key_get_symbol(key);
|
|
||||||
if (!symbol)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Only take action about setting level *after* the key has taken effect, i.e. on release
|
// Only take action about setting level *after* the key has taken effect, i.e. on release
|
||||||
//set_level_from_press (keyboard, key);
|
//set_level_from_press (keyboard, key);
|
||||||
@ -131,43 +106,28 @@ void eek_keyboard_press_button(LevelKeyboard *keyboard, struct squeek_button *bu
|
|||||||
emit_key_activated(keyboard->manager, keyboard, keycode, TRUE, timestamp);
|
emit_key_activated(keyboard->manager, keyboard, keycode, TRUE, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void eek_keyboard_release_button(LevelKeyboard *keyboard,
|
void eek_keyboard_release_key(LevelKeyboard *keyboard,
|
||||||
struct squeek_button *button,
|
struct squeek_key *key,
|
||||||
guint32 timestamp) {
|
guint32 timestamp) {
|
||||||
for (GList *head = keyboard->pressed_buttons; head; head = g_list_next (head)) {
|
for (GList *head = keyboard->pressed_keys; head; head = g_list_next (head)) {
|
||||||
if (head->data == button) {
|
if (squeek_key_equal(head->data, key)) {
|
||||||
keyboard->pressed_buttons = g_list_remove_link (keyboard->pressed_buttons, head);
|
keyboard->pressed_keys = g_list_remove_link (keyboard->pressed_keys, head);
|
||||||
g_list_free1 (head);
|
g_list_free1 (head);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct squeek_symbol *symbol = squeek_button_get_symbol(button);
|
set_level_from_press (keyboard, key);
|
||||||
if (!symbol)
|
|
||||||
return;
|
|
||||||
|
|
||||||
set_level_from_press (keyboard, button);
|
|
||||||
|
|
||||||
// "Borrowed" from eek-context-service; doesn't influence the state but forwards the event
|
// "Borrowed" from eek-context-service; doesn't influence the state but forwards the event
|
||||||
|
|
||||||
guint keycode = squeek_key_get_keycode (squeek_button_get_key(button));
|
guint keycode = squeek_key_get_keycode (key);
|
||||||
|
|
||||||
emit_key_activated(keyboard->manager, keyboard, keycode, FALSE, timestamp);
|
emit_key_activated(keyboard->manager, keyboard, keycode, FALSE, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void level_keyboard_deinit(LevelKeyboard *self) {
|
void level_keyboard_deinit(LevelKeyboard *self) {
|
||||||
g_hash_table_destroy (self->names);
|
squeek_layout_free(self->layout);
|
||||||
for (guint i = 0; i < self->outline_array->len; i++) {
|
|
||||||
EekOutline *outline = &g_array_index (self->outline_array,
|
|
||||||
EekOutline,
|
|
||||||
i);
|
|
||||||
g_slice_free1 (sizeof (EekPoint) * outline->num_points,
|
|
||||||
outline->points);
|
|
||||||
}
|
|
||||||
g_array_free (self->outline_array, TRUE);
|
|
||||||
for (guint i = 0; i < 4; i++) {
|
|
||||||
// free self->view[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void level_keyboard_free(LevelKeyboard *self) {
|
void level_keyboard_free(LevelKeyboard *self) {
|
||||||
@ -175,115 +135,18 @@ void level_keyboard_free(LevelKeyboard *self) {
|
|||||||
g_free(self);
|
g_free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
void level_keyboard_init(LevelKeyboard *self) {
|
void level_keyboard_init(LevelKeyboard *self, struct squeek_layout *layout) {
|
||||||
self->outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline));
|
self->layout = layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_view *views[4], GHashTable *name_button_hash) {
|
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);
|
level_keyboard_init(keyboard, layout);
|
||||||
for (uint i = 0; i < 4; i++) {
|
|
||||||
keyboard->views[i] = views[i];
|
|
||||||
}
|
|
||||||
keyboard->manager = manager;
|
keyboard->manager = manager;
|
||||||
keyboard->names = name_button_hash;
|
|
||||||
return keyboard;
|
return keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* eek_keyboard_find_key_by_name:
|
|
||||||
* @keyboard: an #EekKeyboard
|
|
||||||
* @name: a key name
|
|
||||||
*
|
|
||||||
* Find an #EekKey whose name is @name.
|
|
||||||
* Return value: (transfer none): #EekKey whose name is @name
|
|
||||||
*/
|
|
||||||
struct squeek_button*
|
|
||||||
eek_keyboard_find_button_by_name (LevelKeyboard *keyboard,
|
|
||||||
const gchar *name)
|
|
||||||
{
|
|
||||||
return g_hash_table_lookup (keyboard->names, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eek_keyboard_get_outline:
|
|
||||||
* @keyboard: an #EekKeyboard
|
|
||||||
* @oref: ID of the outline
|
|
||||||
*
|
|
||||||
* Get an outline associated with @oref in @keyboard.
|
|
||||||
* Returns: an #EekOutline, which should not be released
|
|
||||||
*/
|
|
||||||
EekOutline *
|
|
||||||
level_keyboard_get_outline (LevelKeyboard *keyboard,
|
|
||||||
guint oref)
|
|
||||||
{
|
|
||||||
if (oref > keyboard->outline_array->len)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return &g_array_index (keyboard->outline_array, EekOutline, oref);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eek_keyboard_get_keymap:
|
|
||||||
* @keyboard: an #EekKeyboard
|
|
||||||
*
|
|
||||||
* Get the keymap for the keyboard.
|
|
||||||
* Returns: a string containing the XKB keymap.
|
|
||||||
*/
|
|
||||||
gchar *
|
|
||||||
eek_keyboard_get_keymap(LevelKeyboard *keyboard)
|
|
||||||
{
|
|
||||||
/* Start the keycodes and symbols sections with their respective headers. */
|
|
||||||
gchar *keycodes = g_strdup(keymap_keycodes_header);
|
|
||||||
gchar *symbols = g_strdup(keymap_symbols_header);
|
|
||||||
|
|
||||||
/* Iterate over the keys in the name-to-key hash table. */
|
|
||||||
GHashTableIter iter;
|
|
||||||
gchar *button_name;
|
|
||||||
gpointer button_ptr;
|
|
||||||
g_hash_table_iter_init(&iter, keyboard->names);
|
|
||||||
|
|
||||||
while (g_hash_table_iter_next(&iter, (gpointer)&button_name, &button_ptr)) {
|
|
||||||
|
|
||||||
gchar *current, *line;
|
|
||||||
struct squeek_button *button = button_ptr;
|
|
||||||
struct squeek_key *key = squeek_button_get_key(button);
|
|
||||||
guint keycode = squeek_key_get_keycode(key);
|
|
||||||
|
|
||||||
/* Don't include invalid keycodes in the keymap. */
|
|
||||||
if (keycode == EEK_INVALID_KEYCODE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Append a key name-to-keycode definition to the keycodes section. */
|
|
||||||
current = keycodes;
|
|
||||||
line = g_strdup_printf(" <%s> = %i;\n", (char *)button_name, keycode);
|
|
||||||
|
|
||||||
keycodes = g_strconcat(current, line, NULL);
|
|
||||||
g_free(line);
|
|
||||||
g_free(current);
|
|
||||||
|
|
||||||
// FIXME: free
|
|
||||||
const char *key_str = squeek_key_to_keymap_entry(
|
|
||||||
(char*)button_name,
|
|
||||||
key
|
|
||||||
);
|
|
||||||
current = symbols;
|
|
||||||
symbols = g_strconcat(current, key_str, NULL);
|
|
||||||
g_free(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assemble the keymap file from the header, sections and footer. */
|
|
||||||
gchar *keymap = g_strconcat(keymap_header,
|
|
||||||
keycodes, " };\n\n",
|
|
||||||
symbols, " };\n\n",
|
|
||||||
keymap_footer, NULL);
|
|
||||||
|
|
||||||
g_free(keycodes);
|
|
||||||
g_free(symbols);
|
|
||||||
return keymap;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard)
|
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard)
|
||||||
{
|
{
|
||||||
return keyboard->views[keyboard->level];
|
return squeek_layout_get_current_view(keyboard->layout);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,24 +36,19 @@ G_BEGIN_DECLS
|
|||||||
struct _EekModifierKey {
|
struct _EekModifierKey {
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
EekModifierType modifiers;
|
EekModifierType modifiers;
|
||||||
struct squeek_button *button;
|
struct squeek_key *key;
|
||||||
};
|
};
|
||||||
typedef struct _EekModifierKey EekModifierKey;
|
typedef struct _EekModifierKey EekModifierKey;
|
||||||
|
|
||||||
/// Keyboard state holder
|
/// Keyboard state holder
|
||||||
struct _LevelKeyboard {
|
struct _LevelKeyboard {
|
||||||
struct squeek_view *views[4];
|
struct squeek_layout *layout;
|
||||||
guint level;
|
|
||||||
struct xkb_keymap *keymap;
|
struct xkb_keymap *keymap;
|
||||||
int keymap_fd; // keymap formatted as XKB string
|
int keymap_fd; // keymap formatted as XKB string
|
||||||
size_t keymap_len; // length of the data inside keymap_fd
|
size_t keymap_len; // length of the data inside keymap_fd
|
||||||
GArray *outline_array;
|
|
||||||
|
|
||||||
GList *pressed_buttons; // struct squeek_button*
|
GList *pressed_keys; // struct squeek_key*
|
||||||
GList *locked_buttons; // struct squeek_button*
|
GList *locked_keys; // struct EekModifierKey*
|
||||||
|
|
||||||
/* Map button names to button objects: */
|
|
||||||
GHashTable *names;
|
|
||||||
|
|
||||||
guint id; // as a key to layout choices
|
guint id; // as a key to layout choices
|
||||||
|
|
||||||
@ -61,31 +56,25 @@ struct _LevelKeyboard {
|
|||||||
};
|
};
|
||||||
typedef struct _LevelKeyboard LevelKeyboard;
|
typedef struct _LevelKeyboard LevelKeyboard;
|
||||||
|
|
||||||
struct squeek_button *eek_keyboard_find_button_by_name(LevelKeyboard *keyboard,
|
|
||||||
const gchar *name);
|
|
||||||
|
|
||||||
/// Represents the path to the button within a view
|
/// Represents the path to the button within a view
|
||||||
struct button_place {
|
struct button_place {
|
||||||
const struct squeek_row *row;
|
const struct squeek_row *row;
|
||||||
const struct squeek_button *button;
|
const struct squeek_button *button;
|
||||||
};
|
};
|
||||||
|
|
||||||
EekOutline *level_keyboard_get_outline
|
|
||||||
(LevelKeyboard *keyboard,
|
|
||||||
guint oref);
|
|
||||||
EekModifierKey *eek_modifier_key_copy
|
EekModifierKey *eek_modifier_key_copy
|
||||||
(EekModifierKey *modkey);
|
(EekModifierKey *modkey);
|
||||||
void eek_modifier_key_free
|
void eek_modifier_key_free
|
||||||
(EekModifierKey *modkey);
|
(EekModifierKey *modkey);
|
||||||
|
|
||||||
void eek_keyboard_press_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp);
|
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
|
||||||
void eek_keyboard_release_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp);
|
void eek_keyboard_release_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
|
||||||
|
|
||||||
gchar * eek_keyboard_get_keymap
|
gchar * eek_keyboard_get_keymap
|
||||||
(LevelKeyboard *keyboard);
|
(LevelKeyboard *keyboard);
|
||||||
|
|
||||||
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard);
|
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard);
|
||||||
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_view *views[], GHashTable *name_button_hash);
|
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout);
|
||||||
void level_keyboard_deinit(LevelKeyboard *self);
|
void level_keyboard_deinit(LevelKeyboard *self);
|
||||||
void level_keyboard_free(LevelKeyboard *self);
|
void level_keyboard_free(LevelKeyboard *self);
|
||||||
|
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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-keysym
|
|
||||||
* @short_description: an #EekSymbol represents an X keysym
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "eek-keysym.h"
|
|
||||||
|
|
||||||
/* modifier keys */
|
|
||||||
#define EEK_KEYSYM_Shift_L 0xffe1
|
|
||||||
#define EEK_KEYSYM_Shift_R 0xffe2
|
|
||||||
#define EEK_KEYSYM_ISO_Level3_Shift 0xfe03
|
|
||||||
#define EEK_KEYSYM_Caps_Lock 0xffe5
|
|
||||||
#define EEK_KEYSYM_Shift_Lock 0xffe6
|
|
||||||
#define EEK_KEYSYM_Control_L 0xffe3
|
|
||||||
#define EEK_KEYSYM_Control_R 0xffe4
|
|
||||||
#define EEK_KEYSYM_Alt_L 0xffe9
|
|
||||||
#define EEK_KEYSYM_Alt_R 0xffea
|
|
||||||
#define EEK_KEYSYM_Meta_L 0xffe7
|
|
||||||
#define EEK_KEYSYM_Meta_R 0xffe8
|
|
||||||
#define EEK_KEYSYM_Super_L 0xffeb
|
|
||||||
#define EEK_KEYSYM_Super_R 0xffec
|
|
||||||
#define EEK_KEYSYM_Hyper_L 0xffed
|
|
||||||
#define EEK_KEYSYM_Hyper_R 0xffee
|
|
||||||
|
|
||||||
struct _EekKeysymEntry {
|
|
||||||
guint xkeysym;
|
|
||||||
const gchar *name;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _EekKeysymEntry EekKeysymEntry;
|
|
||||||
|
|
||||||
#include "eek-xkeysym-keysym-entries.h"
|
|
||||||
|
|
||||||
guint32
|
|
||||||
eek_keysym_from_name (const gchar *name)
|
|
||||||
{
|
|
||||||
for (uint i = 0; i < G_N_ELEMENTS(xkeysym_keysym_entries); i++) {
|
|
||||||
if (g_strcmp0 (xkeysym_keysym_entries[i].name, name) == 0) {
|
|
||||||
return xkeysym_keysym_entries[i].xkeysym;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_KEYSYM_H
|
|
||||||
#define EEK_KEYSYM_H 1
|
|
||||||
|
|
||||||
#include "glib.h"
|
|
||||||
|
|
||||||
guint32 eek_keysym_from_name (const gchar *name);
|
|
||||||
|
|
||||||
#endif /* EEK_KEYSYM_H */
|
|
||||||
@ -45,9 +45,3 @@ void
|
|||||||
eek_layout_init (EekLayout *self)
|
eek_layout_init (EekLayout *self)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
eek_layout_update_layout(LevelKeyboard *keyboard)
|
|
||||||
{
|
|
||||||
squeek_view_place_contents(level_keyboard_current(keyboard), keyboard);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -56,14 +56,5 @@ struct _EekLayoutClass
|
|||||||
|
|
||||||
GType eek_layout_get_type (void) G_GNUC_CONST;
|
GType eek_layout_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
void eek_layout_place_rows(LevelKeyboard *keyboard, struct squeek_view *level);
|
|
||||||
|
|
||||||
void eek_layout_update_layout(LevelKeyboard *keyboard);
|
|
||||||
|
|
||||||
|
|
||||||
LevelKeyboard *
|
|
||||||
level_keyboard_from_layout (EekLayout *layout,
|
|
||||||
gdouble initial_width,
|
|
||||||
gdouble initial_height);
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
#endif /* EEK_LAYOUT_H */
|
#endif /* EEK_LAYOUT_H */
|
||||||
|
|||||||
@ -24,8 +24,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||||
|
|
||||||
#include "src/symbol.h"
|
|
||||||
|
|
||||||
#include "eek-renderer.h"
|
#include "eek-renderer.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -131,8 +129,7 @@ create_keyboard_surface_row_callback (struct squeek_row *row,
|
|||||||
cairo_rotate (data->cr, angle * G_PI / 180);
|
cairo_rotate (data->cr, angle * G_PI / 180);
|
||||||
|
|
||||||
data->row = row;
|
data->row = row;
|
||||||
squeek_row_foreach(row, create_keyboard_surface_button_callback,
|
squeek_row_foreach(row, create_keyboard_surface_button_callback, data);
|
||||||
data);
|
|
||||||
|
|
||||||
cairo_restore (data->cr);
|
cairo_restore (data->cr);
|
||||||
}
|
}
|
||||||
@ -175,8 +172,8 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
|
|||||||
|
|
||||||
/* draw rows */
|
/* draw rows */
|
||||||
squeek_view_foreach(level_keyboard_current(priv->keyboard),
|
squeek_view_foreach(level_keyboard_current(priv->keyboard),
|
||||||
create_keyboard_surface_row_callback,
|
create_keyboard_surface_row_callback,
|
||||||
&data);
|
&data);
|
||||||
cairo_restore (data.cr);
|
cairo_restore (data.cr);
|
||||||
|
|
||||||
cairo_destroy (data.cr);
|
cairo_destroy (data.cr);
|
||||||
@ -189,14 +186,20 @@ render_button_outline (EekRenderer *renderer,
|
|||||||
gboolean active)
|
gboolean active)
|
||||||
{
|
{
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
||||||
EekOutline *outline;
|
|
||||||
|
|
||||||
guint oref = squeek_button_get_oref(button);
|
|
||||||
outline = level_keyboard_get_outline (priv->keyboard, oref);
|
|
||||||
if (outline == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
EekBounds bounds = squeek_button_get_bounds(button);
|
EekBounds bounds = squeek_button_get_bounds(button);
|
||||||
|
|
||||||
|
/* Set the name of the button on the widget path, using the name obtained
|
||||||
|
from the button's symbol. */
|
||||||
|
g_autoptr (GtkWidgetPath) path = NULL;
|
||||||
|
path = gtk_widget_path_copy (gtk_style_context_get_path (priv->key_context));
|
||||||
|
const char *name = squeek_button_get_name(button);
|
||||||
|
gtk_widget_path_iter_set_name (path, -1, name);
|
||||||
|
|
||||||
|
/* Update the style context with the updated widget path. */
|
||||||
|
gtk_style_context_set_path (priv->key_context, path);
|
||||||
|
|
||||||
|
/* Set the state to take into account whether the button is active
|
||||||
|
(pressed) or normal. */
|
||||||
gtk_style_context_set_state(priv->key_context,
|
gtk_style_context_set_state(priv->key_context,
|
||||||
active ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
|
active ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
|
||||||
|
|
||||||
@ -215,18 +218,12 @@ render_button (EekRenderer *self,
|
|||||||
gboolean active)
|
gboolean active)
|
||||||
{
|
{
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
||||||
EekOutline *outline;
|
|
||||||
cairo_surface_t *outline_surface;
|
cairo_surface_t *outline_surface;
|
||||||
GHashTable *outline_surface_cache;
|
GHashTable *outline_surface_cache;
|
||||||
PangoLayout *layout;
|
PangoLayout *layout;
|
||||||
PangoRectangle extents = { 0, };
|
PangoRectangle extents = { 0, };
|
||||||
EekColor foreground;
|
EekColor foreground;
|
||||||
|
|
||||||
guint oref = squeek_button_get_oref (place->button);
|
|
||||||
outline = level_keyboard_get_outline (priv->keyboard, oref);
|
|
||||||
if (outline == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* render outline */
|
/* render outline */
|
||||||
EekBounds bounds = squeek_button_get_bounds(place->button);
|
EekBounds bounds = squeek_button_get_bounds(place->button);
|
||||||
|
|
||||||
@ -235,7 +232,7 @@ render_button (EekRenderer *self,
|
|||||||
else
|
else
|
||||||
outline_surface_cache = priv->outline_surface_cache;
|
outline_surface_cache = priv->outline_surface_cache;
|
||||||
|
|
||||||
outline_surface = g_hash_table_lookup (outline_surface_cache, outline);
|
outline_surface = g_hash_table_lookup (outline_surface_cache, place->button);
|
||||||
if (!outline_surface) {
|
if (!outline_surface) {
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
|
|
||||||
@ -259,7 +256,7 @@ render_button (EekRenderer *self,
|
|||||||
cairo_destroy (cr);
|
cairo_destroy (cr);
|
||||||
|
|
||||||
g_hash_table_insert (outline_surface_cache,
|
g_hash_table_insert (outline_surface_cache,
|
||||||
outline,
|
(gpointer)place->button,
|
||||||
outline_surface);
|
outline_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,16 +265,12 @@ render_button (EekRenderer *self,
|
|||||||
|
|
||||||
eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
|
eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
|
||||||
/* render icon (if any) */
|
/* render icon (if any) */
|
||||||
struct squeek_symbol *symbol = squeek_button_get_symbol(place->button);
|
const char *icon_name = squeek_button_get_icon_name(place->button);
|
||||||
if (!symbol)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (squeek_symbol_get_icon_name (symbol)) {
|
if (icon_name) {
|
||||||
gint scale = priv->scale_factor;
|
gint scale = priv->scale_factor;
|
||||||
cairo_surface_t *icon_surface =
|
cairo_surface_t *icon_surface =
|
||||||
eek_renderer_get_icon_surface (self,
|
eek_renderer_get_icon_surface (self, icon_name, 16 / priv->scale,
|
||||||
squeek_symbol_get_icon_name (symbol),
|
|
||||||
16 / priv->scale,
|
|
||||||
scale);
|
scale);
|
||||||
if (icon_surface) {
|
if (icon_surface) {
|
||||||
gint width = cairo_image_surface_get_width (icon_surface);
|
gint width = cairo_image_surface_get_width (icon_surface);
|
||||||
@ -372,18 +365,16 @@ eek_renderer_real_render_button_label (EekRenderer *self,
|
|||||||
{
|
{
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
||||||
|
|
||||||
const gchar *label;
|
const gchar *label = squeek_button_get_label(button);
|
||||||
|
|
||||||
|
if (!label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PangoFontDescription *font;
|
PangoFontDescription *font;
|
||||||
PangoLayoutLine *line;
|
PangoLayoutLine *line;
|
||||||
gdouble scale;
|
gdouble scale;
|
||||||
|
|
||||||
struct squeek_symbol *symbol = squeek_button_get_symbol(button);
|
|
||||||
if (!symbol)
|
|
||||||
return;
|
|
||||||
|
|
||||||
label = squeek_symbol_get_label (symbol);
|
|
||||||
if (!label)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!priv->font) {
|
if (!priv->font) {
|
||||||
const PangoFontDescription *base_font;
|
const PangoFontDescription *base_font;
|
||||||
@ -481,7 +472,7 @@ eek_renderer_real_render_keyboard (EekRenderer *self,
|
|||||||
cairo_get_target (cr), 0, 0,
|
cairo_get_target (cr), 0, 0,
|
||||||
priv->allocation_width, priv->allocation_height);
|
priv->allocation_width, priv->allocation_height);
|
||||||
|
|
||||||
render_keyboard_surface (self, priv->keyboard->views[priv->keyboard->level]);
|
render_keyboard_surface (self, squeek_layout_get_current_view(priv->keyboard->layout));
|
||||||
|
|
||||||
cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
|
cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
|
||||||
source = cairo_get_source (cr);
|
source = cairo_get_source (cr);
|
||||||
@ -913,14 +904,6 @@ eek_renderer_get_foreground_color (EekRenderer *renderer,
|
|||||||
color->alpha = gcolor.alpha;
|
color->alpha = gcolor.alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _FindKeyByPositionCallbackData {
|
|
||||||
EekPoint point;
|
|
||||||
EekPoint origin;
|
|
||||||
gint angle;
|
|
||||||
struct squeek_button *button;
|
|
||||||
};
|
|
||||||
typedef struct _FindKeyByPositionCallbackData FindKeyByPositionCallbackData;
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
|
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
|
||||||
{
|
{
|
||||||
@ -968,17 +951,6 @@ eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
find_button_by_position_row_callback (struct squeek_row *row,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
FindKeyByPositionCallbackData *data = user_data;
|
|
||||||
if (data->button) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
data->button = squeek_row_find_button_by_position(row, data->point, data->origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eek_renderer_find_key_by_position:
|
* eek_renderer_find_key_by_position:
|
||||||
* @renderer: The renderer normally used to render the key
|
* @renderer: The renderer normally used to render the key
|
||||||
@ -990,34 +962,18 @@ find_button_by_position_row_callback (struct squeek_row *row,
|
|||||||
**/
|
**/
|
||||||
struct squeek_button *
|
struct squeek_button *
|
||||||
eek_renderer_find_button_by_position (EekRenderer *renderer,
|
eek_renderer_find_button_by_position (EekRenderer *renderer,
|
||||||
struct squeek_view *view,
|
struct squeek_view *view,
|
||||||
gdouble x,
|
gdouble x,
|
||||||
gdouble y)
|
gdouble y)
|
||||||
{
|
{
|
||||||
FindKeyByPositionCallbackData data;
|
|
||||||
|
|
||||||
g_return_val_if_fail (EEK_IS_RENDERER(renderer), NULL);
|
g_return_val_if_fail (EEK_IS_RENDERER(renderer), NULL);
|
||||||
|
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
||||||
EekBounds bounds = squeek_view_get_bounds (view);
|
|
||||||
|
|
||||||
/* Transform from widget coordinates to keyboard coordinates */
|
/* Transform from widget coordinates to keyboard coordinates */
|
||||||
x = (x - priv->origin_x)/priv->scale - bounds.x;
|
EekPoint point = {
|
||||||
y = (y - priv->origin_y)/priv->scale - bounds.y;
|
.x = (x - priv->origin_x)/priv->scale,
|
||||||
|
.y = (y - priv->origin_y)/priv->scale,
|
||||||
if (x < 0 ||
|
};
|
||||||
y < 0 ||
|
return squeek_view_find_button_by_position(view, point);
|
||||||
x > bounds.width ||
|
|
||||||
y > bounds.height)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
data.point.x = x;
|
|
||||||
data.point.y = y;
|
|
||||||
data.origin.x = 0;
|
|
||||||
data.origin.y = 0;
|
|
||||||
data.button = NULL;
|
|
||||||
|
|
||||||
squeek_view_foreach (view, find_button_by_position_row_callback,
|
|
||||||
&data);
|
|
||||||
return data.button;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "eek-section.h"
|
|
||||||
|
|
||||||
EekBounds eek_get_outline_size(LevelKeyboard *keyboard, uint32_t oref) {
|
|
||||||
EekOutline *outline = level_keyboard_get_outline (keyboard, oref);
|
|
||||||
if (outline && outline->num_points > 0) {
|
|
||||||
double minx = outline->points[0].x;
|
|
||||||
double maxx = minx;
|
|
||||||
double miny = outline->points[0].y;
|
|
||||||
double maxy = miny;
|
|
||||||
for (uint i = 1; i < outline->num_points; i++) {
|
|
||||||
EekPoint p = outline->points[i];
|
|
||||||
if (p.x < minx) {
|
|
||||||
minx = p.x;
|
|
||||||
} else if (p.x > maxx) {
|
|
||||||
maxx = p.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.y < miny) {
|
|
||||||
miny = p.y;
|
|
||||||
} else if (p.y > maxy) {
|
|
||||||
maxy = p.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EekBounds key_bounds = {
|
|
||||||
.height = maxy - miny,
|
|
||||||
.width = maxx - minx,
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
};
|
|
||||||
return key_bounds;
|
|
||||||
}
|
|
||||||
EekBounds bounds = {0, 0, 0, 0};
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_SECTION_H
|
|
||||||
#define EEK_SECTION_H 1
|
|
||||||
|
|
||||||
/* Contains row-related functions that couldn't be done in Rust easily. */
|
|
||||||
|
|
||||||
#include <glib-object.h>
|
|
||||||
#include "eek-keyboard.h"
|
|
||||||
#include "src/layout.h"
|
|
||||||
|
|
||||||
#endif /* EEK_SECTION_H */
|
|
||||||
1399
eek/eek-xml-layout.c
1399
eek/eek-xml-layout.c
File diff suppressed because it is too large
Load Diff
@ -23,47 +23,11 @@
|
|||||||
#ifndef EEK_XML_LAYOUT_H
|
#ifndef EEK_XML_LAYOUT_H
|
||||||
#define EEK_XML_LAYOUT_H 1
|
#define EEK_XML_LAYOUT_H 1
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include "eek-types.h"
|
||||||
#include "eek-layout.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define EEK_TYPE_XML_LAYOUT (eek_xml_layout_get_type())
|
|
||||||
G_DECLARE_DERIVABLE_TYPE (EekXmlLayout, eek_xml_layout, EEK, XML_LAYOUT, EekLayout)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EekXmlLayoutClass:
|
|
||||||
*/
|
|
||||||
struct _EekXmlLayoutClass
|
|
||||||
{
|
|
||||||
/*< private >*/
|
|
||||||
EekLayoutClass parent_class;
|
|
||||||
|
|
||||||
/* padding */
|
|
||||||
gpointer pdummy[24];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _EekXmlKeyboardDesc
|
|
||||||
{
|
|
||||||
gchar *id;
|
|
||||||
gchar *name;
|
|
||||||
gchar *geometry;
|
|
||||||
gchar *symbols;
|
|
||||||
gchar *language;
|
|
||||||
gchar *longname;
|
|
||||||
};
|
|
||||||
typedef struct _EekXmlKeyboardDesc EekXmlKeyboardDesc;
|
|
||||||
|
|
||||||
GType eek_xml_layout_get_type (void) G_GNUC_CONST;
|
|
||||||
EekLayout *eek_xml_layout_new (const gchar *id,
|
|
||||||
GError **error);
|
|
||||||
GList *eek_xml_list_keyboards (void);
|
|
||||||
|
|
||||||
EekXmlKeyboardDesc *eek_xml_keyboard_desc_copy (EekXmlKeyboardDesc *desc);
|
|
||||||
void eek_xml_keyboard_desc_free (EekXmlKeyboardDesc *desc);
|
|
||||||
|
|
||||||
LevelKeyboard *
|
LevelKeyboard *
|
||||||
eek_xml_layout_real_create_keyboard (EekLayout *self,
|
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
|
||||||
EekboardContextService *manager);
|
EekboardContextService *manager);
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
#endif /* EEK_XML_LAYOUT_H */
|
#endif /* EEK_XML_LAYOUT_H */
|
||||||
|
|||||||
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
#include "eek-layout.h"
|
#include "eek-layout.h"
|
||||||
#include "eek-keysym.h"
|
|
||||||
|
|
||||||
void eek_init (void);
|
void eek_init (void);
|
||||||
|
|
||||||
|
|||||||
@ -1,50 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
|
|
||||||
# Copyright (C) 2010-2011 Red Hat, Inc.
|
|
||||||
# Copyright (C) 2019 Purism, SPC
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
|
|
||||||
if len(sys.argv) > 3:
|
|
||||||
print("Usage: %s TABLE-NAME [INPUT_FILE]" % sys.argv[0], file=sys.stderr)
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
if len(sys.argv) < 3:
|
|
||||||
in_stream = sys.stdin
|
|
||||||
else:
|
|
||||||
in_stream = open(sys.argv[2])
|
|
||||||
|
|
||||||
table = dict()
|
|
||||||
for line in in_stream:
|
|
||||||
match = re.match(r'\s*(0x[0-9A-F]+)\s+"(.*)"\s+(\S*)', line, re.I)
|
|
||||||
if match:
|
|
||||||
table[int(match.group(1), 16)] = (match.group(2), match.group(3))
|
|
||||||
|
|
||||||
sys.stdout.write("static const EekKeysymEntry %s[] = {\n" %
|
|
||||||
sys.argv[1])
|
|
||||||
|
|
||||||
for index, (keysym, (l, c)) in enumerate([(keysym, table[keysym])
|
|
||||||
for keysym in sorted(table.keys())]):
|
|
||||||
sys.stdout.write(" { 0x%X, \"%s\" }" %
|
|
||||||
(keysym, l))
|
|
||||||
if index < len(table) - 1:
|
|
||||||
sys.stdout.write(",")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
sys.stdout.write("};\n")
|
|
||||||
31
eek/keymap.h
31
eek/keymap.h
@ -6,34 +6,3 @@ squeek_keymap_get_entries_for_keyval (struct xkb_keymap *xkb_keymap,
|
|||||||
guint keyval,
|
guint keyval,
|
||||||
GdkKeymapKey **keys,
|
GdkKeymapKey **keys,
|
||||||
guint *n_keys);
|
guint *n_keys);
|
||||||
|
|
||||||
static const char *keymap_header = "xkb_keymap {\n\
|
|
||||||
\n";
|
|
||||||
|
|
||||||
static const char *keymap_keycodes_header = "\
|
|
||||||
xkb_keycodes \"squeekboard\" {\n\n\
|
|
||||||
minimum = 8;\n\
|
|
||||||
maximum = 255;\n\
|
|
||||||
\n";
|
|
||||||
|
|
||||||
static const char *keymap_symbols_header = "\
|
|
||||||
xkb_symbols \"squeekboard\" {\n\
|
|
||||||
\n\
|
|
||||||
name[Group1] = \"Letters\";\n\
|
|
||||||
name[Group2] = \"Numbers/Symbols\";\n\
|
|
||||||
\n";
|
|
||||||
|
|
||||||
static const char *keymap_footer = "\
|
|
||||||
xkb_types \"squeekboard\" {\n\
|
|
||||||
\n\
|
|
||||||
type \"TWO_LEVEL\" {\n\
|
|
||||||
modifiers = Shift;\n\
|
|
||||||
map[Shift] = Level2;\n\
|
|
||||||
level_name[Level1] = \"Base\";\n\
|
|
||||||
level_name[Level2] = \"Shift\";\n\
|
|
||||||
};\n\
|
|
||||||
};\n\
|
|
||||||
\n\
|
|
||||||
xkb_compatibility \"squeekboard\" {\n\
|
|
||||||
};\n\
|
|
||||||
};";
|
|
||||||
|
|||||||
@ -6,15 +6,3 @@ enum_headers = [
|
|||||||
|
|
||||||
enums = gnome.mkenums_simple('eek-enumtypes', sources: enum_headers)
|
enums = gnome.mkenums_simple('eek-enumtypes', sources: enum_headers)
|
||||||
|
|
||||||
python = find_program('python3')
|
|
||||||
|
|
||||||
gen_keysym_entries_xkeysym = generator(
|
|
||||||
python,
|
|
||||||
arguments: ['@CURRENT_SOURCE_DIR@/gen-keysym-entries.py', 'xkeysym_keysym_entries', '@INPUT@'],
|
|
||||||
capture: true,
|
|
||||||
output: 'eek-@BASENAME@.h',
|
|
||||||
)
|
|
||||||
|
|
||||||
keysym_entries = [
|
|
||||||
gen_keysym_entries_xkeysym.process('./xkeysym-keysym-entries.txt'),
|
|
||||||
]
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#define _XOPEN_SOURCE 500
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/random.h> // TODO: this is Linux-specific
|
#include <sys/random.h> // TODO: this is Linux-specific
|
||||||
@ -89,66 +90,17 @@ static LevelKeyboard *
|
|||||||
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
||||||
const gchar *keyboard_type)
|
const gchar *keyboard_type)
|
||||||
{
|
{
|
||||||
EekLayout *layout;
|
LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self);
|
||||||
GError *error;
|
|
||||||
|
|
||||||
if (g_str_has_prefix (keyboard_type, "xkb:")) {
|
|
||||||
/* TODO: Depends on xklavier
|
|
||||||
XklConfigRec *rec =
|
|
||||||
eekboard_xkl_config_rec_from_string (&keyboard_type[4]);
|
|
||||||
|
|
||||||
if (display == NULL)
|
|
||||||
//display = XOpenDisplay (NULL);
|
|
||||||
return NULL; // FIXME: replace with wl display
|
|
||||||
|
|
||||||
error = NULL;
|
|
||||||
layout = eek_xkl_layout_new (display, &error);
|
|
||||||
if (layout == NULL) {
|
|
||||||
g_warning ("can't create keyboard %s: %s",
|
|
||||||
keyboard_type, error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!eek_xkl_layout_set_config (EEK_XKL_LAYOUT(layout), rec)) {
|
|
||||||
g_object_unref (layout);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
error = NULL;
|
|
||||||
layout = eek_xml_layout_new (keyboard_type, &error);
|
|
||||||
if (layout == NULL) {
|
|
||||||
g_warning ("can't create keyboard %s: %s",
|
|
||||||
keyboard_type, error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
keyboard_type = "us";
|
|
||||||
error = NULL;
|
|
||||||
layout = eek_xml_layout_new (keyboard_type, &error);
|
|
||||||
if (layout == NULL) {
|
|
||||||
g_error ("failed to create fallback layout: %s", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(layout, self);
|
|
||||||
if (!keyboard) {
|
if (!keyboard) {
|
||||||
g_error("Failed to create a keyboard");
|
g_error("Failed to create a keyboard");
|
||||||
}
|
}
|
||||||
g_object_unref (layout);
|
|
||||||
|
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
g_error("No context created");
|
g_error("No context created");
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar *keymap_str = eek_keyboard_get_keymap(keyboard);
|
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
|
||||||
|
|
||||||
int f = open("maprs.map", O_CREAT | O_WRONLY, 0600);
|
|
||||||
write(f, keymap_str, strlen(keymap_str));
|
|
||||||
close(f);
|
|
||||||
|
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
|
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
@ -156,8 +108,6 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
|||||||
if (!keymap)
|
if (!keymap)
|
||||||
g_error("Bad keymap:\n%s", keymap_str);
|
g_error("Bad keymap:\n%s", keymap_str);
|
||||||
|
|
||||||
free(keymap_str);
|
|
||||||
|
|
||||||
xkb_context_unref(context);
|
xkb_context_unref(context);
|
||||||
keyboard->keymap = keymap;
|
keyboard->keymap = keymap;
|
||||||
|
|
||||||
@ -167,7 +117,7 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
|||||||
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
|
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
|
||||||
char *r = &path[strlen(path) - 6];
|
char *r = &path[strlen(path) - 6];
|
||||||
getrandom(r, 6, GRND_NONBLOCK);
|
getrandom(r, 6, GRND_NONBLOCK);
|
||||||
for (uint i = 0; i < 6; i++) {
|
for (unsigned i = 0; i < 6; i++) {
|
||||||
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
|
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
|
||||||
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
|
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
|
||||||
}
|
}
|
||||||
@ -185,9 +135,8 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
|||||||
if ((void*)ptr == (void*)-1) {
|
if ((void*)ptr == (void*)-1) {
|
||||||
g_error("Failed to set up mmap");
|
g_error("Failed to set up mmap");
|
||||||
}
|
}
|
||||||
strcpy(ptr, keymap_str);
|
strncpy(ptr, keymap_str, keyboard->keymap_len);
|
||||||
munmap(ptr, keyboard->keymap_len);
|
munmap(ptr, keyboard->keymap_len);
|
||||||
free(keymap_str);
|
|
||||||
|
|
||||||
return keyboard;
|
return keyboard;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,12 +90,9 @@ send_fake_key (SeatEmitter *emitter,
|
|||||||
gboolean pressed,
|
gboolean pressed,
|
||||||
uint32_t timestamp)
|
uint32_t timestamp)
|
||||||
{
|
{
|
||||||
guint level = keyboard->level;
|
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
|
||||||
uint32_t group = (level / 2);
|
|
||||||
|
|
||||||
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
|
|
||||||
send_virtual_keyboard_key (emitter->virtual_keyboard, keycode - 8, (unsigned)pressed, timestamp);
|
send_virtual_keyboard_key (emitter->virtual_keyboard, keycode - 8, (unsigned)pressed, timestamp);
|
||||||
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
|
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
29
examples/test_layout.rs
Normal file
29
examples/test_layout.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
extern crate rs;
|
||||||
|
extern crate xkbcommon;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use rs::data::{ load_layout_from_resource, LoadError };
|
||||||
|
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
|
||||||
|
fn check_layout(name: &str) {
|
||||||
|
let layout = load_layout_from_resource(name)
|
||||||
|
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
||||||
|
.expect("layout broken");
|
||||||
|
|
||||||
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
|
xkb::Keymap::new_from_string(
|
||||||
|
&context,
|
||||||
|
layout.keymap_str
|
||||||
|
.clone()
|
||||||
|
.into_string().expect("Failed to decode keymap string"),
|
||||||
|
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||||
|
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||||
|
).expect("Failed to create keymap");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> () {
|
||||||
|
check_layout(env::args().nth(1).expect("No argument given").as_str());
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
project(
|
project(
|
||||||
'squeekboard',
|
'squeekboard',
|
||||||
'c', 'rust',
|
'c', 'rust',
|
||||||
version: '1.0.10',
|
version: '1.2.0',
|
||||||
license: 'GPLv3',
|
license: 'GPLv3',
|
||||||
meson_version: '>=0.49.0',
|
meson_version: '>=0.51.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'warning_level=1',
|
'warning_level=1',
|
||||||
'buildtype=debugoptimized',
|
'buildtype=debugoptimized',
|
||||||
@ -53,6 +53,9 @@ summary = [
|
|||||||
]
|
]
|
||||||
message('\n'.join(summary))
|
message('\n'.join(summary))
|
||||||
|
|
||||||
|
cargo = find_program('cargo')
|
||||||
|
cargo_script = find_program('cargo.sh')
|
||||||
|
|
||||||
subdir('data')
|
subdir('data')
|
||||||
subdir('protocols')
|
subdir('protocols')
|
||||||
subdir('eek')
|
subdir('eek')
|
||||||
|
|||||||
1350
src/bitflags.rs
1350
src/bitflags.rs
File diff suppressed because it is too large
Load Diff
640
src/data.rs
Normal file
640
src/data.rs
Normal file
@ -0,0 +1,640 @@
|
|||||||
|
/**! The parsing of the data files for layouts */
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{ HashMap, HashSet };
|
||||||
|
use std::env;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
use ::keyboard::{
|
||||||
|
KeyState,
|
||||||
|
generate_keymap, generate_keycodes, FormattingError
|
||||||
|
};
|
||||||
|
use ::resources;
|
||||||
|
use ::util::c::as_str;
|
||||||
|
use ::xdg;
|
||||||
|
|
||||||
|
// traits, derives
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
||||||
|
/// Gathers stuff defined in C or called by C
|
||||||
|
pub mod c {
|
||||||
|
use super::*;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_load_layout(name: *const c_char) -> *mut ::layout::Layout {
|
||||||
|
let name = as_str(&name)
|
||||||
|
.expect("Bad layout name")
|
||||||
|
.expect("Empty layout name");
|
||||||
|
|
||||||
|
let layout = load_layout_with_fallback(name);
|
||||||
|
Box::into_raw(Box::new(layout))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FALLBACK_LAYOUT_NAME: &str = "us";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum LoadError {
|
||||||
|
BadData(Error),
|
||||||
|
MissingResource,
|
||||||
|
BadResource(serde_yaml::Error),
|
||||||
|
BadKeyMap(FormattingError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LoadError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use self::LoadError::*;
|
||||||
|
match self {
|
||||||
|
BadData(e) => write!(f, "Bad data: {}", e),
|
||||||
|
MissingResource => write!(f, "Missing resource"),
|
||||||
|
BadResource(e) => write!(f, "Bad resource: {}", e),
|
||||||
|
BadKeyMap(e) => write!(f, "Bad key map: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_layout_from_resource(
|
||||||
|
name: &str
|
||||||
|
) -> Result<Layout, LoadError> {
|
||||||
|
let data = resources::get_keyboard(name)
|
||||||
|
.ok_or(LoadError::MissingResource)?;
|
||||||
|
serde_yaml::from_str(data)
|
||||||
|
.map_err(|e| LoadError::BadResource(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DataSource {
|
||||||
|
File(PathBuf),
|
||||||
|
Resource(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DataSource {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DataSource::File(path) => write!(f, "Path: {:?}", path.display()),
|
||||||
|
DataSource::Resource(name) => write!(f, "Resource: {}", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to load the layout from the first place where it's present.
|
||||||
|
/// If the layout exists, but is broken, fallback is activated.
|
||||||
|
fn load_layout(
|
||||||
|
name: &str
|
||||||
|
) -> (Result<::layout::Layout, LoadError>, DataSource) {
|
||||||
|
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.or_else(|| xdg::data_path("squeekboard/keyboards"))
|
||||||
|
.map(|path| path.join(name).with_extension("yaml"));
|
||||||
|
|
||||||
|
let (layout, source) = match path {
|
||||||
|
Some(path) => {(
|
||||||
|
Layout::from_yaml_stream(path.clone())
|
||||||
|
.map_err(|e| LoadError::BadData(e)),
|
||||||
|
DataSource::File(path)
|
||||||
|
)},
|
||||||
|
None => {(
|
||||||
|
load_layout_from_resource(name),
|
||||||
|
DataSource::Resource(name.into())
|
||||||
|
)},
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout = layout.and_then(
|
||||||
|
|layout| layout.build().map_err(LoadError::BadKeyMap)
|
||||||
|
);
|
||||||
|
|
||||||
|
(layout, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_layout_with_fallback(
|
||||||
|
name: &str
|
||||||
|
) -> ::layout::Layout {
|
||||||
|
let (layout, source) = load_layout(name);
|
||||||
|
let (layout, source) = match (layout, source) {
|
||||||
|
(Err(e), source) => {
|
||||||
|
eprintln!(
|
||||||
|
"Failed to load layout from {}: {}, using fallback",
|
||||||
|
source, e
|
||||||
|
);
|
||||||
|
load_layout(FALLBACK_LAYOUT_NAME)
|
||||||
|
},
|
||||||
|
res => res,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (layout, source) = match (layout, source) {
|
||||||
|
(Err(e), source) => {
|
||||||
|
eprintln!(
|
||||||
|
"Failed to load fallback layout from {}: {}, using hardcoded",
|
||||||
|
source, e
|
||||||
|
);
|
||||||
|
(
|
||||||
|
load_layout_from_resource(FALLBACK_LAYOUT_NAME)
|
||||||
|
.and_then(
|
||||||
|
|layout| layout.build().map_err(LoadError::BadKeyMap)
|
||||||
|
),
|
||||||
|
DataSource::Resource(FALLBACK_LAYOUT_NAME.into()),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
res => res,
|
||||||
|
};
|
||||||
|
|
||||||
|
match (layout, source) {
|
||||||
|
(Err(e), source) => {
|
||||||
|
panic!(
|
||||||
|
format!("Failed to load hardcoded layout from {}: {:?}",
|
||||||
|
source, e
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(Ok(layout), source) => {
|
||||||
|
eprintln!("Loaded layout from {}", source);
|
||||||
|
layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The root element describing an entire keyboard
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Layout {
|
||||||
|
bounds: Bounds,
|
||||||
|
views: HashMap<String, Vec<ButtonIds>>,
|
||||||
|
#[serde(default)]
|
||||||
|
buttons: HashMap<String, ButtonMeta>,
|
||||||
|
outlines: HashMap<String, Outline>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
struct Bounds {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Buttons are embedded in a single string
|
||||||
|
type ButtonIds = String;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
struct ButtonMeta {
|
||||||
|
/// Action other than keysym (conflicts with keysym)
|
||||||
|
action: Option<Action>,
|
||||||
|
/// The name of the outline. If not present, will be "default"
|
||||||
|
outline: Option<String>,
|
||||||
|
/// FIXME: start using it
|
||||||
|
keysym: Option<String>,
|
||||||
|
/// If not present, will be derived from the button ID
|
||||||
|
label: Option<String>,
|
||||||
|
/// Conflicts with label
|
||||||
|
icon: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
enum Action {
|
||||||
|
#[serde(rename="locking")]
|
||||||
|
Locking { lock_view: String, unlock_view: String },
|
||||||
|
#[serde(rename="set_view")]
|
||||||
|
SetView(String),
|
||||||
|
#[serde(rename="show_prefs")]
|
||||||
|
ShowPrefs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
struct Outline {
|
||||||
|
corner_radius: f64,
|
||||||
|
bounds: Bounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Yaml(serde_yaml::Error),
|
||||||
|
Io(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Yaml(e) => write!(f, "YAML: {}", e),
|
||||||
|
Error::Io(e) => write!(f, "IO: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
fn from_yaml_stream(path: PathBuf) -> Result<Layout, Error> {
|
||||||
|
let infile = BufReader::new(
|
||||||
|
fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&path)
|
||||||
|
.map_err(Error::Io)?
|
||||||
|
);
|
||||||
|
serde_yaml::from_reader(infile)
|
||||||
|
.map_err(Error::Yaml)
|
||||||
|
}
|
||||||
|
pub fn build(self) -> Result<::layout::Layout, FormattingError> {
|
||||||
|
let button_names = self.views.values()
|
||||||
|
.flat_map(|rows| {
|
||||||
|
rows.iter()
|
||||||
|
.flat_map(|row| row.split_ascii_whitespace())
|
||||||
|
});
|
||||||
|
|
||||||
|
let button_names: HashSet<&str>
|
||||||
|
= HashSet::from_iter(button_names);
|
||||||
|
|
||||||
|
let keycodes = generate_keycodes(
|
||||||
|
button_names.iter()
|
||||||
|
.map(|name| *name)
|
||||||
|
.filter(|name| {
|
||||||
|
match self.buttons.get(*name) {
|
||||||
|
// buttons with defined action can't emit keysyms
|
||||||
|
// and so don't need keycodes
|
||||||
|
Some(ButtonMeta { action: Some(_), .. }) => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let button_states = button_names.iter().map(|name| {(
|
||||||
|
String::from(*name),
|
||||||
|
Rc::new(RefCell::new(KeyState {
|
||||||
|
pressed: false,
|
||||||
|
locked: false,
|
||||||
|
keycode: keycodes.get(*name).map(|k| *k),
|
||||||
|
symbol: create_symbol(
|
||||||
|
&self.buttons,
|
||||||
|
name,
|
||||||
|
self.views.keys().collect()
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
)});
|
||||||
|
|
||||||
|
let button_states =
|
||||||
|
HashMap::<String, Rc<RefCell<KeyState>>>::from_iter(
|
||||||
|
button_states
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: generate from symbols
|
||||||
|
let keymap_str = generate_keymap(&button_states)?;
|
||||||
|
|
||||||
|
let views = HashMap::from_iter(
|
||||||
|
self.views.iter().map(|(name, view)| {(
|
||||||
|
name.clone(),
|
||||||
|
Box::new(::layout::View {
|
||||||
|
bounds: ::layout::c::Bounds {
|
||||||
|
x: self.bounds.x,
|
||||||
|
y: self.bounds.y,
|
||||||
|
width: self.bounds.width,
|
||||||
|
height: self.bounds.height,
|
||||||
|
},
|
||||||
|
rows: view.iter().map(|row| {
|
||||||
|
Box::new(::layout::Row {
|
||||||
|
angle: 0,
|
||||||
|
bounds: None,
|
||||||
|
buttons: row.split_ascii_whitespace().map(|name| {
|
||||||
|
Box::new(create_button(
|
||||||
|
&self.buttons,
|
||||||
|
&self.outlines,
|
||||||
|
name,
|
||||||
|
button_states.get(name.into())
|
||||||
|
.expect("Button state not created")
|
||||||
|
.clone()
|
||||||
|
))
|
||||||
|
}).collect(),
|
||||||
|
})
|
||||||
|
}).collect(),
|
||||||
|
})
|
||||||
|
)})
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(::layout::Layout {
|
||||||
|
current_view: "base".into(),
|
||||||
|
views: views,
|
||||||
|
keymap_str: {
|
||||||
|
CString::new(keymap_str)
|
||||||
|
.expect("Invalid keymap string generated")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_symbol(
|
||||||
|
button_info: &HashMap<String, ButtonMeta>,
|
||||||
|
name: &str,
|
||||||
|
view_names: Vec<&String>,
|
||||||
|
) -> ::symbol::Symbol {
|
||||||
|
let default_meta = ButtonMeta::default();
|
||||||
|
let symbol_meta = button_info.get(name)
|
||||||
|
.unwrap_or(&default_meta);
|
||||||
|
|
||||||
|
fn filter_view_name(
|
||||||
|
button_name: &str,
|
||||||
|
view_name: String,
|
||||||
|
view_names: &Vec<&String>
|
||||||
|
) -> String {
|
||||||
|
if view_names.contains(&&view_name) {
|
||||||
|
view_name
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"Button {} switches to missing view {}",
|
||||||
|
button_name,
|
||||||
|
view_name
|
||||||
|
);
|
||||||
|
"base".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keysym_valid(name: &str) -> bool {
|
||||||
|
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
let keysym = match &symbol_meta.action {
|
||||||
|
Some(_) => None,
|
||||||
|
None => Some(match &symbol_meta.keysym {
|
||||||
|
Some(keysym) => match keysym_valid(keysym.as_str()) {
|
||||||
|
true => keysym.clone(),
|
||||||
|
false => {
|
||||||
|
eprintln!("Keysym name invalid: {}", keysym);
|
||||||
|
"space".into() // placeholder
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => match keysym_valid(name) {
|
||||||
|
true => String::from(name),
|
||||||
|
false => match name.chars().count() {
|
||||||
|
1 => format!("U{:04X}", name.chars().next().unwrap() as u32),
|
||||||
|
// If the name is longer than 1 char,
|
||||||
|
// then it's not a single Unicode char,
|
||||||
|
// but was trying to be an identifier
|
||||||
|
_ => {
|
||||||
|
eprintln!(
|
||||||
|
"Could not derive a valid keysym for key {}",
|
||||||
|
name
|
||||||
|
);
|
||||||
|
"space".into() // placeholder
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
match &symbol_meta.action {
|
||||||
|
Some(Action::SetView(view_name)) => ::symbol::Symbol {
|
||||||
|
action: ::symbol::Action::SetLevel(
|
||||||
|
filter_view_name(name, view_name.clone(), &view_names)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Some(Action::Locking { lock_view, unlock_view }) => ::symbol::Symbol {
|
||||||
|
action: ::symbol::Action::LockLevel {
|
||||||
|
lock: filter_view_name(name, lock_view.clone(), &view_names),
|
||||||
|
unlock: filter_view_name(
|
||||||
|
name,
|
||||||
|
unlock_view.clone(),
|
||||||
|
&view_names
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Some(Action::ShowPrefs) => ::symbol::Symbol {
|
||||||
|
action: ::symbol::Action::Submit {
|
||||||
|
text: None,
|
||||||
|
keys: Vec::new(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => ::symbol::Symbol {
|
||||||
|
action: ::symbol::Action::Submit {
|
||||||
|
text: None,
|
||||||
|
keys: vec!(
|
||||||
|
::symbol::KeySym(keysym.unwrap()),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: Since this will receive user-provided data,
|
||||||
|
/// all .expect() on them should be turned into soft fails
|
||||||
|
fn create_button(
|
||||||
|
button_info: &HashMap<String, ButtonMeta>,
|
||||||
|
outlines: &HashMap<String, Outline>,
|
||||||
|
name: &str,
|
||||||
|
state: Rc<RefCell<KeyState>>,
|
||||||
|
) -> ::layout::Button {
|
||||||
|
let cname = CString::new(name.clone())
|
||||||
|
.expect("Bad name");
|
||||||
|
// don't remove, because multiple buttons with the same name are allowed
|
||||||
|
let default_meta = ButtonMeta::default();
|
||||||
|
let button_meta = button_info.get(name)
|
||||||
|
.unwrap_or(&default_meta);
|
||||||
|
|
||||||
|
// TODO: move conversion to the C/Rust boundary
|
||||||
|
let label = if let Some(label) = &button_meta.label {
|
||||||
|
::layout::Label::Text(CString::new(label.as_str())
|
||||||
|
.expect("Bad label"))
|
||||||
|
} else if let Some(icon) = &button_meta.icon {
|
||||||
|
::layout::Label::IconName(CString::new(icon.as_str())
|
||||||
|
.expect("Bad icon"))
|
||||||
|
} else {
|
||||||
|
::layout::Label::Text(cname.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let outline_name = match &button_meta.outline {
|
||||||
|
Some(outline) => {
|
||||||
|
if outlines.contains_key(outline) {
|
||||||
|
outline.clone()
|
||||||
|
} else {
|
||||||
|
eprintln!("Outline named {} does not exist! Using default for button {}", outline, name);
|
||||||
|
"default".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => "default".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let outline = outlines.get(&outline_name)
|
||||||
|
.map(|outline| (*outline).clone())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
eprintln!("No default outline defied Using 1x1!");
|
||||||
|
Outline {
|
||||||
|
corner_radius: 0f64,
|
||||||
|
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
::layout::Button {
|
||||||
|
name: cname,
|
||||||
|
// TODO: do layout before creating buttons
|
||||||
|
bounds: ::layout::c::Bounds {
|
||||||
|
x: outline.bounds.x,
|
||||||
|
y: outline.bounds.y,
|
||||||
|
width: outline.bounds.width,
|
||||||
|
height: outline.bounds.height,
|
||||||
|
},
|
||||||
|
corner_radius: outline.corner_radius,
|
||||||
|
label: label,
|
||||||
|
state: state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::error::Error as ErrorTrait;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_path() {
|
||||||
|
assert_eq!(
|
||||||
|
Layout::from_yaml_stream(
|
||||||
|
PathBuf::from("tests/layout.yaml")
|
||||||
|
).unwrap(),
|
||||||
|
Layout {
|
||||||
|
bounds: Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 },
|
||||||
|
views: hashmap!(
|
||||||
|
"base".into() => vec!("test".into()),
|
||||||
|
),
|
||||||
|
buttons: hashmap!{
|
||||||
|
"test".into() => ButtonMeta {
|
||||||
|
icon: None,
|
||||||
|
keysym: None,
|
||||||
|
action: None,
|
||||||
|
label: Some("test".into()),
|
||||||
|
outline: None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlines: hashmap!{
|
||||||
|
"default".into() => Outline {
|
||||||
|
corner_radius: 1f64,
|
||||||
|
bounds: Bounds {
|
||||||
|
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the default protection works
|
||||||
|
#[test]
|
||||||
|
fn test_empty_views() {
|
||||||
|
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout2.yaml"));
|
||||||
|
match out {
|
||||||
|
Ok(_) => assert!(false, "Data mistakenly accepted"),
|
||||||
|
Err(e) => {
|
||||||
|
let mut handled = false;
|
||||||
|
if let Error::Yaml(ye) = &e {
|
||||||
|
handled = ye.description() == "missing field `views`";
|
||||||
|
};
|
||||||
|
if !handled {
|
||||||
|
println!("Unexpected error {:?}", e);
|
||||||
|
assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extra_field() {
|
||||||
|
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout3.yaml"));
|
||||||
|
match out {
|
||||||
|
Ok(_) => assert!(false, "Data mistakenly accepted"),
|
||||||
|
Err(e) => {
|
||||||
|
let mut handled = false;
|
||||||
|
if let Error::Yaml(ye) = &e {
|
||||||
|
handled = ye.description()
|
||||||
|
.starts_with("unknown field `bad_field`");
|
||||||
|
};
|
||||||
|
if !handled {
|
||||||
|
println!("Unexpected error {:?}", e);
|
||||||
|
assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_layout_punctuation() {
|
||||||
|
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout_key1.yaml"))
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
out.views["base"]
|
||||||
|
.rows[0]
|
||||||
|
.buttons[0]
|
||||||
|
.label,
|
||||||
|
::layout::Label::Text(CString::new("test").unwrap())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_layout_unicode() {
|
||||||
|
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout_key2.yaml"))
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
out.views["base"]
|
||||||
|
.rows[0]
|
||||||
|
.buttons[0]
|
||||||
|
.label,
|
||||||
|
::layout::Label::Text(CString::new("test").unwrap())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parsing_fallback() {
|
||||||
|
assert!(load_layout_from_resource(FALLBACK_LAYOUT_NAME)
|
||||||
|
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unicode_keysym() {
|
||||||
|
let keysym = xkb::keysym_from_name(
|
||||||
|
format!("U{:X}", "å".chars().next().unwrap() as u32).as_str(),
|
||||||
|
xkb::KEYSYM_NO_FLAGS,
|
||||||
|
);
|
||||||
|
let keysym = xkb::keysym_to_utf8(keysym);
|
||||||
|
assert_eq!(keysym, "å\0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_key_unicode() {
|
||||||
|
assert_eq!(
|
||||||
|
create_symbol(
|
||||||
|
&hashmap!{
|
||||||
|
".".into() => ButtonMeta {
|
||||||
|
icon: None,
|
||||||
|
keysym: None,
|
||||||
|
action: None,
|
||||||
|
label: Some("test".into()),
|
||||||
|
outline: None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
".",
|
||||||
|
Vec::new()
|
||||||
|
),
|
||||||
|
::symbol::Symbol {
|
||||||
|
action: ::symbol::Action::Submit {
|
||||||
|
text: None,
|
||||||
|
keys: vec!(::symbol::KeySym("U002E".into())),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,10 @@
|
|||||||
/* Adapted from https://github.com/notriddle/rust-float-ord revision e995165f
|
/* Adapted from https://github.com/notriddle/rust-float-ord revision e995165f
|
||||||
* maintained by Michael Howell <michael@notriddle.com>
|
* maintained by Michael Howell <michael@notriddle.com>
|
||||||
* licensed under MIT / Apache-2.0 licenses
|
* licensed under MIT / Apache-2.0 licenses
|
||||||
|
*
|
||||||
|
* This version drops any dependency on rand.
|
||||||
|
* Caution: Don't pull the version from crates.io
|
||||||
|
* before making sure rand is optional.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern crate core;
|
extern crate core;
|
||||||
@ -67,6 +71,7 @@ float_ord_impl!(f64, u64, 64);
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// use rs::float_ord;
|
||||||
/// let mut v = [-5.0, 4.0, 1.0, -3.0, 2.0];
|
/// let mut v = [-5.0, 4.0, 1.0, -3.0, 2.0];
|
||||||
///
|
///
|
||||||
/// float_ord::sort(&mut v);
|
/// float_ord::sort(&mut v);
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use std::num::Wrapping;
|
|||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
use super::bitflags;
|
use super::bitflags;
|
||||||
|
use ::util::c::into_cstring;
|
||||||
|
|
||||||
// Traits
|
// Traits
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@ -13,15 +14,8 @@ use std::convert::TryFrom;
|
|||||||
pub mod c {
|
pub mod c {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
|
|
||||||
fn into_cstring(s: *const c_char) -> Result<CString, std::ffi::NulError> {
|
|
||||||
CString::new(
|
|
||||||
unsafe {CStr::from_ptr(s)}.to_bytes()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following defined in C
|
// The following defined in C
|
||||||
|
|
||||||
/// struct zwp_input_method_v2*
|
/// struct zwp_input_method_v2*
|
||||||
@ -91,7 +85,9 @@ pub mod c {
|
|||||||
{
|
{
|
||||||
let imservice = check_imservice(imservice, im).unwrap();
|
let imservice = check_imservice(imservice, im).unwrap();
|
||||||
imservice.pending = IMProtocolState {
|
imservice.pending = IMProtocolState {
|
||||||
surrounding_text: into_cstring(text).expect("Received invalid string"),
|
surrounding_text: into_cstring(text)
|
||||||
|
.expect("Received invalid string")
|
||||||
|
.expect("Received null string"),
|
||||||
surrounding_cursor: cursor,
|
surrounding_cursor: cursor,
|
||||||
..imservice.pending.clone()
|
..imservice.pending.clone()
|
||||||
};
|
};
|
||||||
@ -230,7 +226,8 @@ bitflags!{
|
|||||||
/// Map to `text_input_unstable_v3.content_purpose` values
|
/// Map to `text_input_unstable_v3.content_purpose` values
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// assert_eq!(ContentPurpose::Alpha as u32, 0);
|
/// use rs::imservice::ContentPurpose;
|
||||||
|
/// assert_eq!(ContentPurpose::Alpha as u32, 1);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ContentPurpose {
|
pub enum ContentPurpose {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef __KEYBOARD_H
|
#ifndef __KEYBOARD_H
|
||||||
#define __KYBOARD_H
|
#define __KEYBOARD_H
|
||||||
|
|
||||||
#include "stdbool.h"
|
#include "stdbool.h"
|
||||||
#include "inttypes.h"
|
#include "inttypes.h"
|
||||||
@ -19,6 +19,7 @@ uint32_t squeek_key_is_locked(struct squeek_key *key);
|
|||||||
void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed);
|
void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed);
|
||||||
uint32_t squeek_key_get_keycode(struct squeek_key *key);
|
uint32_t squeek_key_get_keycode(struct squeek_key *key);
|
||||||
void squeek_key_set_keycode(struct squeek_key *key, uint32_t keycode);
|
void squeek_key_set_keycode(struct squeek_key *key, uint32_t keycode);
|
||||||
|
uint32_t squeek_key_equal(struct squeek_key* key, struct squeek_key* key1);
|
||||||
|
|
||||||
struct squeek_symbol *squeek_key_get_symbol(struct squeek_key* key);
|
struct squeek_symbol *squeek_key_get_symbol(struct squeek_key* key);
|
||||||
const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_key *key);
|
const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_key *key);
|
||||||
|
|||||||
365
src/keyboard.rs
365
src/keyboard.rs
@ -1,90 +1,41 @@
|
|||||||
use std::vec::Vec;
|
/*! State of the emulated keyboard and keys */
|
||||||
|
|
||||||
use super::symbol;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
|
use ::symbol::{ Symbol, Action };
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
use std::iter::{ FromIterator, IntoIterator };
|
||||||
|
|
||||||
/// 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 ::util::c::{ as_cstr, into_cstring };
|
use ::util::c;
|
||||||
|
use ::util::c::as_cstr;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::ptr;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
// traits
|
pub type CKeyState = c::Wrapped<KeyState>;
|
||||||
|
|
||||||
use std::borrow::ToOwned;
|
|
||||||
|
|
||||||
|
|
||||||
// The following defined in C
|
|
||||||
#[no_mangle]
|
|
||||||
extern "C" {
|
|
||||||
fn eek_keysym_from_name(name: *const c_char) -> u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The wrapped structure for KeyState suitable for handling in C
|
|
||||||
/// Since C doesn't respect borrowing rules,
|
|
||||||
/// RefCell will enforce them dynamically (only 1 writer/many readers)
|
|
||||||
/// Rc is implied and will ensure timely dropping
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct CKeyState(*const RefCell<KeyState>);
|
|
||||||
|
|
||||||
impl Clone for CKeyState {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
CKeyState(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CKeyState {
|
|
||||||
pub fn wrap(state: Rc<RefCell<KeyState>>) -> CKeyState {
|
|
||||||
CKeyState(Rc::into_raw(state))
|
|
||||||
}
|
|
||||||
pub fn unwrap(self) -> Rc<RefCell<KeyState>> {
|
|
||||||
unsafe { Rc::from_raw(self.0) }
|
|
||||||
}
|
|
||||||
fn to_owned(self) -> KeyState {
|
|
||||||
let rc = self.unwrap();
|
|
||||||
let state = rc.borrow().to_owned();
|
|
||||||
Rc::into_raw(rc); // Prevent dropping
|
|
||||||
state
|
|
||||||
}
|
|
||||||
fn borrow_mut<F, T>(self, f: F) -> T where F: FnOnce(&mut KeyState) -> T {
|
|
||||||
let rc = self.unwrap();
|
|
||||||
let ret = {
|
|
||||||
let mut state = rc.borrow_mut();
|
|
||||||
f(&mut state)
|
|
||||||
};
|
|
||||||
Rc::into_raw(rc); // Prevent dropping
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: unwrapping
|
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
// TODO: this will receive data from the filesystem,
|
|
||||||
// so it should handle garbled strings in the future
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_key_new(keycode: u32) -> CKeyState {
|
|
||||||
let state: Rc<RefCell<KeyState>> = Rc::new(RefCell::new(
|
|
||||||
KeyState {
|
|
||||||
pressed: false,
|
|
||||||
locked: false,
|
|
||||||
keycode: keycode,
|
|
||||||
symbol: None,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
CKeyState::wrap(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_free(key: CKeyState) {
|
fn squeek_key_free(key: CKeyState) {
|
||||||
key.unwrap(); // reference dropped
|
unsafe { key.unwrap() }; // reference dropped
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compares pointers to the data
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_key_equal(key: CKeyState, key2: CKeyState) -> u32 {
|
||||||
|
return Rc::ptr_eq(&key.clone_ref(), &key2.clone_ref()) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -97,7 +48,9 @@ pub mod c {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_set_pressed(key: CKeyState, pressed: u32) {
|
fn squeek_key_set_pressed(key: CKeyState, pressed: u32) {
|
||||||
key.borrow_mut(|key| key.pressed = pressed != 0);
|
let key = key.clone_ref();
|
||||||
|
let mut key = key.borrow_mut();
|
||||||
|
key.pressed = pressed != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -109,109 +62,15 @@ pub mod c {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_set_locked(key: CKeyState, locked: u32) {
|
fn squeek_key_set_locked(key: CKeyState, locked: u32) {
|
||||||
key.borrow_mut(|key| key.locked = locked != 0);
|
let key = key.clone_ref();
|
||||||
|
let mut key = key.borrow_mut();
|
||||||
|
key.locked = locked != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_get_keycode(key: CKeyState) -> u32 {
|
fn squeek_key_get_keycode(key: CKeyState) -> u32 {
|
||||||
return key.to_owned().keycode as u32;
|
return key.to_owned().keycode.unwrap_or(0u32);
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_key_set_keycode(key: CKeyState, code: u32) {
|
|
||||||
key.borrow_mut(|key| key.keycode = code);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this will receive data from the filesystem,
|
|
||||||
// so it should handle garbled strings in the future
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_key_add_symbol(
|
|
||||||
key: CKeyState,
|
|
||||||
element: *const c_char,
|
|
||||||
text_raw: *const c_char, keyval: u32,
|
|
||||||
label: *const c_char, icon: *const c_char,
|
|
||||||
tooltip: *const c_char,
|
|
||||||
) {
|
|
||||||
let element = as_cstr(&element)
|
|
||||||
.expect("Missing element name");
|
|
||||||
|
|
||||||
let text = into_cstring(text_raw)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
eprintln!("Text unreadable: {}", e);
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.and_then(|text| {
|
|
||||||
if text.as_bytes() == b"" {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(text)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let icon = into_cstring(icon)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
eprintln!("Icon name unreadable: {}", e);
|
|
||||||
None
|
|
||||||
});
|
|
||||||
|
|
||||||
use symbol::*;
|
|
||||||
// Only read label if there's no icon
|
|
||||||
let label = match icon {
|
|
||||||
Some(icon) => Label::IconName(icon),
|
|
||||||
None => Label::Text(
|
|
||||||
into_cstring(label)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
eprintln!("Label unreadable: {}", e);
|
|
||||||
Some(CString::new(" ").unwrap())
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
eprintln!("Label missing");
|
|
||||||
CString::new(" ").unwrap()
|
|
||||||
})
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let tooltip = into_cstring(tooltip)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
eprintln!("Tooltip unreadable: {}", e);
|
|
||||||
None
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
key.borrow_mut(|key| {
|
|
||||||
if let Some(_) = key.symbol {
|
|
||||||
eprintln!("Key {:?} already has a symbol defined", text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
key.symbol = Some(match element.to_bytes() {
|
|
||||||
b"symbol" => Symbol {
|
|
||||||
action: Action::Submit {
|
|
||||||
text: text,
|
|
||||||
keys: Vec::new(),
|
|
||||||
},
|
|
||||||
label: label,
|
|
||||||
tooltip: tooltip,
|
|
||||||
},
|
|
||||||
_ => panic!("unsupported element type {:?}", element),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_key_get_symbol(key: CKeyState) -> *const symbol::Symbol {
|
|
||||||
key.borrow_mut(|key| {
|
|
||||||
match key.symbol {
|
|
||||||
// This pointer stays after the function exits,
|
|
||||||
// so it must reference borrowed data and not any copy
|
|
||||||
Some(ref symbol) => symbol as *const symbol::Symbol,
|
|
||||||
None => ptr::null(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -225,20 +84,14 @@ pub mod c {
|
|||||||
.to_str()
|
.to_str()
|
||||||
.expect("Bad key name");
|
.expect("Bad key name");
|
||||||
|
|
||||||
let symbol_name = match key.to_owned().symbol {
|
let symbol_name = match key.to_owned().symbol.action {
|
||||||
Some(ref symbol) => match &symbol.action {
|
Action::Submit { text: Some(text), .. } => {
|
||||||
symbol::Action::Submit { text: Some(text), .. } => {
|
Some(
|
||||||
Some(
|
text.clone()
|
||||||
text.clone()
|
.into_string().expect("Bad symbol")
|
||||||
.into_string().expect("Bad symbol")
|
)
|
||||||
)
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
eprintln!("Key {} has no symbol", key_name);
|
|
||||||
None
|
|
||||||
},
|
},
|
||||||
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = match symbol_name {
|
let inner = match symbol_name {
|
||||||
@ -251,7 +104,7 @@ pub mod c {
|
|||||||
.into_raw()
|
.into_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_get_action_name(
|
fn squeek_key_get_action_name(
|
||||||
key_name: *const c_char,
|
key_name: *const c_char,
|
||||||
@ -262,20 +115,14 @@ pub mod c {
|
|||||||
.to_str()
|
.to_str()
|
||||||
.expect("Bad key name");
|
.expect("Bad key name");
|
||||||
|
|
||||||
let symbol_name = match key.to_owned().symbol {
|
let symbol_name = match key.to_owned().symbol.action {
|
||||||
Some(ref symbol) => match &symbol.action {
|
Action::Submit { text: Some(text), .. } => {
|
||||||
symbol::Action::Submit { text: Some(text), .. } => {
|
Some(
|
||||||
Some(
|
text.clone()
|
||||||
text.clone()
|
.into_string().expect("Bad symbol text")
|
||||||
.into_string().expect("Bad symbol")
|
)
|
||||||
)
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
eprintln!("Key {} has no symbol", key_name);
|
|
||||||
None
|
|
||||||
},
|
},
|
||||||
|
_ => None
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = match symbol_name {
|
let inner = match symbol_name {
|
||||||
@ -287,14 +134,126 @@ pub mod c {
|
|||||||
.expect("Couldn't convert string")
|
.expect("Couldn't convert string")
|
||||||
.into_raw()
|
.into_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct KeyState {
|
pub struct KeyState {
|
||||||
pub pressed: bool,
|
pub pressed: bool,
|
||||||
pub locked: bool,
|
pub locked: bool,
|
||||||
pub keycode: u32,
|
pub keycode: Option<u32>,
|
||||||
// TODO: remove the optionality of a symbol
|
pub symbol: Symbol,
|
||||||
pub symbol: Option<symbol::Symbol>,
|
}
|
||||||
|
|
||||||
|
/// Generates a mapping where each key gets a keycode, starting from 8
|
||||||
|
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
|
||||||
|
key_names: C
|
||||||
|
) -> HashMap<String, u32> {
|
||||||
|
HashMap::from_iter(
|
||||||
|
key_names.into_iter()
|
||||||
|
.map(|name| String::from(name))
|
||||||
|
.zip(8..)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FormattingError {
|
||||||
|
Utf(FromUtf8Error),
|
||||||
|
Format(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FormattingError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
FormattingError::Utf(e) => write!(f, "UTF: {}", e),
|
||||||
|
FormattingError::Format(e) => write!(f, "Format: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for FormattingError {
|
||||||
|
fn from(e: io::Error) -> Self {
|
||||||
|
FormattingError::Format(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a de-facto single level keymap. TODO: actually drop second level
|
||||||
|
pub fn generate_keymap(
|
||||||
|
keystates: &HashMap::<String, Rc<RefCell<KeyState>>>
|
||||||
|
) -> Result<String, FormattingError> {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
"xkb_keymap {{
|
||||||
|
|
||||||
|
xkb_keycodes \"squeekboard\" {{
|
||||||
|
minimum = 8;
|
||||||
|
maximum = 255;"
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for (name, state) in keystates.iter() {
|
||||||
|
let state = state.borrow();
|
||||||
|
if let ::symbol::Action::Submit { text: _, keys } = &state.symbol.action {
|
||||||
|
match keys.len() {
|
||||||
|
0 => eprintln!("Key {} has no keysyms", name),
|
||||||
|
a => {
|
||||||
|
// TODO: don't ignore any keysyms
|
||||||
|
if a > 1 {
|
||||||
|
eprintln!("Key {} multiple keysyms", name);
|
||||||
|
}
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
"
|
||||||
|
<{}> = {};",
|
||||||
|
keys[0].0,
|
||||||
|
state.keycode.unwrap()
|
||||||
|
)?;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
"
|
||||||
|
}};
|
||||||
|
|
||||||
|
xkb_symbols \"squeekboard\" {{
|
||||||
|
|
||||||
|
name[Group1] = \"Letters\";
|
||||||
|
name[Group2] = \"Numbers/Symbols\";"
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for (name, state) in keystates.iter() {
|
||||||
|
if let ::symbol::Action::Submit { text: _, keys } = &state.borrow().symbol.action {
|
||||||
|
if let Some(keysym) = keys.iter().next() {
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
"
|
||||||
|
key <{}> {{ [ {0} ] }};",
|
||||||
|
keysym.0,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
"
|
||||||
|
}};
|
||||||
|
|
||||||
|
xkb_types \"squeekboard\" {{
|
||||||
|
|
||||||
|
type \"TWO_LEVEL\" {{
|
||||||
|
modifiers = Shift;
|
||||||
|
map[Shift] = Level2;
|
||||||
|
level_name[Level1] = \"Base\";
|
||||||
|
level_name[Level2] = \"Shift\";
|
||||||
|
}};
|
||||||
|
}};
|
||||||
|
|
||||||
|
xkb_compatibility \"squeekboard\" {{
|
||||||
|
}};
|
||||||
|
}};"
|
||||||
|
)?;
|
||||||
|
|
||||||
|
String::from_utf8(buf).map_err(FormattingError::Utf)
|
||||||
}
|
}
|
||||||
|
|||||||
41
src/layout.h
41
src/layout.h
@ -9,23 +9,16 @@
|
|||||||
struct squeek_button;
|
struct squeek_button;
|
||||||
struct squeek_row;
|
struct squeek_row;
|
||||||
struct squeek_view;
|
struct squeek_view;
|
||||||
|
struct squeek_layout;
|
||||||
|
|
||||||
struct squeek_row *squeek_row_new(int32_t angle);
|
|
||||||
struct squeek_button *squeek_row_create_button (struct squeek_row *row,
|
|
||||||
guint keycode, guint oref);
|
|
||||||
struct squeek_button *squeek_row_create_button_with_state(struct squeek_row *row,
|
|
||||||
struct squeek_button *source);
|
|
||||||
void squeek_row_set_angle(struct squeek_row *row, int32_t angle);
|
|
||||||
int32_t squeek_row_get_angle(const struct squeek_row*);
|
int32_t squeek_row_get_angle(const struct squeek_row*);
|
||||||
|
|
||||||
EekBounds squeek_row_get_bounds(const struct squeek_row*);
|
EekBounds squeek_row_get_bounds(const struct squeek_row*);
|
||||||
void squeek_row_set_bounds(struct squeek_row* row, EekBounds bounds);
|
|
||||||
|
|
||||||
uint32_t squeek_row_contains(struct squeek_row*, struct squeek_button *button);
|
uint32_t squeek_row_contains(struct squeek_row*, struct squeek_button *button);
|
||||||
|
|
||||||
struct button_place squeek_view_find_key(struct squeek_view*, struct squeek_key *state);
|
struct button_place squeek_view_find_key(struct squeek_view*, struct squeek_key *state);
|
||||||
|
|
||||||
struct squeek_button *squeek_row_find_button_by_position(struct squeek_row *row, EekPoint point, EekPoint origin);
|
|
||||||
|
|
||||||
typedef void (*ButtonCallback) (struct squeek_button *button, gpointer user_data);
|
typedef void (*ButtonCallback) (struct squeek_button *button, gpointer user_data);
|
||||||
void squeek_row_foreach(struct squeek_row*,
|
void squeek_row_foreach(struct squeek_row*,
|
||||||
@ -34,34 +27,19 @@ void squeek_row_foreach(struct squeek_row*,
|
|||||||
|
|
||||||
void squeek_row_free(struct squeek_row*);
|
void squeek_row_free(struct squeek_row*);
|
||||||
|
|
||||||
/*
|
|
||||||
struct squeek_button *squeek_buttons_find_by_position(
|
|
||||||
const struct squeek_buttons *buttons,
|
|
||||||
double x, double y,
|
|
||||||
double origin_x, double origin_y,
|
|
||||||
double angle);
|
|
||||||
void squeek_buttons_add(struct squeek_buttons*, const struct squeek_button* button);
|
|
||||||
void squeek_buttons_remove_key(struct squeek_buttons*, const struct squeek_key* key);
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct squeek_button *squeek_button_new(uint32_t keycode, uint32_t oref);
|
|
||||||
struct squeek_button *squeek_button_new_with_state(const struct squeek_button* source);
|
|
||||||
uint32_t squeek_button_get_oref(const struct squeek_button*);
|
uint32_t squeek_button_get_oref(const struct squeek_button*);
|
||||||
EekBounds squeek_button_get_bounds(const struct squeek_button*);
|
EekBounds squeek_button_get_bounds(const struct squeek_button*);
|
||||||
void squeek_button_set_bounds(struct squeek_button* button, EekBounds bounds);
|
const char *squeek_button_get_label(const struct squeek_button*);
|
||||||
|
const char *squeek_button_get_icon_name(const struct squeek_button*);
|
||||||
|
const char *squeek_button_get_name(const struct squeek_button*);
|
||||||
|
|
||||||
struct squeek_symbol *squeek_button_get_symbol (
|
|
||||||
const struct squeek_button *button);
|
|
||||||
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
|
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
|
||||||
uint32_t *squeek_button_has_key(const struct squeek_button* button,
|
uint32_t *squeek_button_has_key(const struct squeek_button* button,
|
||||||
const struct squeek_key *key);
|
const struct squeek_key *key);
|
||||||
void squeek_button_print(const struct squeek_button* button);
|
void squeek_button_print(const struct squeek_button* button);
|
||||||
|
|
||||||
|
|
||||||
struct squeek_view *squeek_view_new(EekBounds bounds);
|
|
||||||
struct squeek_row *squeek_view_create_row(struct squeek_view *, int32_t angle);
|
|
||||||
EekBounds squeek_view_get_bounds(const struct squeek_view*);
|
EekBounds squeek_view_get_bounds(const struct squeek_view*);
|
||||||
void squeek_view_set_bounds(const struct squeek_view*, EekBounds bounds);
|
|
||||||
|
|
||||||
typedef void (*RowCallback) (struct squeek_row *row, gpointer user_data);
|
typedef void (*RowCallback) (struct squeek_row *row, gpointer user_data);
|
||||||
void squeek_view_foreach(struct squeek_view*,
|
void squeek_view_foreach(struct squeek_view*,
|
||||||
@ -71,7 +49,16 @@ void squeek_view_foreach(struct squeek_view*,
|
|||||||
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
|
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
|
||||||
struct squeek_button *button);
|
struct squeek_button *button);
|
||||||
|
|
||||||
|
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
squeek_view_place_contents(struct squeek_view *view, LevelKeyboard *keyboard);
|
squeek_layout_place_contents(struct squeek_layout*);
|
||||||
|
struct squeek_view *squeek_layout_get_current_view(struct squeek_layout*);
|
||||||
|
void squeek_layout_set_state_from_press(struct squeek_layout* layout, LevelKeyboard *keyboard, struct squeek_key* key);
|
||||||
|
|
||||||
|
|
||||||
|
struct squeek_layout *squeek_load_layout(const char *type);
|
||||||
|
const char *squeek_layout_get_keymap(const struct squeek_layout*);
|
||||||
|
void squeek_layout_free(struct squeek_layout*);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
591
src/layout.rs
591
src/layout.rs
@ -1,4 +1,25 @@
|
|||||||
|
/*!
|
||||||
|
* Layout-related data.
|
||||||
|
*
|
||||||
|
* The `View` contains `Row`s and each `Row` contains `Button`s.
|
||||||
|
* They carry data relevant to their positioning only,
|
||||||
|
* except the Button, which also carries some data
|
||||||
|
* about its appearance and function.
|
||||||
|
*
|
||||||
|
* The layout is determined bottom-up, by measuring `Button` sizes,
|
||||||
|
* deriving `Row` sizes from them, and then centering them within the `View`.
|
||||||
|
*
|
||||||
|
* That makes the `View` position immutable,
|
||||||
|
* and therefore different than the other positions.
|
||||||
|
*
|
||||||
|
* Note that it might be a better idea
|
||||||
|
* to make `View` position depend on its contents,
|
||||||
|
* and let the renderer scale and center it within the widget.
|
||||||
|
*/
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::CString;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
@ -10,14 +31,15 @@ use ::symbol::*;
|
|||||||
pub mod c {
|
pub mod c {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::{ c_char, c_void };
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
// The following defined in C
|
// The following defined in C
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct UserData(*const c_void);
|
pub struct UserData(*const c_void);
|
||||||
|
|
||||||
/// The index in the relevant outline table
|
/// The index in the relevant outline table
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -41,57 +63,18 @@ pub mod c {
|
|||||||
pub height: f64
|
pub height: f64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bounds {
|
|
||||||
pub fn zero() -> Bounds {
|
|
||||||
Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData);
|
type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData);
|
||||||
type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData);
|
type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData);
|
||||||
|
|
||||||
// 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 extern "C"
|
|
||||||
fn squeek_view_new(bounds: Bounds) -> *mut ::layout::View {
|
|
||||||
Box::into_raw(Box::new(::layout::View {
|
|
||||||
rows: Vec::new(),
|
|
||||||
bounds: bounds,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
|
fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
|
||||||
unsafe { &*view }.bounds.clone()
|
unsafe { &*view }.bounds.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_view_set_bounds(view: *mut ::layout::View, bounds: Bounds) {
|
|
||||||
unsafe { &mut *view }.bounds = bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Places a row into the view and returns a reference to it
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_view_create_row(
|
|
||||||
view: *mut ::layout::View,
|
|
||||||
angle: i32,
|
|
||||||
) -> *mut ::layout::Row {
|
|
||||||
let view = unsafe { &mut *view };
|
|
||||||
|
|
||||||
view.rows.push(Box::new(::layout::Row::new(angle)));
|
#[no_mangle]
|
||||||
// Return the reference directly instead of a Box, it's not on the stack
|
|
||||||
// It will live as long as the Vec
|
|
||||||
let last_idx = view.rows.len() - 1;
|
|
||||||
// Caution: Box can't be returned directly,
|
|
||||||
// so returning a reference to its innards
|
|
||||||
view.rows[last_idx].as_mut() as *mut ::layout::Row
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_view_foreach(
|
fn squeek_view_foreach(
|
||||||
view: *mut ::layout::View,
|
view: *mut ::layout::View,
|
||||||
@ -104,68 +87,6 @@ pub mod c {
|
|||||||
unsafe { callback(row, data) };
|
unsafe { callback(row, data) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_row_new(angle: i32) -> *mut ::layout::Row {
|
|
||||||
Box::into_raw(Box::new(::layout::Row::new(angle)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Places a button into the row and returns a reference to it
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_row_create_button(
|
|
||||||
row: *mut ::layout::Row,
|
|
||||||
keycode: u32, oref: u32
|
|
||||||
) -> *mut ::layout::Button {
|
|
||||||
let row = unsafe { &mut *row };
|
|
||||||
let state: Rc<RefCell<::keyboard::KeyState>> = Rc::new(RefCell::new(
|
|
||||||
::keyboard::KeyState {
|
|
||||||
pressed: false,
|
|
||||||
locked: false,
|
|
||||||
keycode: keycode,
|
|
||||||
symbol: None,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
row.buttons.push(Box::new(::layout::Button {
|
|
||||||
oref: OutlineRef(oref),
|
|
||||||
bounds: None,
|
|
||||||
state: state,
|
|
||||||
}));
|
|
||||||
// Return the reference directly instead of a Box, it's not on the stack
|
|
||||||
// It will live as long as the Vec
|
|
||||||
let last_idx = row.buttons.len() - 1;
|
|
||||||
// Caution: Box can't be returned directly,
|
|
||||||
// so returning a reference to its innards
|
|
||||||
row.buttons[last_idx].as_mut() as *mut ::layout::Button
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Places a button into the row, copying its state,
|
|
||||||
/// and returns a reference to it
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_row_create_button_with_state(
|
|
||||||
row: *mut ::layout::Row,
|
|
||||||
button: *const ::layout::Button,
|
|
||||||
) -> *mut ::layout::Button {
|
|
||||||
let row = unsafe { &mut *row };
|
|
||||||
let source = unsafe { &*button };
|
|
||||||
row.buttons.push(Box::new(source.clone()));
|
|
||||||
// Return the reference directly instead of a Box, it's not on the stack
|
|
||||||
// It will live as long as the Vec
|
|
||||||
let last_idx = row.buttons.len() - 1;
|
|
||||||
// Caution: Box can't be returned directly,
|
|
||||||
// so returning a reference to its innards directly
|
|
||||||
row.buttons[last_idx].as_mut() as *mut ::layout::Button
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_row_set_angle(row: *mut ::layout::Row, angle: i32) {
|
|
||||||
let row = unsafe { &mut *row };
|
|
||||||
row.angle = angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
@ -212,59 +133,13 @@ pub mod c {
|
|||||||
unsafe { Box::from_raw(row) }; // gets dropped
|
unsafe { Box::from_raw(row) }; // gets dropped
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_button_new(keycode: u32, oref: u32) -> *mut ::layout::Button {
|
|
||||||
let state: Rc<RefCell<::keyboard::KeyState>> = Rc::new(RefCell::new(
|
|
||||||
::keyboard::KeyState {
|
|
||||||
pressed: false,
|
|
||||||
locked: false,
|
|
||||||
keycode: keycode,
|
|
||||||
symbol: None,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
Box::into_raw(Box::new(::layout::Button {
|
|
||||||
oref: OutlineRef(oref),
|
|
||||||
bounds: None,
|
|
||||||
state: state,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_button_new_with_state(source: *mut ::layout::Button) -> *mut ::layout::Button {
|
|
||||||
let source = unsafe { &*source };
|
|
||||||
let button = Box::new(source.clone());
|
|
||||||
Box::into_raw(button)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_button_get_oref(button: *const ::layout::Button) -> u32 {
|
|
||||||
let button = unsafe { &*button };
|
|
||||||
button.oref.0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bounds transparently mapped to C, therefore no pointer needed
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
|
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
|
||||||
let button = unsafe { &*button };
|
let button = unsafe { &*button };
|
||||||
match &button.bounds {
|
button.bounds.clone()
|
||||||
Some(bounds) => bounds.clone(),
|
|
||||||
None => panic!("Button doesn't have any bounds yet"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set bounds by consuming the value
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_button_set_bounds(button: *mut ::layout::Button, bounds: Bounds) {
|
|
||||||
let button = unsafe { &mut *button };
|
|
||||||
button.bounds = Some(bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Borrow a new reference to key state. Doesn't need freeing
|
/// Borrow a new reference to key state. Doesn't need freeing
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
@ -283,12 +158,43 @@ pub mod c {
|
|||||||
) -> *const Symbol {
|
) -> *const Symbol {
|
||||||
let button = unsafe { &*button };
|
let button = unsafe { &*button };
|
||||||
let state = button.state.borrow();
|
let state = button.state.borrow();
|
||||||
match state.symbol {
|
&state.symbol as *const Symbol
|
||||||
Some(ref symbol) => symbol as *const Symbol,
|
}
|
||||||
None => ptr::null(),
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_button_get_label(
|
||||||
|
button: *const ::layout::Button
|
||||||
|
) -> *const c_char {
|
||||||
|
let button = unsafe { &*button };
|
||||||
|
match &button.label {
|
||||||
|
Label::Text(text) => text.as_ptr(),
|
||||||
|
// returning static strings to C is a bit cumbersome
|
||||||
|
Label::IconName(_) => unsafe {
|
||||||
|
// CStr doesn't allocate anything, so it only points to
|
||||||
|
// the 'static str, avoiding a memory leak
|
||||||
|
CStr::from_bytes_with_nul_unchecked(b"icon\0")
|
||||||
|
}.as_ptr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_button_get_icon_name(button: *const Button) -> *const c_char {
|
||||||
|
let button = unsafe { &*button };
|
||||||
|
match &button.label {
|
||||||
|
Label::Text(_) => ptr::null(),
|
||||||
|
Label::IconName(name) => name.as_ptr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_button_get_name(button: *const Button) -> *const c_char {
|
||||||
|
let button = unsafe { &*button };
|
||||||
|
button.name.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_button_has_key(
|
fn squeek_button_has_key(
|
||||||
@ -296,9 +202,8 @@ pub mod c {
|
|||||||
state: ::keyboard::c::CKeyState,
|
state: ::keyboard::c::CKeyState,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
let button = unsafe { &*button };
|
let button = unsafe { &*button };
|
||||||
let state = state.unwrap();
|
let state = state.clone_ref();
|
||||||
let equal = Rc::ptr_eq(&button.state, &state);
|
let equal = Rc::ptr_eq(&button.state, &state);
|
||||||
Rc::into_raw(state); // Prevent dropping
|
|
||||||
equal as u32
|
equal as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,43 +214,100 @@ pub mod c {
|
|||||||
println!("{:?}", button);
|
println!("{:?}", button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_layout_get_current_view(layout: *const Layout) -> *const View {
|
||||||
|
let layout = unsafe { &*layout };
|
||||||
|
let view_name = layout.current_view.clone();
|
||||||
|
layout.views.get(&view_name)
|
||||||
|
.expect("Current view doesn't exist")
|
||||||
|
.as_ref() as *const View
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_layout_get_keymap(layout: *const Layout) -> *const c_char {
|
||||||
|
let layout = unsafe { &*layout };
|
||||||
|
layout.keymap_str.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_layout_free(layout: *mut Layout) {
|
||||||
|
unsafe { Box::from_raw(layout) };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Entry points for more complex procedures and algoithms which span multiple modules
|
/// Entry points for more complex procedures and algoithms which span multiple modules
|
||||||
mod procedures {
|
pub mod procedures {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct LevelKeyboard(*const c_void);
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct ButtonPlace {
|
pub struct ButtonPlace {
|
||||||
row: *const Row,
|
row: *const Row,
|
||||||
button: *const Button,
|
button: *const Button,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct LevelKeyboard(*const c_void);
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn eek_get_outline_size(
|
|
||||||
keyboard: *const LevelKeyboard,
|
|
||||||
outline: u32
|
|
||||||
) -> Bounds;
|
|
||||||
|
|
||||||
/// Checks if point falls within bounds,
|
/// Checks if point falls within bounds,
|
||||||
/// which are relative to origin and rotated by angle (I think)
|
/// which are relative to origin and rotated by angle (I think)
|
||||||
fn eek_are_bounds_inside (bounds: Bounds,
|
pub fn eek_are_bounds_inside (bounds: Bounds,
|
||||||
point: Point,
|
point: Point,
|
||||||
origin: Point,
|
origin: Point,
|
||||||
angle: i32
|
angle: i32
|
||||||
) -> u32;
|
) -> u32;
|
||||||
|
|
||||||
|
pub fn eek_keyboard_set_key_locked(
|
||||||
|
keyboard: *mut LevelKeyboard,
|
||||||
|
key: ::keyboard::c::CKeyState,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn squeek_buttons_get_outlines(
|
#[no_mangle]
|
||||||
buttons: &Vec<Box<Button>>,
|
pub extern "C"
|
||||||
keyboard: *const LevelKeyboard,
|
fn squeek_layout_set_state_from_press(
|
||||||
) -> Vec<Bounds> {
|
layout: *mut Layout,
|
||||||
buttons.iter().map(|button| {
|
keyboard: *mut LevelKeyboard,
|
||||||
unsafe { eek_get_outline_size(keyboard, button.oref.0) }
|
key: ::keyboard::c::CKeyState,
|
||||||
}).collect()
|
) {
|
||||||
|
let layout = unsafe { &mut *layout };
|
||||||
|
|
||||||
|
let view_name = match key.to_owned().symbol.action {
|
||||||
|
::symbol::Action::SetLevel(name) => {
|
||||||
|
Some(name.clone())
|
||||||
|
},
|
||||||
|
::symbol::Action::LockLevel { lock, unlock } => {
|
||||||
|
let locked = {
|
||||||
|
let key = key.clone_ref();
|
||||||
|
let mut key = key.borrow_mut();
|
||||||
|
key.locked ^= true;
|
||||||
|
key.locked
|
||||||
|
};
|
||||||
|
|
||||||
|
if locked {
|
||||||
|
unsafe {
|
||||||
|
eek_keyboard_set_key_locked(
|
||||||
|
keyboard,
|
||||||
|
key
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(if locked { lock } else { unlock }.clone())
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(view_name) = view_name {
|
||||||
|
if let Err(_e) = layout.set_view(view_name.clone()) {
|
||||||
|
eprintln!("No such view: {}, ignoring switch", view_name)
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Places each button in order, starting from 0 on the left,
|
/// Places each button in order, starting from 0 on the left,
|
||||||
@ -356,49 +318,21 @@ pub mod c {
|
|||||||
/// Sets button and row sizes according to their contents.
|
/// Sets button and row sizes according to their contents.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_view_place_contents(
|
fn squeek_layout_place_contents(
|
||||||
view: *mut ::layout::View,
|
layout: *mut Layout,
|
||||||
keyboard: *const LevelKeyboard, // source of outlines
|
|
||||||
) {
|
) {
|
||||||
let view = unsafe { &mut *view };
|
let layout = unsafe { &mut *layout };
|
||||||
|
for view in layout.views.values_mut() {
|
||||||
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row|
|
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
|
||||||
squeek_buttons_get_outlines(&row.buttons, keyboard)
|
row.buttons.iter()
|
||||||
).collect();
|
.map(|button| button.bounds.clone())
|
||||||
|
.collect()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
view.place_buttons_with_sizes(sizes);
|
view.place_buttons_with_sizes(sizes);
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_row_find_button_by_position(
|
|
||||||
row: *mut Row, point: Point, origin: Point
|
|
||||||
) -> *mut Button {
|
|
||||||
let row = unsafe { &mut *row };
|
|
||||||
let row_bounds = row.bounds
|
|
||||||
.as_ref().expect("Missing bounds on row");
|
|
||||||
let origin = Point {
|
|
||||||
x: origin.x + row_bounds.x,
|
|
||||||
y: origin.y + row_bounds.y,
|
|
||||||
};
|
|
||||||
let angle = row.angle;
|
|
||||||
let result = row.buttons.iter_mut().find(|button| {
|
|
||||||
let bounds = button.bounds
|
|
||||||
.as_ref().expect("Missing bounds on button")
|
|
||||||
.clone();
|
|
||||||
let point = point.clone();
|
|
||||||
let origin = origin.clone();
|
|
||||||
unsafe {
|
|
||||||
eek_are_bounds_inside(bounds, point, origin, angle) == 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Some(button) => button.as_mut() as *mut Button,
|
|
||||||
None => ptr::null_mut(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
|
fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
|
||||||
row.buttons.iter().position(
|
row.buttons.iter().position(
|
||||||
// TODO: wrap Button properly in Rc; this comparison is unreliable
|
// TODO: wrap Button properly in Rc; this comparison is unreliable
|
||||||
@ -429,13 +363,10 @@ pub mod c {
|
|||||||
needle: ::keyboard::c::CKeyState,
|
needle: ::keyboard::c::CKeyState,
|
||||||
) -> ButtonPlace {
|
) -> ButtonPlace {
|
||||||
let view = unsafe { &*view };
|
let view = unsafe { &*view };
|
||||||
let state = needle.unwrap();
|
let state = needle.clone_ref();
|
||||||
|
|
||||||
let paths = ::layout::procedures::find_key_paths(view, &state);
|
let paths = ::layout::procedures::find_key_paths(view, &state);
|
||||||
|
|
||||||
// Iterators used up, can turn the reference back into pointer
|
|
||||||
Rc::into_raw(state);
|
|
||||||
|
|
||||||
// Can only return 1 entry back to C
|
// Can only return 1 entry back to C
|
||||||
let (row, button) = match paths.get(0) {
|
let (row, button) = match paths.get(0) {
|
||||||
Some((row, button)) => (
|
Some((row, button)) => (
|
||||||
@ -446,40 +377,55 @@ pub mod c {
|
|||||||
};
|
};
|
||||||
ButtonPlace { row, button }
|
ButtonPlace { row, button }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_view_find_button_by_position(
|
||||||
|
view: *mut View, point: Point
|
||||||
|
) -> *mut Button {
|
||||||
|
let view = unsafe { &mut *view };
|
||||||
|
let result = view.find_button_by_position(point);
|
||||||
|
match result {
|
||||||
|
Some(button) => button.as_mut() as *mut Button,
|
||||||
|
None => ptr::null_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use super::super::test::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn row_has_button() {
|
fn row_has_button() {
|
||||||
let mut row = Row::new(0);
|
let state = make_state();
|
||||||
let button = squeek_row_create_button(&mut row as *mut Row, 0, 0);
|
let button = make_button_with_state(
|
||||||
assert_eq!(squeek_row_contains(&row, button), true);
|
"test".into(),
|
||||||
let shared_button = squeek_row_create_button_with_state(
|
state.clone()
|
||||||
&mut row as *mut Row,
|
|
||||||
button
|
|
||||||
);
|
);
|
||||||
assert_eq!(squeek_row_contains(&row, shared_button), true);
|
let button_ptr = button_as_raw(&button);
|
||||||
|
let mut row = Row::new(0);
|
||||||
|
row.buttons.push(button);
|
||||||
|
assert_eq!(squeek_row_contains(&row, button_ptr), true);
|
||||||
|
let shared_button = make_button_with_state(
|
||||||
|
"test2".into(),
|
||||||
|
state
|
||||||
|
);
|
||||||
|
let shared_button_ptr = button_as_raw(&shared_button);
|
||||||
|
row.buttons.push(shared_button);
|
||||||
|
assert_eq!(squeek_row_contains(&row, shared_button_ptr), true);
|
||||||
let row = Row::new(0);
|
let row = Row::new(0);
|
||||||
assert_eq!(squeek_row_contains(&row, button), false);
|
assert_eq!(squeek_row_contains(&row, button_ptr), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn view_has_button() {
|
fn view_has_button() {
|
||||||
let state = Rc::new(RefCell::new(::keyboard::KeyState {
|
let state = make_state();
|
||||||
pressed: false,
|
|
||||||
locked: false,
|
|
||||||
keycode: 0,
|
|
||||||
symbol: None,
|
|
||||||
}));
|
|
||||||
let state_clone = ::keyboard::c::CKeyState::wrap(state.clone());
|
let state_clone = ::keyboard::c::CKeyState::wrap(state.clone());
|
||||||
|
|
||||||
let button = Box::new(Button {
|
let button = make_button_with_state("1".into(), state);
|
||||||
oref: OutlineRef(0),
|
|
||||||
bounds: None,
|
|
||||||
state: state,
|
|
||||||
});
|
|
||||||
let button_ptr = button.as_ref() as *const Button;
|
let button_ptr = button.as_ref() as *const Button;
|
||||||
|
|
||||||
let row = Box::new(Row {
|
let row = Box::new(Row {
|
||||||
@ -532,18 +478,64 @@ pub mod c {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use ::keyboard::c::CKeyState;
|
||||||
|
|
||||||
|
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
|
||||||
|
Rc::new(RefCell::new(::keyboard::KeyState {
|
||||||
|
pressed: false,
|
||||||
|
locked: false,
|
||||||
|
keycode: None,
|
||||||
|
symbol: Symbol {
|
||||||
|
action: Action::SetLevel("default".into()),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_button_with_state(
|
||||||
|
name: String,
|
||||||
|
state: Rc<RefCell<::keyboard::KeyState>>,
|
||||||
|
) -> Box<Button> {
|
||||||
|
Box::new(Button {
|
||||||
|
name: CString::new(name.clone()).unwrap(),
|
||||||
|
corner_radius: 0f64,
|
||||||
|
bounds: c::Bounds {
|
||||||
|
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
||||||
|
},
|
||||||
|
label: Label::Text(CString::new(name).unwrap()),
|
||||||
|
state: state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button_as_raw(button: &Box<Button>) -> *const Button {
|
||||||
|
button.as_ref() as *const Button
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn button_has_key() {
|
fn button_has_key() {
|
||||||
let button = squeek_button_new(0, 0);
|
let state = make_state();
|
||||||
let state = squeek_button_get_key(button);
|
let button = make_button_with_state("1".into(), state.clone());
|
||||||
assert_eq!(squeek_button_has_key(button, state.clone()), 1);
|
assert_eq!(
|
||||||
let other_button = squeek_button_new(0, 0);
|
squeek_button_has_key(
|
||||||
assert_eq!(squeek_button_has_key(other_button, state.clone()), 0);
|
button_as_raw(&button),
|
||||||
let other_state = ::keyboard::c::squeek_key_new(0);
|
CKeyState::wrap(state.clone())
|
||||||
assert_eq!(squeek_button_has_key(button, other_state), 0);
|
),
|
||||||
let shared_button = squeek_button_new_with_state(button);
|
1
|
||||||
assert_eq!(squeek_button_has_key(shared_button, state), 1);
|
);
|
||||||
|
let other_state = make_state();
|
||||||
|
let other_button = make_button_with_state("1".into(), other_state);
|
||||||
|
assert_eq!(
|
||||||
|
squeek_button_has_key(
|
||||||
|
button_as_raw(&other_button),
|
||||||
|
CKeyState::wrap(state.clone())
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
let orphan_state = CKeyState::wrap(make_state());
|
||||||
|
assert_eq!(
|
||||||
|
squeek_button_has_key(button_as_raw(&button), orphan_state),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -554,26 +546,40 @@ pub struct Size {
|
|||||||
pub height: f64,
|
pub height: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Label {
|
||||||
|
/// Text used to display the symbol
|
||||||
|
Text(CString),
|
||||||
|
/// Icon name used to render the symbol
|
||||||
|
IconName(CString),
|
||||||
|
}
|
||||||
|
|
||||||
/// The graphical representation of a button
|
/// The graphical representation of a button
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Button {
|
pub struct Button {
|
||||||
oref: c::OutlineRef,
|
/// ID string, e.g. for CSS
|
||||||
/// TODO: abolish Option, buttons should be created with bounds fully formed
|
pub name: CString,
|
||||||
bounds: Option<c::Bounds>,
|
/// Label to display to the user
|
||||||
|
pub label: Label,
|
||||||
|
pub corner_radius: f64,
|
||||||
|
/// TODO: position the buttons before they get initial bounds
|
||||||
|
/// Position relative to some origin (i.e. parent/row)
|
||||||
|
pub bounds: c::Bounds,
|
||||||
/// current state, shared with other buttons
|
/// current state, shared with other buttons
|
||||||
pub state: Rc<RefCell<KeyState>>,
|
pub state: Rc<RefCell<KeyState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: derive from the style/margin/padding
|
||||||
const BUTTON_SPACING: f64 = 4.0;
|
const BUTTON_SPACING: f64 = 4.0;
|
||||||
const ROW_SPACING: f64 = 7.0;
|
const ROW_SPACING: f64 = 7.0;
|
||||||
|
|
||||||
/// The graphical representation of a row of buttons
|
/// The graphical representation of a row of buttons
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
buttons: Vec<Box<Button>>,
|
pub buttons: Vec<Box<Button>>,
|
||||||
/// Angle is not really used anywhere...
|
/// Angle is not really used anywhere...
|
||||||
angle: i32,
|
pub angle: i32,
|
||||||
bounds: Option<c::Bounds>,
|
/// Position relative to some origin (i.e. parent/view origin)
|
||||||
|
pub bounds: Option<c::Bounds>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Row {
|
impl Row {
|
||||||
@ -621,20 +627,41 @@ impl Row {
|
|||||||
};
|
};
|
||||||
Size { width: total_width, height: max_height }
|
Size { width: total_width, height: max_height }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds the first button that covers the specified point
|
||||||
|
/// relative to row's position's origin
|
||||||
|
fn find_button_by_position(&mut self, point: c::Point)
|
||||||
|
-> Option<&mut Box<Button>>
|
||||||
|
{
|
||||||
|
let row_bounds = self.bounds.as_ref().expect("Missing bounds on row");
|
||||||
|
let origin = c::Point {
|
||||||
|
x: row_bounds.x,
|
||||||
|
y: row_bounds.y,
|
||||||
|
};
|
||||||
|
let angle = self.angle;
|
||||||
|
self.buttons.iter_mut().find(|button| {
|
||||||
|
let bounds = button.bounds.clone();
|
||||||
|
let point = point.clone();
|
||||||
|
let origin = origin.clone();
|
||||||
|
procedures::is_point_inside(bounds, point, origin, angle)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct View {
|
pub struct View {
|
||||||
/// Position relative to keyboard origin
|
/// Position relative to keyboard origin
|
||||||
bounds: c::Bounds,
|
pub bounds: c::Bounds,
|
||||||
rows: Vec<Box<Row>>,
|
pub rows: Vec<Box<Row>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
/// Determines the positions of rows based on their sizes
|
/// Determines the positions of rows based on their sizes
|
||||||
/// Each row will be centered horizontally
|
/// Each row will be centered horizontally
|
||||||
/// The collection of rows will not be altered vertically
|
/// The collection of rows will not be altered vertically
|
||||||
/// (TODO: make view bounds a constraint,
|
/// (TODO: maybe make view bounds a constraint,
|
||||||
/// and derive a scaling factor that lets contents fit into view)
|
/// and derive a scaling factor that lets contents fit into view)
|
||||||
|
/// (or TODO: blow up view bounds to match contents
|
||||||
|
/// and then scale the entire thing)
|
||||||
fn calculate_row_positions(&self, sizes: Vec<Size>) -> Vec<c::Bounds> {
|
fn calculate_row_positions(&self, sizes: Vec<Size>) -> Vec<c::Bounds> {
|
||||||
let mut y_offset = self.bounds.y;
|
let mut y_offset = self.bounds.y;
|
||||||
sizes.into_iter().map(|size| {
|
sizes.into_iter().map(|size| {
|
||||||
@ -676,10 +703,47 @@ impl View {
|
|||||||
for (mut button, button_position)
|
for (mut button, button_position)
|
||||||
in row.buttons.iter_mut()
|
in row.buttons.iter_mut()
|
||||||
.zip(button_positions) {
|
.zip(button_positions) {
|
||||||
button.bounds = Some(button_position);
|
button.bounds = button_position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds the first button that covers the specified point
|
||||||
|
/// relative to view's position's origin
|
||||||
|
fn find_button_by_position(&mut self, point: c::Point)
|
||||||
|
-> Option<&mut Box<Button>>
|
||||||
|
{
|
||||||
|
// make point relative to the inside of the view,
|
||||||
|
// which is the origin of all rows
|
||||||
|
let point = c::Point {
|
||||||
|
x: point.x - self.bounds.x,
|
||||||
|
y: point.y - self.bounds.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.rows.iter_mut().find_map(
|
||||||
|
|row| row.find_button_by_position(point.clone())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Layout {
|
||||||
|
pub current_view: String,
|
||||||
|
pub views: HashMap<String, Box<View>>,
|
||||||
|
// TODO: move to ::keyboard::Keyboard
|
||||||
|
pub keymap_str: CString,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NoSuchView;
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
|
||||||
|
if self.views.contains_key(&view) {
|
||||||
|
self.current_view = view;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(NoSuchView)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod procedures {
|
mod procedures {
|
||||||
@ -703,4 +767,17 @@ mod procedures {
|
|||||||
row_paths.into_iter()
|
row_paths.into_iter()
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if point is inside bounds which are rotated by angle.
|
||||||
|
/// FIXME: what's origin about?
|
||||||
|
pub fn is_point_inside(
|
||||||
|
bounds: c::Bounds,
|
||||||
|
point: c::Point,
|
||||||
|
origin: c::Point,
|
||||||
|
angle: i32
|
||||||
|
) -> bool {
|
||||||
|
(unsafe {
|
||||||
|
c::procedures::eek_are_bounds_inside(bounds, point, origin, angle)
|
||||||
|
}) == 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/lib.rs
13
src/lib.rs
@ -1,9 +1,16 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod bitflags;
|
extern crate bitflags;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate maplit;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate xkbcommon;
|
||||||
|
|
||||||
mod float_ord;
|
pub mod data;
|
||||||
mod imservice;
|
pub mod float_ord;
|
||||||
|
pub mod imservice;
|
||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod layout;
|
mod layout;
|
||||||
|
mod resources;
|
||||||
mod symbol;
|
mod symbol;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod xdg;
|
||||||
|
|||||||
@ -20,16 +20,13 @@ sources = [
|
|||||||
'../eek/eek-gtk-keyboard.c',
|
'../eek/eek-gtk-keyboard.c',
|
||||||
'../eek/eek-keyboard.c',
|
'../eek/eek-keyboard.c',
|
||||||
'../eek/eek-keyboard-drawing.c',
|
'../eek/eek-keyboard-drawing.c',
|
||||||
'../eek/eek-keysym.c',
|
|
||||||
'../eek/eek-layout.c',
|
'../eek/eek-layout.c',
|
||||||
'../eek/eek-renderer.c',
|
'../eek/eek-renderer.c',
|
||||||
'../eek/eek-section.c',
|
|
||||||
'../eek/eek-types.c',
|
'../eek/eek-types.c',
|
||||||
'../eek/eek-xml-layout.c',
|
'../eek/eek-xml-layout.c',
|
||||||
'../eek/layersurface.c',
|
'../eek/layersurface.c',
|
||||||
dbus_src,
|
dbus_src,
|
||||||
enums,
|
enums,
|
||||||
keysym_entries,
|
|
||||||
'../eekboard/key-emitter.c',
|
'../eekboard/key-emitter.c',
|
||||||
'../eekboard/eekboard-context-service.c',
|
'../eekboard/eekboard-context-service.c',
|
||||||
'../eekboard/eekboard-context.c',
|
'../eekboard/eekboard-context.c',
|
||||||
@ -56,25 +53,25 @@ deps = [
|
|||||||
# dependency('libxklavier'), # FIXME remove
|
# dependency('libxklavier'), # FIXME remove
|
||||||
]
|
]
|
||||||
|
|
||||||
# Replacement for eekboard-server
|
rslibs = custom_target(
|
||||||
rslib = static_library(
|
'rslibs',
|
||||||
'rslib',
|
build_by_default: true,
|
||||||
sources: ['lib.rs'],
|
build_always_stale: true,
|
||||||
rust_crate_type: 'staticlib'
|
output: ['librs.a'],
|
||||||
|
install: false,
|
||||||
|
console: true,
|
||||||
|
command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@', 'build']
|
||||||
)
|
)
|
||||||
|
|
||||||
rstests = executable(
|
test(
|
||||||
'rstests',
|
'rstest',
|
||||||
sources: ['lib.rs'],
|
cargo_script,
|
||||||
rust_args: ['--test'],
|
args: [meson.source_root(), '', 'test']
|
||||||
install: false
|
|
||||||
)
|
)
|
||||||
|
|
||||||
test('rstests', rstests)
|
|
||||||
|
|
||||||
libsqueekboard = static_library('libsqueekboard',
|
libsqueekboard = static_library('libsqueekboard',
|
||||||
sources,
|
sources,
|
||||||
link_with: rslib,
|
link_with: [rslibs],
|
||||||
include_directories: [include_directories('..'), include_directories('../eek')],
|
include_directories: [include_directories('..'), include_directories('../eek')],
|
||||||
dependencies: deps,
|
dependencies: deps,
|
||||||
c_args: [
|
c_args: [
|
||||||
|
|||||||
24
src/resources.rs
Normal file
24
src/resources.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*! Statically linked resources.
|
||||||
|
* This could be done using GResource, but that would need additional work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const KEYBOARDS: &[(*const str, *const str)] = &[
|
||||||
|
("us", include_str!("../data/keyboards/us.yaml")),
|
||||||
|
("nb", include_str!("../data/keyboards/nb.yaml")),
|
||||||
|
("number", include_str!("../data/keyboards/number.yaml")),
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn get_keyboard(needle: &str) -> Option<&'static str> {
|
||||||
|
// Need to dereference in unsafe code
|
||||||
|
// comparing *const str to &str will compare pointers
|
||||||
|
KEYBOARDS.iter()
|
||||||
|
.find(|(name, _)| {
|
||||||
|
let name: *const str = *name;
|
||||||
|
(unsafe { &*name }) == needle
|
||||||
|
})
|
||||||
|
.map(|(_, value)| {
|
||||||
|
let value: *const str = *value;
|
||||||
|
unsafe { &*value }
|
||||||
|
})
|
||||||
|
}
|
||||||
24
src/symbol.h
24
src/symbol.h
@ -1,24 +0,0 @@
|
|||||||
#ifndef __SYMBOL_H
|
|
||||||
#define __SYMBOL_H
|
|
||||||
|
|
||||||
#include "stdbool.h"
|
|
||||||
#include "inttypes.h"
|
|
||||||
// Defined in Rust
|
|
||||||
|
|
||||||
struct squeek_symbol;
|
|
||||||
struct squeek_symbols;
|
|
||||||
|
|
||||||
void squeek_symbols_add(struct squeek_symbols*,
|
|
||||||
const char *element_name,
|
|
||||||
const char *text, uint32_t keyval,
|
|
||||||
const char *label, const char *icon,
|
|
||||||
const char *tooltip);
|
|
||||||
|
|
||||||
|
|
||||||
const char *squeek_symbol_get_name(struct squeek_symbol* symbol);
|
|
||||||
const char *squeek_symbol_get_label(struct squeek_symbol* symbol);
|
|
||||||
const char *squeek_symbol_get_icon_name(struct squeek_symbol* symbol);
|
|
||||||
uint32_t squeek_symbol_get_modifier_mask(struct squeek_symbol* symbol);
|
|
||||||
|
|
||||||
void squeek_symbol_print(struct squeek_symbol* symbol);
|
|
||||||
#endif
|
|
||||||
128
src/symbol.rs
128
src/symbol.rs
@ -1,138 +1,46 @@
|
|||||||
|
/*! The symbol object, defining actions that the key can do when activated */
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
/// Name of the keysym
|
||||||
/// Gathers stuff defined in C or called by C
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub mod c {
|
pub struct KeySym(pub String);
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
// The following defined in C
|
|
||||||
|
|
||||||
// Legacy; Will never be used in Rust as a bit field
|
|
||||||
enum ModifierMask {
|
|
||||||
Nothing = 0,
|
|
||||||
Shift = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following defined in Rust.
|
|
||||||
|
|
||||||
// TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
|
|
||||||
// Symbols are owned by Rust and will move towards no C manipulation, so it may make sense not to wrap them
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_symbol_get_name(symbol: *const Symbol) -> *const c_char {
|
|
||||||
let symbol = unsafe { &*symbol };
|
|
||||||
match &symbol.action {
|
|
||||||
Action::Submit { text: Some(text), .. } => text.as_ptr(),
|
|
||||||
_ => ptr::null(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_symbol_get_label(symbol: *const Symbol) -> *const c_char {
|
|
||||||
let symbol = unsafe { &*symbol };
|
|
||||||
match &symbol.label {
|
|
||||||
Label::Text(text) => text.as_ptr(),
|
|
||||||
// returning static strings to C is a bit cumbersome
|
|
||||||
Label::IconName(_) => unsafe {
|
|
||||||
CStr::from_bytes_with_nul_unchecked(b"icon\0")
|
|
||||||
}.as_ptr(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_symbol_get_icon_name(symbol: *const Symbol) -> *const c_char {
|
|
||||||
let symbol = unsafe { &*symbol };
|
|
||||||
match &symbol.label {
|
|
||||||
Label::Text(_) => ptr::null(),
|
|
||||||
Label::IconName(name) => name.as_ptr(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Legacy; throw away
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_symbol_get_modifier_mask(symbol: *const Symbol) -> u32 {
|
|
||||||
let symbol = unsafe { &*symbol };
|
|
||||||
(match &symbol.action {
|
|
||||||
Action::SetLevel(1) => ModifierMask::Shift,
|
|
||||||
_ => ModifierMask::Nothing,
|
|
||||||
}) as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_symbol_print(symbol: *const Symbol) {
|
|
||||||
let symbol = unsafe { &*symbol };
|
|
||||||
println!("{:?}", symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Just defines some int->identifier mappings for convenience
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum KeySym {
|
|
||||||
Unknown = 0,
|
|
||||||
Shift = 0xffe1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeySym {
|
|
||||||
pub fn from_u32(num: u32) -> KeySym {
|
|
||||||
match num {
|
|
||||||
0xffe1 => KeySym::Shift,
|
|
||||||
_ => KeySym::Unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct XKeySym(pub u32);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Label {
|
|
||||||
/// Text used to display the symbol
|
|
||||||
Text(CString),
|
|
||||||
/// Icon name used to render the symbol
|
|
||||||
IconName(CString),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use to switch layouts
|
/// Use to switch layouts
|
||||||
type Level = u8;
|
type Level = String;
|
||||||
|
|
||||||
/// Use to send modified keypresses
|
/// Use to send modified keypresses
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Modifier {
|
pub enum Modifier {
|
||||||
Control,
|
Control,
|
||||||
Alt,
|
Alt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Action to perform on the keypress and, in reverse, on keyrelease
|
/// Action to perform on the keypress and, in reverse, on keyrelease
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Switch to this level TODO: reverse?
|
/// Switch to this view
|
||||||
SetLevel(Level),
|
SetLevel(Level),
|
||||||
|
/// Switch to a view and latch
|
||||||
|
LockLevel {
|
||||||
|
lock: Level,
|
||||||
|
/// When unlocked by pressing it or emitting a key
|
||||||
|
unlock: Level,
|
||||||
|
},
|
||||||
/// Set this modifier TODO: release?
|
/// Set this modifier TODO: release?
|
||||||
SetModifier(Modifier),
|
SetModifier(Modifier),
|
||||||
/// Submit some text
|
/// Submit some text
|
||||||
Submit {
|
Submit {
|
||||||
/// orig: Canonical name of the symbol
|
/// Text to submit with input-method
|
||||||
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<XKeySym>,
|
keys: Vec<KeySym>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains a static description of a particular key's actions
|
/// Contains a static description of a particular key's actions
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Symbol {
|
pub struct Symbol {
|
||||||
/// The action that this key performs
|
/// The action that this key performs
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
/// Label to display to the user
|
|
||||||
pub label: Label,
|
|
||||||
// FIXME: is it used?
|
|
||||||
pub tooltip: Option<CString>,
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
src/util.rs
57
src/util.rs
@ -1,9 +1,14 @@
|
|||||||
pub mod c {
|
pub mod c {
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::ffi::{ CStr, CString };
|
use std::ffi::{ CStr, CString };
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
|
// traits
|
||||||
|
|
||||||
|
use std::borrow::ToOwned;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
|
pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
|
||||||
if s.is_null() {
|
if s.is_null() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -13,7 +18,7 @@ pub mod c {
|
|||||||
.map(Some)
|
.map(Some)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_cstr(s: &*const c_char) -> Option<&CStr> {
|
pub fn as_cstr(s: &*const c_char) -> Option<&CStr> {
|
||||||
if s.is_null() {
|
if s.is_null() {
|
||||||
None
|
None
|
||||||
@ -47,4 +52,52 @@ pub mod c {
|
|||||||
assert_eq!(as_str(&ptr::null()), Ok(None))
|
assert_eq!(as_str(&ptr::null()), Ok(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wraps structures to pass them safely to/from C
|
||||||
|
/// Since C doesn't respect borrowing rules,
|
||||||
|
/// RefCell will enforce them dynamically (only 1 writer/many readers)
|
||||||
|
/// Rc is implied and will ensure timely dropping
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Wrapped<T: Clone>(*const RefCell<T>);
|
||||||
|
|
||||||
|
// It would be nice to implement `Borrow`
|
||||||
|
// directly on the raw pointer to avoid one conversion call,
|
||||||
|
// but the `borrow()` call needs to extract a `Rc`,
|
||||||
|
// and at the same time return a reference to it (`std::cell::Ref`)
|
||||||
|
// to take advantage of `Rc<RefCell>::borrow()` runtime checks.
|
||||||
|
// Unfortunately, that needs a `Ref` struct with self-referential fields,
|
||||||
|
// which is a bit too complex for now.
|
||||||
|
|
||||||
|
impl<T: Clone> Wrapped<T> {
|
||||||
|
pub fn wrap(state: Rc<RefCell<T>>) -> Wrapped<T> {
|
||||||
|
Wrapped(Rc::into_raw(state))
|
||||||
|
}
|
||||||
|
/// Extracts the reference to the data.
|
||||||
|
/// It may cause problems if attempted in more than one place
|
||||||
|
pub unsafe fn unwrap(self) -> Rc<RefCell<T>> {
|
||||||
|
Rc::from_raw(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Rc reference to the same data
|
||||||
|
pub fn clone_ref(&self) -> Rc<RefCell<T>> {
|
||||||
|
// A bit dangerous: the Rc may be in use elsewhere
|
||||||
|
let used_rc = unsafe { Rc::from_raw(self.0) };
|
||||||
|
let rc = used_rc.clone();
|
||||||
|
Rc::into_raw(used_rc); // prevent dropping the original reference
|
||||||
|
rc
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of the underlying data
|
||||||
|
pub fn to_owned(&self) -> T {
|
||||||
|
let rc = self.clone_ref();
|
||||||
|
let r = rc.borrow();
|
||||||
|
r.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Clone for Wrapped<T> {
|
||||||
|
fn clone(&self) -> Wrapped<T> {
|
||||||
|
Wrapped::wrap(self.clone_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
src/xdg.rs
Normal file
63
src/xdg.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*! XDG directory handling. */
|
||||||
|
|
||||||
|
/* Based on dirs-sys https://github.com/soc/dirs-sys-rs
|
||||||
|
* by "Simon Ochsenreither <simon@ochsenreither.de>",
|
||||||
|
* Licensed under either of
|
||||||
|
|
||||||
|
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
*
|
||||||
|
* Based on dirs https://github.com/soc/dirs-rs
|
||||||
|
* by "Simon Ochsenreither <simon@ochsenreither.de>",
|
||||||
|
* Licensed under either of
|
||||||
|
|
||||||
|
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
*
|
||||||
|
* Based on xdg https://github.com/whitequark/rust-xdg
|
||||||
|
* by "Ben Longbons <b.r.longbons@gmail.com>",
|
||||||
|
* "whitequark <whitequark@whitequark.org>",
|
||||||
|
rust-xdg is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
|
||||||
|
*
|
||||||
|
* The above crates were used to get
|
||||||
|
* a version without all the excessive dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::path::{ Path, PathBuf };
|
||||||
|
|
||||||
|
|
||||||
|
fn is_absolute_path(path: OsString) -> Option<PathBuf> {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
if path.is_absolute() {
|
||||||
|
Some(path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn home_dir() -> Option<PathBuf> {
|
||||||
|
return env::var_os("HOME")
|
||||||
|
.and_then(|h| if h.is_empty() { None } else { Some(h) })
|
||||||
|
.map(PathBuf::from);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_dir() -> Option<PathBuf> {
|
||||||
|
env::var_os("XDG_DATA_HOME")
|
||||||
|
.and_then(is_absolute_path)
|
||||||
|
.or_else(|| home_dir().map(|h| h.join(".local/share")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path to the directory within the data dir
|
||||||
|
pub fn data_path<P>(path: P) -> Option<PathBuf>
|
||||||
|
where P: AsRef<Path>
|
||||||
|
{
|
||||||
|
data_dir().map(|dir| {
|
||||||
|
dir.join(path.as_ref())
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
#include "eek/eek.h"
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
static void
|
|
||||||
test_create (void)
|
|
||||||
{
|
|
||||||
EekBounds bounds = {0};
|
|
||||||
struct squeek_view *view = squeek_view_new(bounds);
|
|
||||||
struct squeek_button *button0, *button1;
|
|
||||||
|
|
||||||
struct squeek_row *row = squeek_view_create_row (view, 0);
|
|
||||||
g_assert (row);
|
|
||||||
button0 = squeek_row_create_button (row, 1, 0);
|
|
||||||
g_assert (button0);
|
|
||||||
button1 = squeek_row_create_button (row, 2, 0);
|
|
||||||
g_assert (button1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
g_test_init (&argc, &argv, NULL);
|
|
||||||
g_test_add_func ("/eek-simple-test/create", test_create);
|
|
||||||
return g_test_run ();
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* For gdk_x11_display_get_xdisplay(). See main(). */
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "eek/eek.h"
|
|
||||||
#include "eek/eek-xml-layout.h"
|
|
||||||
|
|
||||||
static void
|
|
||||||
test_output_parse (void)
|
|
||||||
{
|
|
||||||
EekLayout *layout;
|
|
||||||
LevelKeyboard *keyboard;
|
|
||||||
GError *error;
|
|
||||||
|
|
||||||
error = NULL;
|
|
||||||
layout = eek_xml_layout_new ("us", &error);
|
|
||||||
g_assert_no_error (error);
|
|
||||||
|
|
||||||
/* We don't need the context service to parse an XML file, so we can pass
|
|
||||||
NULL when creating a keyboard. */
|
|
||||||
keyboard = eek_xml_layout_real_create_keyboard(layout, NULL);
|
|
||||||
g_object_unref (layout);
|
|
||||||
level_keyboard_free(keyboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
g_test_init (&argc, &argv, NULL);
|
|
||||||
|
|
||||||
g_test_add_func ("/eek-xml-test/output-parse", test_output_parse);
|
|
||||||
|
|
||||||
return g_test_run ();
|
|
||||||
}
|
|
||||||
41
tests/entry.py
Normal file
41
tests/entry.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import gi
|
||||||
|
import sys
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
|
||||||
|
from gi.repository import Gtk
|
||||||
|
|
||||||
|
class App(Gtk.Application):
|
||||||
|
|
||||||
|
purposes = [
|
||||||
|
("Free form", Gtk.InputPurpose.FREE_FORM),
|
||||||
|
("Alphabetical", Gtk.InputPurpose.ALPHA),
|
||||||
|
("Digits", Gtk.InputPurpose.DIGITS),
|
||||||
|
("Number", Gtk.InputPurpose.NUMBER),
|
||||||
|
("Phone", Gtk.InputPurpose.PHONE),
|
||||||
|
("URL", Gtk.InputPurpose.URL),
|
||||||
|
("E-mail", Gtk.InputPurpose.EMAIL),
|
||||||
|
("Name", Gtk.InputPurpose.NAME),
|
||||||
|
("Password", Gtk.InputPurpose.PASSWORD),
|
||||||
|
("PIN", Gtk.InputPurpose.PIN)
|
||||||
|
]
|
||||||
|
|
||||||
|
def do_activate(self):
|
||||||
|
w = Gtk.ApplicationWindow(application=self)
|
||||||
|
grid = Gtk.Grid(orientation='vertical', column_spacing=8, row_spacing=8)
|
||||||
|
i = 0
|
||||||
|
for text, purpose in self.purposes:
|
||||||
|
|
||||||
|
l = Gtk.Label(label=text)
|
||||||
|
e = Gtk.Entry(hexpand=True)
|
||||||
|
e.set_input_purpose(purpose)
|
||||||
|
grid.attach(l, 0, i, 1, 1)
|
||||||
|
grid.attach(e, 1, i, 1, 1)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
w.add(grid)
|
||||||
|
w.show_all()
|
||||||
|
|
||||||
|
app = App()
|
||||||
|
app.run(sys.argv)
|
||||||
17
tests/layout.yaml
Normal file
17
tests/layout.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
bounds:
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "test"
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
test:
|
||||||
|
label: "test"
|
||||||
12
tests/layout2.yaml
Normal file
12
tests/layout2.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
# missing views
|
||||||
|
bounds:
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
16
tests/layout3.yaml
Normal file
16
tests/layout3.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# extra field
|
||||||
|
bounds:
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "test"
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
|
bad_field: false
|
||||||
18
tests/layout_key1.yaml
Normal file
18
tests/layout_key1.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# punctuation
|
||||||
|
bounds:
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "."
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
".":
|
||||||
|
label: "test"
|
||||||
18
tests/layout_key2.yaml
Normal file
18
tests/layout_key2.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# punctuation
|
||||||
|
bounds:
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "å"
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
corner_radius: 1
|
||||||
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
å:
|
||||||
|
label: "test"
|
||||||
@ -19,13 +19,10 @@ test_link_args = [
|
|||||||
'-fPIC',
|
'-fPIC',
|
||||||
]
|
]
|
||||||
|
|
||||||
tests = [
|
c_tests = [
|
||||||
'eek-simple-test',
|
|
||||||
'eek-xml-test',
|
|
||||||
'test-keymap-generation'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach name : tests
|
foreach name : c_tests
|
||||||
|
|
||||||
test_sources = [name + '.c']
|
test_sources = [name + '.c']
|
||||||
|
|
||||||
@ -47,4 +44,15 @@ foreach name : tests
|
|||||||
|
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
|
# The layout test is in the examples directory
|
||||||
|
# due to the way Cargo builds executables
|
||||||
|
# and the need to call it manually
|
||||||
|
foreach layout : ['us', 'nb', 'number']
|
||||||
|
test(
|
||||||
|
'test_layout_' + layout,
|
||||||
|
cargo_script,
|
||||||
|
args: [meson.source_root(), '', 'run', '--example', 'test_layout', layout]
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
|
|
||||||
* Copyright (C) 2010-2011 Red Hat, Inc.
|
|
||||||
* Copyright (C) 2019 Purism SPC
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* For gdk_x11_display_get_xdisplay(). See main(). */
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "eek/eek.h"
|
|
||||||
#include "eek/eek-xml-layout.h"
|
|
||||||
|
|
||||||
static void
|
|
||||||
test_check_xkb (void)
|
|
||||||
{
|
|
||||||
EekLayout *layout;
|
|
||||||
LevelKeyboard *keyboard;
|
|
||||||
GError *error;
|
|
||||||
|
|
||||||
error = NULL;
|
|
||||||
layout = eek_xml_layout_new ("us", &error);
|
|
||||||
g_assert_no_error (error);
|
|
||||||
|
|
||||||
keyboard = eek_xml_layout_real_create_keyboard(layout, NULL);
|
|
||||||
gchar *keymap_str = eek_keyboard_get_keymap(keyboard);
|
|
||||||
|
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
||||||
if (!context) {
|
|
||||||
g_error("No context created");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
|
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
||||||
|
|
||||||
free(keymap_str);
|
|
||||||
|
|
||||||
xkb_context_unref(context);
|
|
||||||
if (!keymap) {
|
|
||||||
g_error("Bad keymap");
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_unref (layout);
|
|
||||||
level_keyboard_free(keyboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
g_test_init (&argc, &argv, NULL);
|
|
||||||
|
|
||||||
g_test_add_func ("/test-keymap-generation/check-xkb", test_check_xkb);
|
|
||||||
|
|
||||||
return g_test_run ();
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user