Compare commits

..

89 Commits

Author SHA1 Message Date
0f5c5ef10f Bump version number 2019-09-24 10:46:41 +00:00
6e183ccb13 cargo: Update lock 2019-09-24 10:46:21 +00:00
af0137a4fc Merge branch 'test_deb' into 'master'
ci: Build .deb

Closes #108

See merge request Librem5/squeekboard!169
2019-09-23 13:35:53 +00:00
bb3c26b0d8 ci: Build .deb 2019-09-23 13:06:45 +00:00
6dcea4599f Merge branch 'lintian' into 'master'
debian: Silence false positive in Lintian

Closes #107

See merge request Librem5/squeekboard!170
2019-09-23 12:52:22 +00:00
d32749d533 debian: Silence false positive in Lintian
Needed to have working repository builds
2019-09-23 10:48:40 +00:00
36306f2eea Merge branch 'backslash' into 'master'
us: Escape backslash character

See merge request Librem5/squeekboard!168
2019-09-22 21:32:05 +00:00
34a4c6ffb5 us: Escape backslash character
This fixes backslash being missing from the symbols view.
2019-09-22 00:30:35 +02:00
74e75d2dae Merge branch 'fixbuild' into 'master'
Fix Cargo interaction with Debian build system

See merge request Librem5/squeekboard!162
2019-09-20 19:29:13 +00:00
a3e421db3d build: Fix Debian Cargo.toml mismatch
Debian uses a separate registry for the packages it distributes. Checksums for some Debian packages don't match anything that's available on crates.io, which is the default source of dependencies. *linked-hash-map* in particular doesn't provide any hash.

As a result, Debian's `Cargo.lock` and crates.io's `Cargo.lock` are not matching, and building is only possible with one or the other, depending on what's checked in.

As a separate issue, Debian packages are usually not checked in in multiple versions, so checking in Debian's `Cargo.lock` would result in the package not building whenever a bugfix is distributed (due to checksum changes).

This change removes the crates.io `Cargo.lock` so that a new one will be created whenever a .deb is built, solving the above. What keeps falsely passing builds from happening is `Cargo.toml` specifying no interface changes, as well as Build-Depends, which seem enough for any other Debian package.
2019-09-20 09:52:36 +00:00
241e5c0fc6 debian: Use Debian versions of dependencies 2019-09-20 09:40:18 +00:00
28e0c26671 build: Change dependencies to match Debian packages 2019-09-20 09:40:18 +00:00
7d0070a155 debian: Use CARGO_HOME more like librsvg does 2019-09-20 09:37:46 +00:00
9093226abe Merge branch 'update-symbols-button' into 'master'
Update symbols button to reflect the symbols view

See merge request Librem5/squeekboard!166
2019-09-19 14:48:58 +00:00
45dc51f08f Update symbols button to reflect the symbols view 2019-09-16 17:07:41 +02:00
b486dc8afd Merge branch 'testing' into 'master'
tests: Describe how to test

See merge request Librem5/squeekboard!165
2019-09-13 19:55:25 +00:00
e70a64a47e Merge branch 'cleanups' into 'master'
Cleanups

See merge request Librem5/squeekboard!164
2019-09-13 19:52:19 +00:00
225b243446 tests: Describe how to test 2019-09-13 15:55:26 +00:00
db994da531 keysyms: Remove dead code 2019-09-13 15:25:48 +00:00
d47aff357b layout: Remove dead code 2019-09-13 15:21:29 +00:00
96c2c2dd1f Merge branch 'test_layouts' into 'master'
Test layouts

See merge request Librem5/squeekboard!163
2019-09-13 15:12:46 +00:00
c8cc5b1997 layout: Bundle number layout 2019-09-13 09:11:20 +00:00
edb28cb859 tests: Verify all bundled layouts 2019-09-13 09:09:17 +00:00
b07689939b Merge branch 'fixbuild' into 'master'
build: Fix cargo behaviour

See merge request Librem5/squeekboard!161
2019-09-12 11:32:16 +00:00
6072e5768a build: Fix cargo behaviour
Cargo caused .deb builds to crash by storing its data in $HOME.

https://www.debian.org/doc/debian-policy/ch-source.html#main-building-script-debian-rules says TMP_DIR may be used freely, so that's where Cargo will keep its stuff now.
2019-09-12 11:26:03 +00:00
fe8d66a635 Merge branch 'autokeysym' into 'master'
Unicode key name -> keysym conversion

See merge request Librem5/squeekboard!160
2019-09-11 15:20:44 +00:00
c8658b00e3 Merge branch 'warnings' into 'master'
warnings: Fix some C stuff

See merge request Librem5/squeekboard!159
2019-09-11 14:58:19 +00:00
0989771a3b Merge branch 'simple_cargo' into 'master'
cargo: Allow for manual cargo invokations

See merge request Librem5/squeekboard!158
2019-09-11 13:34:42 +00:00
9c2acde826 Merge branch 'reparse' into 'master'
rewrite of the parsing

See merge request Librem5/squeekboard!152
2019-09-11 12:33:25 +00:00
2352e31f01 us: Simplify by using Unicode directly 2019-09-11 12:03:57 +00:00
8e654346a2 keysyms: Derive from Unicode labels 2019-09-11 12:03:57 +00:00
015ba79f65 Merge branch 'fix_tests' into reparse 2019-09-11 12:03:24 +00:00
d6aa54f30c Merge branch 'fix_tests' into 'master'
Fix doctests

See merge request Librem5/squeekboard!157
2019-09-11 11:55:26 +00:00
fd0d8d4244 warnings: Fix some C stuff 2019-09-10 11:06:18 +00:00
c725cd7f14 cargo: Allow for manual cargo invokations 2019-09-10 10:17:28 +00:00
0922d4a87a tests: Allow the building of doctests
Also allows for standalone tests and examples to be built and run with Cargo.
2019-09-10 09:34:25 +00:00
51562d5185 Fix imservice docstring 2019-09-10 09:27:21 +00:00
ed8b6eec28 More float_ord docs 2019-09-10 09:27:21 +00:00
e9c236a682 float_ord: Fix test 2019-09-10 09:27:19 +00:00
04a47ad0af Merge branch '33-multi-touch-typing-not-yet-working' into 'master'
Resolve "Multi-touch typing not-yet working"

Closes #33

See merge request Librem5/squeekboard!135
2019-09-10 09:15:50 +00:00
99c577be60 Merge remote-tracking branch 'upstream/master' into reparse 2019-09-10 09:11:57 +00:00
db8340181f Merge branch '88-visual-improvements' into 'master'
Resolve colors used in "Visual Improvements"

See merge request Librem5/squeekboard!155
2019-09-10 08:16:57 +00:00
4f18ffd34e Resolve colors used in "Visual Improvements" 2019-09-10 08:16:56 +00:00
4306ec9c1e Merge branch 'drop_bitflags' into 'master'
deps: Remove bitflags

See merge request Librem5/squeekboard!156
2019-09-09 20:16:44 +00:00
31c12e5182 layout: Convert numbers to yaml 2019-09-09 19:10:42 +00:00
521796a46d deps: Remove bitflags
This removes the need to use a modified copy and makes running tests easier.
2019-09-09 19:00:40 +00:00
a187221d3f Convert nb layout to yaml 2019-09-09 16:35:21 +00:00
583b546e81 Fix CI error 2019-09-09 15:49:25 +00:00
aa9523338f Merge branch 'buttonlists' into reparse 2019-09-09 15:11:59 +00:00
0ed66e0eab locked: Use keys instead of buttons 2019-09-09 13:54:55 +00:00
6523275b6a views: Change based on layout file 2019-09-09 13:25:03 +00:00
b9e9ca368a Merge branch 'cargo' into 'master'
rust: Use Cargo to build the Rust portion

See merge request Librem5/squeekboard!147
2019-09-07 17:52:24 +00:00
c4886e362a Merge branch 'dorota.czaplejewicz/squeekboard-cargo' into 'cargo'
Add new meson and current ninja to the build dependencies

See merge request dorota.czaplejewicz/squeekboard!8
2019-09-07 11:37:42 +00:00
fc5f671e57 Add new meson and current ninja to the dependencies 2019-09-06 22:15:02 +02:00
035ecd6df1 Merge branch 'clean' into 'master'
cleanup: Vala bindings

See merge request Librem5/squeekboard!154
2019-09-04 12:46:42 +00:00
26d1a6047c Merge branch 'fixes' into reparse 2019-09-04 10:18:53 +00:00
60a89b6c3f Merge branch 'wrapping' into reparse 2019-09-04 10:01:22 +00:00
b84c402c4a WIP
WIP

WIP: keymap generation test passes

meta: Update features and version

WiP: cargo.lock

WIP: don't crash

WIP: no outlines

parsing: New tests

WIP: base level works

WIP: remove old keyboard

symbols correctly input

WIP: lodaing files

WIP: fallback works

Valid fallback
2019-09-04 09:44:31 +00:00
2579d2fea9 cleanup: Vala bindings 2019-09-04 09:21:55 +00:00
c75ed9b230 Merge branch 'kill_tooltip' into 'master'
tooltips: Remove

See merge request Librem5/squeekboard!150
2019-09-02 14:58:02 +00:00
789e8b6bff Merge branch 'buttonlists' into 'master'
Buttonlists

See merge request Librem5/squeekboard!145
2019-09-02 14:42:19 +00:00
633d15c438 Merge branch 'fixes' into 'master'
layout: Remove unused C functions

See merge request Librem5/squeekboard!151
2019-09-02 11:24:54 +00:00
baabcb1400 layout: Remove unused C functions 2019-09-01 11:42:02 +00:00
c16bbb9e7f Merge branch 'fix-number-layout' into 'master'
Adjust width and height of keypad geometry

See merge request Librem5/squeekboard!149
2019-08-31 12:31:45 +00:00
623181cc34 tooltips: Remove 2019-08-31 12:18:55 +00:00
76b5104fb7 Merge branch 'improve-styling' into 'master'
Apply symbol names to widget paths, add styles

See merge request Librem5/squeekboard!148
2019-08-31 12:16:03 +00:00
6c0a642abf Store key instead of button in pressed lists 2019-08-31 11:30:51 +00:00
132435a9c8 Drop callback iteration for button finding 2019-08-31 11:30:51 +00:00
521bcfc484 Adjust width and height of keypad geometry 2019-08-30 15:16:46 +00:00
3413021d30 rust: Use Cargo to build the Rust portion
Pros: Ability to use Rust libraries from crates.io
Problems: Need to lock library versions and document their reproducible building, either via Cargo.lock or vendoring.
2019-08-30 09:00:34 +00:00
ffc64c6d56 Apply symbol names to widget paths, add styles 2019-08-29 21:02:35 +00:00
79672f3a2d Merge branch 'nodisplay' into 'master'
desktop file: Use NoDisplay=true

See merge request Librem5/squeekboard!143
2019-08-29 16:18:55 +00:00
e1d5731466 Merge branch 'wrapping' into 'master'
ffi: Use a generic wrapper for opaque Rust structs

See merge request Librem5/squeekboard!144
2019-08-29 15:37:57 +00:00
09deef2d6c Merge branch 'fix-formatting' into 'master'
Fix code formatting

See merge request Librem5/squeekboard!146
2019-08-29 15:03:14 +00:00
83907af456 Fix code formatting 2019-08-29 14:52:10 +00:00
878b7ed18e ffi: Use a generic wrapper for opaque Rust structs 2019-08-29 13:33:04 +00:00
e6f3b9e5be Merge branch 'layouts' into 'master'
Layouts

See merge request Librem5/squeekboard!141
2019-08-29 12:19:42 +00:00
75992ff13f Check for button position more in Rust
The check against fitting inside the Layout was removed: as an optimization it is unneeded, as the actual search must be optimized to be quick. In addition, the view bounds don't correspond to anything physical as long as negative offsets are allowed.
2019-08-29 12:19:42 +00:00
2d7dddd505 Merge branch '56-key-stays-pressed-when-the-keyboard-hides-during-a-keypress' into 'master'
Resolve "Key stays pressed when the keyboard hides during a keypress"

Closes #56

See merge request Librem5/squeekboard!138
2019-08-29 11:50:16 +00:00
afe0ed1674 Merge branch 'adjust-extended-layout-height' into 'master'
Adjust height of the extended keyboard layout

See merge request Librem5/squeekboard!142
2019-08-29 10:20:26 +00:00
b9ab4288d7 Try releasing old touches and only letting new ones be dragged 2019-08-28 15:53:57 +00:00
d816cc261a desktop file: Use NoDisplay=true
There's no point in having the keyboard in the list of installed
applications and it has no icon either.
2019-08-28 15:41:38 +02:00
2e2ae96114 Adjust height of the extended keyboard layout 2019-08-28 14:34:20 +02:00
ec7e7c3f8b Merge branch 'iters' into 'master'
layout: Place items using simple loops

See merge request Librem5/squeekboard!140
2019-08-28 12:31:59 +00:00
5551ed2bd2 Merge branch 'remove-debugging-code' into 'master'
Remove debugging code

See merge request Librem5/squeekboard!139
2019-08-28 06:31:50 +00:00
0da02aab21 Remove debugging code 2019-08-27 20:24:49 +00:00
1ae8d072a6 Release buttons directly when unmapping the keyboard 2019-08-27 20:04:11 +00:00
e61a3a6fe8 Remove unnecessary assignment 2019-08-26 21:13:04 +00:00
087da5cd9e Enable and respond to all touch events 2019-08-26 20:57:31 +00:00
76 changed files with 2276 additions and 6202 deletions

View File

@ -9,6 +9,10 @@ stages:
- librem5
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 build-dep .
@ -22,6 +26,26 @@ build_meson:
- meson . _build/ -Ddepdatadir=/usr/share
- 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:
<<: *tags
stage: test

162
Cargo.lock generated Normal file
View 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
View 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
View 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
```

View File

@ -3,14 +3,18 @@
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
It squeaks because some Rust got inside.
Features
--------
### Present
- GTK3
- Custom xml-defined keyboards
- Custom yaml-defined keyboards
- DBus interface to show and hide
- Use Wayland input method protocol to show and hide
- Use Wayland virtual keyboard protocol
### Temporarily dropped
@ -18,8 +22,6 @@ Features
### TODO
- Use Wayland virtual keyboard protocol
- Use Wayland text input protocol
- Use Wayland input method protocol
- Pick up DBus interface files from /usr/share
@ -38,31 +40,20 @@ $ cd squeekboard
$ mkdir ../build
$ meson ../build/
$ cd ../build
$ ninja test
$ 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
-------
```
$ rootston
$ phoc # if no compatible Wayland compositor is running yet
$ cd ../build/
$ src/squeekboard
```
### Testing
Developing
----------
```
$ 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
```
See `HACKING.md`

View File

@ -1 +0,0 @@
Eek cheader_filename="eek/eek.h"

View File

@ -1 +0,0 @@
EekGtk cheader_filename="eek/eek-gtk.h"

View File

@ -1 +0,0 @@
EekXkl cheader_filename="eek/eek-xkl.h"

View File

@ -1 +0,0 @@
gio-2.0

View File

@ -1 +0,0 @@
eek-0.90

View File

@ -1,2 +0,0 @@
eek-0.90
x11

23
cargo.sh Normal file
View 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

View File

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

View File

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

View File

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

View File

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

View 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: ")"

View File

@ -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="&#964;">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="&#183;">U00B7</symbol>
<symbol label="t">t</symbol>
<symbol label="T">T</symbol>
<symbol label="5">5</symbol>
<symbol label="&#8730;">squareroot</symbol>
<symbol label="y">y</symbol>
<symbol label="Y">Y</symbol>
<symbol label="6">6</symbol>
<symbol label="&#960;">Greek_pi</symbol>
<symbol label="u">u</symbol>
<symbol label="U">U</symbol>
<symbol label="7">7</symbol>
<symbol label="&#247;">division</symbol>
<symbol label="i">i</symbol>
<symbol label="I">I</symbol>
<symbol label="8">8</symbol>
<symbol label="&#215;">multiply</symbol>
<symbol label="o">o</symbol>
<symbol label="O">O</symbol>
<symbol label="9">9</symbol>
<symbol label="&#182;">paragraph</symbol>
<symbol label="p">p</symbol>
<symbol label="P">P</symbol>
<symbol label="0">0</symbol>
<symbol label="&#9651;">U25B3</symbol>
<symbol keyval="229" label="&#229;">aring</symbol>
<symbol keyval="197" label="&#197;">Aring</symbol>
<symbol label="a">a</symbol>
<symbol label="A">A</symbol>
<symbol label="@">at</symbol>
<symbol label="&#169;">copyright</symbol>
<symbol label="s">s</symbol>
<symbol label="S">S</symbol>
<symbol label="#">numbersign</symbol>
<symbol label="&#174;">U00AE</symbol>
<symbol label="d">d</symbol>
<symbol label="D">D</symbol>
<symbol label="$">dollar</symbol>
<symbol label="&#163;">U00A3</symbol>
<symbol label="f">f</symbol>
<symbol label="F">F</symbol>
<symbol label="%">percent</symbol>
<symbol label="&#8364;">EuroSign</symbol>
<symbol label="g">g</symbol>
<symbol label="G">G</symbol>
<symbol label="&amp;">ampersand</symbol>
<symbol label="&#165;">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="&#176;">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="&#248;">oslash</symbol>
<symbol keyval="216" label="&#216;">Oslash</symbol>
<symbol label=")">parenright</symbol>
<symbol label="}">braceright</symbol>
<symbol keyval="230" label="&#230;">ae</symbol>
<symbol keyval="198" label="&#198;">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="&quot;">quotedbl</symbol>
<symbol label="/">slash</symbol>
<symbol label="c">c</symbol>
<symbol label="C">C</symbol>
<symbol label="'">quoteright</symbol>
<symbol label="&lt;">less</symbol>
<symbol label="v">v</symbol>
<symbol label="V">V</symbol>
<symbol label=":">colon</symbol>
<symbol label="&gt;">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="&#9786;" icon="keyboard-mode-symbolic" tooltip="Setup">preferences</symbol>
<symbol label=" ">space</symbol>
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
</symbols>

View File

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

View File

@ -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="&#183;">U00B7</symbol>
<symbol label="t">t</symbol>
<symbol label="T">T</symbol>
<symbol label="5">5</symbol>
<symbol label="&#8730;">squareroot</symbol>
<symbol label="y">y</symbol>
<symbol label="Y">Y</symbol>
<symbol label="6">6</symbol>
<symbol label="&#960;">Greek_pi</symbol>
<symbol label="u">u</symbol>
<symbol label="U">U</symbol>
<symbol label="7">7</symbol>
<symbol label="&#247;">division</symbol>
<symbol label="i">i</symbol>
<symbol label="I">I</symbol>
<symbol label="8">8</symbol>
<symbol label="&#215;">multiply</symbol>
<symbol label="o">o</symbol>
<symbol label="O">O</symbol>
<symbol label="9">9</symbol>
<symbol label="&#182;">paragraph</symbol>
<symbol label="p">p</symbol>
<symbol label="P">P</symbol>
<symbol label="0">0</symbol>
<symbol label="&#964;">Greek_tau</symbol>
<symbol label="a">a</symbol>
<symbol label="A">A</symbol>
<symbol label="@">at</symbol>
<symbol label="&#169;">copyright</symbol>
<symbol label="s">s</symbol>
<symbol label="S">S</symbol>
<symbol label="#">numbersign</symbol>
<symbol label="&#174;">U00AE</symbol>
<symbol label="d">d</symbol>
<symbol label="D">D</symbol>
<symbol label="$">dollar</symbol>
<symbol label="&#163;">U00A3</symbol>
<symbol label="f">f</symbol>
<symbol label="F">F</symbol>
<symbol label="%">percent</symbol>
<symbol label="&#8364;">EuroSign</symbol>
<symbol label="g">g</symbol>
<symbol label="G">G</symbol>
<symbol label="&amp;">ampersand</symbol>
<symbol label="&#165;">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="&#176;">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="&quot;">quotedbl</symbol>
<symbol label="/">slash</symbol>
<symbol label="c">c</symbol>
<symbol label="C">C</symbol>
<symbol label="'">quoteright</symbol>
<symbol label="&lt;">less</symbol>
<symbol label="v">v</symbol>
<symbol label="V">V</symbol>
<symbol label=":">colon</symbol>
<symbol label="&gt;">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="&#9786;" 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
View 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"

View File

@ -6,4 +6,5 @@ Exec=squeekboard
Icon=squeekboard
Terminal=false
Type=Application
NoDisplay=true
Categories=GTK;Utility;

View File

@ -2,10 +2,6 @@
<gresources>
<gresource prefix="/sm/puri/squeekboard">
<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/as-inscript.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/mr-inscript.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/pa-inscript.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/ua.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/special/number.xml</file>
<file>icons/key-enter.svg</file>
<file>icons/key-shift.svg</file>
<file>icons/keyboard-mode-symbolic.svg</file>

View File

@ -17,3 +17,23 @@
background: #1c71d8;
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
View 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
View File

@ -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
* Use a shared DBus definition

9
debian/control vendored
View File

@ -3,12 +3,19 @@ Section: x11
Priority: optional
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
Build-Depends:
cargo,
debhelper (>= 10),
meson (>=0.43.0),
meson (>=0.51.0),
ninja-build,
pkg-config,
libglib2.0-dev,
libgtk-3-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),
rustc,
wayland-protocols (>= 1.14),

7
debian/rules vendored
View File

@ -1,8 +1,15 @@
#!/usr/bin/make -f
export CARGO_HOME = $(CURDIR)/debian/cargo
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
%:
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:

2
debian/squeekboard.lintian-overrides vendored Normal file
View 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

View File

@ -34,7 +34,6 @@
#include "eek-renderer.h"
#include "eek-keyboard.h"
#include "src/symbol.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)
static void on_button_pressed (struct squeek_button *button, struct squeek_view *view,
EekGtkKeyboard *self);
static void on_button_released (struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self);
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
static void render_locked_button (GtkWidget *widget,
struct button_place *place);
static void render_released_button (GtkWidget *widget,
struct squeek_button *button);
EekGtkKeyboard *self);
static void on_button_released (const struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self);
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
static void render_locked_button (GtkWidget *widget,
struct button_place *place);
static void render_released_button (GtkWidget *widget,
const struct squeek_button *button);
static void
eek_gtk_keyboard_real_realize (GtkWidget *self)
@ -81,7 +80,8 @@ eek_gtk_keyboard_real_realize (GtkWidget *self)
GDK_KEY_RELEASE_MASK |
GDK_BUTTON_PRESS_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);
}
@ -109,25 +109,23 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
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 */
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)) {
struct button_place place = squeek_view_find_key(
view, squeek_button_get_key(head->data)
view, head->data
);
if (place.button)
render_pressed_button (self, &place);
}
/* redraw locked key */
list = priv->keyboard->locked_buttons;
list = priv->keyboard->locked_keys;
for (const GList *head = list; head; head = g_list_next (head)) {
struct button_place place = squeek_view_find_key(
view, squeek_button_get_key(
((EekModifierKey *)head->data)->button
)
view, ((EekModifierKey *)head->data)->key
);
if (place.button)
render_locked_button (self, &place);
@ -160,56 +158,78 @@ static void depress(EekGtkKeyboard *self,
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
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);
}
}
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);
struct squeek_view *view = level_keyboard_current(priv->keyboard);
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
GList *list, *head;
list = g_list_copy(priv->keyboard->pressed_buttons);
list = g_list_copy(priv->keyboard->pressed_keys);
if (button) {
gboolean found = FALSE;
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;
} else {
eek_keyboard_release_button(priv->keyboard, head->data, time);
on_button_released(button, 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);
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);
}
} else {
for (head = list; head; head = g_list_next (head)) {
eek_keyboard_release_button(priv->keyboard, head->data, time);
on_button_released(head->data, view, self);
struct squeek_key *key = head->data;
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);
}
}
static void release(EekGtkKeyboard *self, guint32 time) {
static void release(EekGtkKeyboard *self, guint32 time)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
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)) {
struct squeek_button *button = head->data;
eek_keyboard_release_button(priv->keyboard, button, time);
on_button_released(button, view, self);
struct squeek_key *key = head->data;
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);
}
@ -254,27 +274,27 @@ handle_touch_event (GtkWidget *widget,
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
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 (priv->sequence) {
// Ignore second and following touch points
return FALSE;
}
release(self, event->time);
priv->sequence = event->sequence;
depress(self, event->x, event->y, event->time);
return TRUE;
}
if (priv->sequence != event->sequence) {
return FALSE;
}
if (event->type == GDK_TOUCH_UPDATE) {
/* Only allow the latest touch point to be dragged. */
if (event->type == GDK_TOUCH_UPDATE && event->sequence == priv->sequence) {
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?
release(self, event->time);
priv->sequence = NULL;
/* Only respond to the release of the latest touch point. Previous
touches have already been released. */
if (event->sequence == priv->sequence) {
release(self, event->time);
priv->sequence = NULL;
}
}
return TRUE;
}
@ -286,49 +306,20 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
if (priv->keyboard) {
GList *list, *head;
GList *head;
/* Make a copy of HEAD before sending "released" signal on
elements, so that the default handler of
EekKeyboard::key-released signal can remove elements from its
internal copy */
list = g_list_copy(priv->keyboard->pressed_buttons);
for (head = list; head; head = g_list_next (head)) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey released");
g_signal_emit_by_name (head->data, "released");
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
/* Unlike other places where we call this, we don't call
on_button_released afterwards since we don't want to queue a
redraw. */
eek_keyboard_release_key(priv->keyboard, head->data,
gdk_event_get_time(NULL));
}
g_list_free (list);
}
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
eek_gtk_keyboard_set_property (GObject *object,
guint prop_id,
@ -351,17 +342,16 @@ eek_gtk_keyboard_dispose (GObject *object)
if (priv->renderer) {
g_object_unref (priv->renderer);
priv->renderer = NULL;
priv->renderer = NULL;
}
if (priv->keyboard) {
GList *list, *head;
GList *head;
list = g_list_copy(priv->keyboard->pressed_buttons);
for (head = list; head; head = g_list_next (head)) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey pressed");
g_signal_emit_by_name (head->data, "released", level_keyboard_current(priv->keyboard));
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
eek_keyboard_release_key(priv->keyboard, head->data,
gdk_event_get_time(NULL));
}
g_list_free (list);
priv->keyboard = NULL;
}
@ -385,8 +375,6 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
eek_gtk_keyboard_real_button_release_event;
widget_class->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;
gobject_class->set_property = eek_gtk_keyboard_set_property;
@ -467,9 +455,10 @@ render_locked_button (GtkWidget *widget, struct button_place *place)
cairo_region_destroy (region);
}
// TODO: does it really redraw the entire keyboard?
static void
render_released_button (GtkWidget *widget,
struct squeek_button *button)
const struct squeek_button *button)
{
(void)button;
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
@ -489,8 +478,8 @@ render_released_button (GtkWidget *widget,
static void
on_button_pressed (struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self)
struct squeek_view *view,
EekGtkKeyboard *self)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
@ -518,9 +507,9 @@ on_button_pressed (struct squeek_button *button,
}
static void
on_button_released (struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self)
on_button_released (const struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self)
{
(void)view;
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);

View File

@ -34,7 +34,6 @@
#include "eekboard/key-emitter.h"
#include "keymap.h"
#include "src/keyboard.h"
#include "src/symbol.h"
#include "eek-keyboard.h"
@ -50,76 +49,52 @@ eek_modifier_key_free (EekModifierKey *modkey)
g_slice_free (EekModifierKey, modkey);
}
/// Updates the state of locked keys based on the key that was activated
/// FIXME: make independent of what the key are named,
/// and instead refer to the contained symbols
static guint
set_key_states (LevelKeyboard *keyboard,
struct squeek_button *button,
guint new_level)
void
eek_keyboard_set_key_locked (LevelKeyboard *keyboard,
struct squeek_key *key)
{
struct squeek_key *key = squeek_button_get_key(button);
// Keys locking rules hardcoded for the time being...
const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(key));
// Lock the shift whenever it's pressed on the baselevel
// TODO: need to lock shift on the destination level
if (g_strcmp0(name, "Shift_L") == 0 && keyboard->level == 0) {
EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
modifier_key->modifiers = 0;
modifier_key->button = button;
keyboard->locked_buttons =
g_list_prepend (keyboard->locked_buttons, modifier_key);
squeek_key_set_locked(key, true);
}
if (keyboard->level == 1) {
// Only shift is locked in this state, unlock on any key press
for (GList *head = keyboard->locked_buttons; head; ) {
EekModifierKey *modifier_key = head->data;
GList *next = g_list_next (head);
keyboard->locked_buttons =
g_list_remove_link (keyboard->locked_buttons, head);
squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
g_list_free1 (head);
head = next;
}
return 0;
}
return new_level;
EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
modifier_key->modifiers = 0;
modifier_key->key = key;
keyboard->locked_keys =
g_list_prepend (keyboard->locked_keys, modifier_key);
}
/// Unlock all locked keys.
/// All locked keys will unlock at the next keypress (should be called "stuck")
/// Returns the number of handled keys
/// TODO: may need to check key type in order to chain locks
/// before pressing an "emitting" key
static int unlock_keys(LevelKeyboard *keyboard) {
int handled = 0;
for (GList *head = keyboard->locked_keys; head; ) {
EekModifierKey *modifier_key = head->data;
GList *next = g_list_next (head);
keyboard->locked_keys =
g_list_remove_link (keyboard->locked_keys, head);
//squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
squeek_layout_set_state_from_press(keyboard->layout, keyboard, modifier_key->key);
g_list_free1 (head);
head = next;
handled++;
}
return handled;
}
// FIXME: unhardcode, parse some user information as to which key triggers which view (level)
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 */
guint level = keyboard->level;
/* Handle non-emitting keys */
if (button) {
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;
}
// If the currently locked key was already handled in the unlock phase,
// then skip
if (unlock_keys(keyboard) == 0) {
squeek_layout_set_state_from_press(keyboard->layout, keyboard, key);
}
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) {
struct squeek_key *key = squeek_button_get_key(button);
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp) {
squeek_key_set_pressed(key, TRUE);
keyboard->pressed_buttons = g_list_prepend (keyboard->pressed_buttons, button);
struct squeek_symbol *symbol = squeek_key_get_symbol(key);
if (!symbol)
return;
keyboard->pressed_keys = g_list_prepend (keyboard->pressed_keys, key);
// Only take action about setting level *after* the key has taken effect, i.e. on release
//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);
}
void eek_keyboard_release_button(LevelKeyboard *keyboard,
struct squeek_button *button,
guint32 timestamp) {
for (GList *head = keyboard->pressed_buttons; head; head = g_list_next (head)) {
if (head->data == button) {
keyboard->pressed_buttons = g_list_remove_link (keyboard->pressed_buttons, head);
void eek_keyboard_release_key(LevelKeyboard *keyboard,
struct squeek_key *key,
guint32 timestamp) {
for (GList *head = keyboard->pressed_keys; head; head = g_list_next (head)) {
if (squeek_key_equal(head->data, key)) {
keyboard->pressed_keys = g_list_remove_link (keyboard->pressed_keys, head);
g_list_free1 (head);
break;
}
}
struct squeek_symbol *symbol = squeek_button_get_symbol(button);
if (!symbol)
return;
set_level_from_press (keyboard, button);
set_level_from_press (keyboard, key);
// "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);
}
void level_keyboard_deinit(LevelKeyboard *self) {
g_hash_table_destroy (self->names);
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];
}
squeek_layout_free(self->layout);
}
void level_keyboard_free(LevelKeyboard *self) {
@ -175,115 +135,18 @@ void level_keyboard_free(LevelKeyboard *self) {
g_free(self);
}
void level_keyboard_init(LevelKeyboard *self) {
self->outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline));
void level_keyboard_init(LevelKeyboard *self, struct squeek_layout *layout) {
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);
level_keyboard_init(keyboard);
for (uint i = 0; i < 4; i++) {
keyboard->views[i] = views[i];
}
level_keyboard_init(keyboard, layout);
keyboard->manager = manager;
keyboard->names = name_button_hash;
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)
{
return keyboard->views[keyboard->level];
return squeek_layout_get_current_view(keyboard->layout);
}

View File

@ -36,24 +36,19 @@ G_BEGIN_DECLS
struct _EekModifierKey {
/*< public >*/
EekModifierType modifiers;
struct squeek_button *button;
struct squeek_key *key;
};
typedef struct _EekModifierKey EekModifierKey;
/// Keyboard state holder
struct _LevelKeyboard {
struct squeek_view *views[4];
guint level;
struct squeek_layout *layout;
struct xkb_keymap *keymap;
int keymap_fd; // keymap formatted as XKB string
size_t keymap_len; // length of the data inside keymap_fd
GArray *outline_array;
GList *pressed_buttons; // struct squeek_button*
GList *locked_buttons; // struct squeek_button*
/* Map button names to button objects: */
GHashTable *names;
GList *pressed_keys; // struct squeek_key*
GList *locked_keys; // struct EekModifierKey*
guint id; // as a key to layout choices
@ -61,31 +56,25 @@ struct _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
struct button_place {
const struct squeek_row *row;
const struct squeek_button *button;
};
EekOutline *level_keyboard_get_outline
(LevelKeyboard *keyboard,
guint oref);
EekModifierKey *eek_modifier_key_copy
(EekModifierKey *modkey);
void eek_modifier_key_free
(EekModifierKey *modkey);
void eek_keyboard_press_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp);
void eek_keyboard_release_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_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
gchar * eek_keyboard_get_keymap
(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_free(LevelKeyboard *self);

View File

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

View File

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

View File

@ -45,9 +45,3 @@ void
eek_layout_init (EekLayout *self)
{
}
void
eek_layout_update_layout(LevelKeyboard *keyboard)
{
squeek_view_place_contents(level_keyboard_current(keyboard), keyboard);
}

View File

@ -56,14 +56,5 @@ struct _EekLayoutClass
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
#endif /* EEK_LAYOUT_H */

View File

@ -24,8 +24,6 @@
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "src/symbol.h"
#include "eek-renderer.h"
enum {
@ -131,8 +129,7 @@ create_keyboard_surface_row_callback (struct squeek_row *row,
cairo_rotate (data->cr, angle * G_PI / 180);
data->row = row;
squeek_row_foreach(row, create_keyboard_surface_button_callback,
data);
squeek_row_foreach(row, create_keyboard_surface_button_callback, data);
cairo_restore (data->cr);
}
@ -175,8 +172,8 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
/* draw rows */
squeek_view_foreach(level_keyboard_current(priv->keyboard),
create_keyboard_surface_row_callback,
&data);
create_keyboard_surface_row_callback,
&data);
cairo_restore (data.cr);
cairo_destroy (data.cr);
@ -189,14 +186,20 @@ render_button_outline (EekRenderer *renderer,
gboolean active)
{
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);
/* 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,
active ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
@ -215,18 +218,12 @@ render_button (EekRenderer *self,
gboolean active)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
EekOutline *outline;
cairo_surface_t *outline_surface;
GHashTable *outline_surface_cache;
PangoLayout *layout;
PangoRectangle extents = { 0, };
EekColor foreground;
guint oref = squeek_button_get_oref (place->button);
outline = level_keyboard_get_outline (priv->keyboard, oref);
if (outline == NULL)
return;
/* render outline */
EekBounds bounds = squeek_button_get_bounds(place->button);
@ -235,7 +232,7 @@ render_button (EekRenderer *self,
else
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) {
cairo_t *cr;
@ -259,7 +256,7 @@ render_button (EekRenderer *self,
cairo_destroy (cr);
g_hash_table_insert (outline_surface_cache,
outline,
(gpointer)place->button,
outline_surface);
}
@ -268,16 +265,12 @@ render_button (EekRenderer *self,
eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
/* render icon (if any) */
struct squeek_symbol *symbol = squeek_button_get_symbol(place->button);
if (!symbol)
return;
const char *icon_name = squeek_button_get_icon_name(place->button);
if (squeek_symbol_get_icon_name (symbol)) {
if (icon_name) {
gint scale = priv->scale_factor;
cairo_surface_t *icon_surface =
eek_renderer_get_icon_surface (self,
squeek_symbol_get_icon_name (symbol),
16 / priv->scale,
eek_renderer_get_icon_surface (self, icon_name, 16 / priv->scale,
scale);
if (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);
const gchar *label;
const gchar *label = squeek_button_get_label(button);
if (!label) {
return;
}
PangoFontDescription *font;
PangoLayoutLine *line;
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) {
const PangoFontDescription *base_font;
@ -481,7 +472,7 @@ eek_renderer_real_render_keyboard (EekRenderer *self,
cairo_get_target (cr), 0, 0,
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);
source = cairo_get_source (cr);
@ -971,14 +962,15 @@ eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_
**/
struct squeek_button *
eek_renderer_find_button_by_position (EekRenderer *renderer,
struct squeek_view *view,
gdouble x,
gdouble y)
struct squeek_view *view,
gdouble x,
gdouble y)
{
g_return_val_if_fail (EEK_IS_RENDERER(renderer), NULL);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
/* Transform from widget coordinates to keyboard coordinates */
EekPoint point = {
.x = (x - priv->origin_x)/priv->scale,
.y = (y - priv->origin_y)/priv->scale,

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -23,47 +23,11 @@
#ifndef EEK_XML_LAYOUT_H
#define EEK_XML_LAYOUT_H 1
#include <gio/gio.h>
#include "eek-layout.h"
#include "eek-types.h"
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 *
eek_xml_layout_real_create_keyboard (EekLayout *self,
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
EekboardContextService *manager);
G_END_DECLS
#endif /* EEK_XML_LAYOUT_H */

View File

@ -24,7 +24,6 @@
#include "eek-keyboard.h"
#include "eek-layout.h"
#include "eek-keysym.h"
void eek_init (void);

View File

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

View File

@ -6,34 +6,3 @@ squeek_keymap_get_entries_for_keyval (struct xkb_keymap *xkb_keymap,
guint keyval,
GdkKeymapKey **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\
};";

View File

@ -6,15 +6,3 @@ 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

View File

@ -29,6 +29,7 @@
#include <fcntl.h>
#include <stdio.h>
#define _XOPEN_SOURCE 500
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h> // TODO: this is Linux-specific
@ -89,66 +90,17 @@ static LevelKeyboard *
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
const gchar *keyboard_type)
{
EekLayout *layout;
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);
LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
g_object_unref (layout);
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
g_error("No context created");
}
gchar *keymap_str = eek_keyboard_get_keymap(keyboard);
int f = open("maprs.map", O_CREAT | O_WRONLY, 0600);
write(f, keymap_str, strlen(keymap_str));
close(f);
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
@ -156,8 +108,6 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
if (!keymap)
g_error("Bad keymap:\n%s", keymap_str);
free(keymap_str);
xkb_context_unref(context);
keyboard->keymap = keymap;
@ -167,7 +117,7 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
char *r = &path[strlen(path) - 6];
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] > '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) {
g_error("Failed to set up mmap");
}
strcpy(ptr, keymap_str);
strncpy(ptr, keymap_str, keyboard->keymap_len);
munmap(ptr, keyboard->keymap_len);
free(keymap_str);
return keyboard;
}

View File

@ -90,12 +90,9 @@ send_fake_key (SeatEmitter *emitter,
gboolean pressed,
uint32_t timestamp)
{
guint level = keyboard->level;
uint32_t group = (level / 2);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
send_virtual_keyboard_key (emitter->virtual_keyboard, keycode - 8, (unsigned)pressed, timestamp);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
}
void

29
examples/test_layout.rs Normal file
View 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());
}

View File

@ -1,9 +1,9 @@
project(
'squeekboard',
'c', 'rust',
version: '1.0.10',
version: '1.2.0',
license: 'GPLv3',
meson_version: '>=0.49.0',
meson_version: '>=0.51.0',
default_options: [
'warning_level=1',
'buildtype=debugoptimized',
@ -53,6 +53,9 @@ summary = [
]
message('\n'.join(summary))
cargo = find_program('cargo')
cargo_script = find_program('cargo.sh')
subdir('data')
subdir('protocols')
subdir('eek')

File diff suppressed because it is too large Load Diff

640
src/data.rs Normal file
View 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())),
},
}
);
}
}

View File

@ -5,6 +5,10 @@
/* Adapted from https://github.com/notriddle/rust-float-ord revision e995165f
* maintained by Michael Howell <michael@notriddle.com>
* 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;
@ -67,6 +71,7 @@ float_ord_impl!(f64, u64, 64);
/// # Example
///
/// ```
/// use rs::float_ord;
/// let mut v = [-5.0, 4.0, 1.0, -3.0, 2.0];
///
/// float_ord::sort(&mut v);

View File

@ -4,6 +4,7 @@ use std::num::Wrapping;
use std::string::String;
use super::bitflags;
use ::util::c::into_cstring;
// Traits
use std::convert::TryFrom;
@ -13,15 +14,8 @@ use std::convert::TryFrom;
pub mod c {
use super::*;
use std::ffi::CStr;
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
/// struct zwp_input_method_v2*
@ -91,7 +85,9 @@ pub mod c {
{
let imservice = check_imservice(imservice, im).unwrap();
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,
..imservice.pending.clone()
};
@ -230,7 +226,8 @@ bitflags!{
/// 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)]
pub enum ContentPurpose {

View File

@ -1,5 +1,5 @@
#ifndef __KEYBOARD_H
#define __KYBOARD_H
#define __KEYBOARD_H
#include "stdbool.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);
uint32_t squeek_key_get_keycode(struct squeek_key *key);
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);
const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_key *key);

View File

@ -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
pub mod c {
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::os::raw::c_char;
use std::ptr;
use std::rc::Rc;
// traits
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
pub type CKeyState = c::Wrapped<KeyState>;
// 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]
pub extern "C"
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]
@ -97,7 +48,9 @@ pub mod c {
#[no_mangle]
pub extern "C"
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]
@ -109,109 +62,15 @@ pub mod c {
#[no_mangle]
pub extern "C"
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]
pub extern "C"
fn squeek_key_get_keycode(key: CKeyState) -> u32 {
return key.to_owned().keycode as u32;
}
#[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(),
}
})
return key.to_owned().keycode.unwrap_or(0u32);
}
#[no_mangle]
@ -225,20 +84,14 @@ pub mod c {
.to_str()
.expect("Bad key name");
let symbol_name = match key.to_owned().symbol {
Some(ref symbol) => match &symbol.action {
symbol::Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol")
)
},
_ => None
},
None => {
eprintln!("Key {} has no symbol", key_name);
None
let symbol_name = match key.to_owned().symbol.action {
Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol")
)
},
_ => None,
};
let inner = match symbol_name {
@ -251,7 +104,7 @@ pub mod c {
.into_raw()
}
#[no_mangle]
#[no_mangle]
pub extern "C"
fn squeek_key_get_action_name(
key_name: *const c_char,
@ -262,20 +115,14 @@ pub mod c {
.to_str()
.expect("Bad key name");
let symbol_name = match key.to_owned().symbol {
Some(ref symbol) => match &symbol.action {
symbol::Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol")
)
},
_ => None
},
None => {
eprintln!("Key {} has no symbol", key_name);
None
let symbol_name = match key.to_owned().symbol.action {
Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol text")
)
},
_ => None
};
let inner = match symbol_name {
@ -287,14 +134,126 @@ pub mod c {
.expect("Couldn't convert string")
.into_raw()
}
}
#[derive(Debug, Clone)]
pub struct KeyState {
pub pressed: bool,
pub locked: bool,
pub keycode: u32,
// TODO: remove the optionality of a symbol
pub symbol: Option<symbol::Symbol>,
pub keycode: Option<u32>,
pub 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)
}

View File

@ -9,17 +9,11 @@
struct squeek_button;
struct squeek_row;
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*);
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);
@ -33,34 +27,19 @@ void squeek_row_foreach(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*);
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*);
uint32_t *squeek_button_has_key(const struct squeek_button* button,
const struct squeek_key *key);
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*);
void squeek_view_set_bounds(const struct squeek_view*, EekBounds bounds);
typedef void (*RowCallback) (struct squeek_row *row, gpointer user_data);
void squeek_view_foreach(struct squeek_view*,
@ -70,9 +49,16 @@ void squeek_view_foreach(struct squeek_view*,
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
struct squeek_button *button);
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
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_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
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

View File

@ -1,4 +1,4 @@
/**
/*!
* Layout-related data.
*
* The `View` contains `Row`s and each `Row` contains `Button`s.
@ -18,6 +18,8 @@
*/
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::rc::Rc;
use std::vec::Vec;
@ -29,14 +31,15 @@ use ::symbol::*;
pub mod c {
use super::*;
use std::os::raw::c_void;
use std::ffi::CStr;
use std::os::raw::{ c_char, c_void };
use std::ptr;
// The following defined in C
#[repr(transparent)]
pub struct UserData(*const c_void);
/// The index in the relevant outline table
#[repr(C)]
#[derive(Clone, Debug)]
@ -60,57 +63,18 @@ pub mod c {
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 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
#[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]
pub extern "C"
fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
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)));
// 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]
#[no_mangle]
pub extern "C"
fn squeek_view_foreach(
view: *mut ::layout::View,
@ -123,68 +87,6 @@ pub mod c {
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]
pub extern "C"
@ -231,59 +133,13 @@ pub mod c {
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]
pub extern "C"
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
let button = unsafe { &*button };
match &button.bounds {
Some(bounds) => bounds.clone(),
None => panic!("Button doesn't have any bounds yet"),
}
button.bounds.clone()
}
/// 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
#[no_mangle]
pub extern "C"
@ -302,12 +158,43 @@ pub mod c {
) -> *const Symbol {
let button = unsafe { &*button };
let state = button.state.borrow();
match state.symbol {
Some(ref symbol) => symbol as *const Symbol,
None => ptr::null(),
&state.symbol as *const Symbol
}
#[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]
pub extern "C"
fn squeek_button_has_key(
@ -315,9 +202,8 @@ pub mod c {
state: ::keyboard::c::CKeyState,
) -> u32 {
let button = unsafe { &*button };
let state = state.unwrap();
let state = state.clone_ref();
let equal = Rc::ptr_eq(&button.state, &state);
Rc::into_raw(state); // Prevent dropping
equal as u32
}
@ -328,27 +214,46 @@ pub mod c {
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
pub mod procedures {
use super::*;
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
#[repr(C)]
#[derive(PartialEq, Debug)]
pub struct ButtonPlace {
row: *const Row,
button: *const Button,
}
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
#[no_mangle]
extern "C" {
fn eek_get_outline_size(
keyboard: *const LevelKeyboard,
outline: u32
) -> Bounds;
/// Checks if point falls within bounds,
/// which are relative to origin and rotated by angle (I think)
pub fn eek_are_bounds_inside (bounds: Bounds,
@ -356,15 +261,53 @@ pub mod c {
origin: Point,
angle: i32
) -> u32;
pub fn eek_keyboard_set_key_locked(
keyboard: *mut LevelKeyboard,
key: ::keyboard::c::CKeyState,
);
}
fn squeek_buttons_get_outlines(
buttons: &Vec<Box<Button>>,
keyboard: *const LevelKeyboard,
) -> Vec<Bounds> {
buttons.iter().map(|button| {
unsafe { eek_get_outline_size(keyboard, button.oref.0) }
}).collect()
#[no_mangle]
pub extern "C"
fn squeek_layout_set_state_from_press(
layout: *mut Layout,
keyboard: *mut LevelKeyboard,
key: ::keyboard::c::CKeyState,
) {
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,
@ -375,17 +318,19 @@ pub mod c {
/// Sets button and row sizes according to their contents.
#[no_mangle]
pub extern "C"
fn squeek_view_place_contents(
view: *mut ::layout::View,
keyboard: *const LevelKeyboard, // source of outlines
fn squeek_layout_place_contents(
layout: *mut Layout,
) {
let view = unsafe { &mut *view };
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row|
squeek_buttons_get_outlines(&row.buttons, keyboard)
).collect();
let layout = unsafe { &mut *layout };
for view in layout.views.values_mut() {
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
row.buttons.iter()
.map(|button| button.bounds.clone())
.collect()
}).collect();
view.place_buttons_with_sizes(sizes);
view.place_buttons_with_sizes(sizes);
}
}
fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
@ -418,13 +363,10 @@ pub mod c {
needle: ::keyboard::c::CKeyState,
) -> ButtonPlace {
let view = unsafe { &*view };
let state = needle.unwrap();
let state = needle.clone_ref();
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
let (row, button) = match paths.get(0) {
Some((row, button)) => (
@ -454,35 +396,36 @@ pub mod c {
mod test {
use super::*;
use super::super::test::*;
#[test]
fn row_has_button() {
let mut row = Row::new(0);
let button = squeek_row_create_button(&mut row as *mut Row, 0, 0);
assert_eq!(squeek_row_contains(&row, button), true);
let shared_button = squeek_row_create_button_with_state(
&mut row as *mut Row,
button
let state = make_state();
let button = make_button_with_state(
"test".into(),
state.clone()
);
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);
assert_eq!(squeek_row_contains(&row, button), false);
assert_eq!(squeek_row_contains(&row, button_ptr), false);
}
#[test]
fn view_has_button() {
let state = Rc::new(RefCell::new(::keyboard::KeyState {
pressed: false,
locked: false,
keycode: 0,
symbol: None,
}));
let state = make_state();
let state_clone = ::keyboard::c::CKeyState::wrap(state.clone());
let button = Box::new(Button {
oref: OutlineRef(0),
bounds: None,
state: state,
});
let button = make_button_with_state("1".into(), state);
let button_ptr = button.as_ref() as *const Button;
let row = Box::new(Row {
@ -535,18 +478,64 @@ pub mod c {
#[cfg(test)]
mod test {
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]
fn button_has_key() {
let button = squeek_button_new(0, 0);
let state = squeek_button_get_key(button);
assert_eq!(squeek_button_has_key(button, state.clone()), 1);
let other_button = squeek_button_new(0, 0);
assert_eq!(squeek_button_has_key(other_button, state.clone()), 0);
let other_state = ::keyboard::c::squeek_key_new(0);
assert_eq!(squeek_button_has_key(button, other_state), 0);
let shared_button = squeek_button_new_with_state(button);
assert_eq!(squeek_button_has_key(shared_button, state), 1);
let state = make_state();
let button = make_button_with_state("1".into(), state.clone());
assert_eq!(
squeek_button_has_key(
button_as_raw(&button),
CKeyState::wrap(state.clone())
),
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
);
}
}
}
@ -557,13 +546,25 @@ pub struct Size {
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
#[derive(Clone, Debug)]
pub struct Button {
oref: c::OutlineRef,
/// TODO: abolish Option, buttons should be created with bounds fully formed
/// ID string, e.g. for CSS
pub name: CString,
/// 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)
bounds: Option<c::Bounds>,
pub bounds: c::Bounds,
/// current state, shared with other buttons
pub state: Rc<RefCell<KeyState>>,
}
@ -574,11 +575,11 @@ const ROW_SPACING: f64 = 7.0;
/// The graphical representation of a row of buttons
pub struct Row {
buttons: Vec<Box<Button>>,
pub buttons: Vec<Box<Button>>,
/// Angle is not really used anywhere...
angle: i32,
pub angle: i32,
/// Position relative to some origin (i.e. parent/view origin)
bounds: Option<c::Bounds>,
pub bounds: Option<c::Bounds>,
}
impl Row {
@ -639,9 +640,7 @@ impl Row {
};
let angle = self.angle;
self.buttons.iter_mut().find(|button| {
let bounds = button.bounds
.as_ref().expect("Missing bounds on button")
.clone();
let bounds = button.bounds.clone();
let point = point.clone();
let origin = origin.clone();
procedures::is_point_inside(bounds, point, origin, angle)
@ -651,8 +650,8 @@ impl Row {
pub struct View {
/// Position relative to keyboard origin
bounds: c::Bounds,
rows: Vec<Box<Row>>,
pub bounds: c::Bounds,
pub rows: Vec<Box<Row>>,
}
impl View {
@ -704,7 +703,7 @@ impl View {
for (mut button, button_position)
in row.buttons.iter_mut()
.zip(button_positions) {
button.bounds = Some(button_position);
button.bounds = button_position;
}
}
}
@ -727,6 +726,26 @@ impl View {
}
}
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 {
use super::*;

View File

@ -1,9 +1,16 @@
#[macro_use]
mod bitflags;
extern crate bitflags;
#[macro_use]
extern crate maplit;
extern crate serde;
extern crate xkbcommon;
mod float_ord;
mod imservice;
pub mod data;
pub mod float_ord;
pub mod imservice;
mod keyboard;
mod layout;
mod resources;
mod symbol;
mod util;
mod xdg;

View File

@ -20,16 +20,13 @@ sources = [
'../eek/eek-gtk-keyboard.c',
'../eek/eek-keyboard.c',
'../eek/eek-keyboard-drawing.c',
'../eek/eek-keysym.c',
'../eek/eek-layout.c',
'../eek/eek-renderer.c',
'../eek/eek-section.c',
'../eek/eek-types.c',
'../eek/eek-xml-layout.c',
'../eek/layersurface.c',
dbus_src,
enums,
keysym_entries,
'../eekboard/key-emitter.c',
'../eekboard/eekboard-context-service.c',
'../eekboard/eekboard-context.c',
@ -56,25 +53,25 @@ deps = [
# dependency('libxklavier'), # FIXME remove
]
# Replacement for eekboard-server
rslib = static_library(
'rslib',
sources: ['lib.rs'],
rust_crate_type: 'staticlib'
rslibs = custom_target(
'rslibs',
build_by_default: true,
build_always_stale: true,
output: ['librs.a'],
install: false,
console: true,
command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@', 'build']
)
rstests = executable(
'rstests',
sources: ['lib.rs'],
rust_args: ['--test'],
install: false
test(
'rstest',
cargo_script,
args: [meson.source_root(), '', 'test']
)
test('rstests', rstests)
libsqueekboard = static_library('libsqueekboard',
sources,
link_with: rslib,
link_with: [rslibs],
include_directories: [include_directories('..'), include_directories('../eek')],
dependencies: deps,
c_args: [

24
src/resources.rs Normal file
View 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 }
})
}

View File

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

View File

@ -1,138 +1,46 @@
/*! The symbol object, defining actions that the key can do when activated */
use std::ffi::CString;
/// Gathers stuff defined in C or called by C
pub mod c {
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),
}
/// Name of the keysym
#[derive(Debug, Clone, PartialEq)]
pub struct KeySym(pub String);
/// Use to switch layouts
type Level = u8;
type Level = String;
/// Use to send modified keypresses
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum Modifier {
Control,
Alt,
}
/// Action to perform on the keypress and, in reverse, on keyrelease
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
/// Switch to this level TODO: reverse?
/// Switch to this view
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?
SetModifier(Modifier),
/// Submit some text
Submit {
/// orig: Canonical name of the symbol
/// Text to submit with input-method
text: Option<CString>,
/// 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
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct Symbol {
/// The action that this key performs
pub action: Action,
/// Label to display to the user
pub label: Label,
// FIXME: is it used?
pub tooltip: Option<CString>,
}

View File

@ -1,9 +1,14 @@
pub mod c {
use std::cell::RefCell;
use std::ffi::{ CStr, CString };
use std::os::raw::c_char;
use std::rc::Rc;
use std::str::Utf8Error;
// traits
use std::borrow::ToOwned;
#[allow(dead_code)]
pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
if s.is_null() {
Ok(None)
@ -13,7 +18,7 @@ pub mod c {
.map(Some)
}
}
pub fn as_cstr(s: &*const c_char) -> Option<&CStr> {
if s.is_null() {
None
@ -47,4 +52,52 @@ pub mod c {
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
View 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())
})
}

View File

@ -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 ();
}

View File

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

View File

@ -19,13 +19,10 @@ test_link_args = [
'-fPIC',
]
tests = [
'eek-simple-test',
'eek-xml-test',
'test-keymap-generation'
c_tests = [
]
foreach name : tests
foreach name : c_tests
test_sources = [name + '.c']
@ -47,4 +44,15 @@ foreach name : tests
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

View File

@ -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 ();
}