Compare commits

..

60 Commits

Author SHA1 Message Date
1488aa97f3 Release 1.8.1 "Corona"
- Landscape layout doesn't crash
- CSS font is actually taken into account
- Failed start due to dbus is now communicated
- Better log messages
- Fixed Enter in numbers layout
- More consistent terminal layout
- Proper font sizes in terminal layout
2020-01-31 10:08:59 +00:00
4eaa8e316e cargo: Update deps 2020-01-31 10:08:48 +00:00
cbee649939 Merge branch 'update-docs' into 'master'
Update docs and CI builds

See merge request Librem5/squeekboard!320
2020-01-30 22:57:07 +00:00
99b1439d08 Use pip to install recommonmark 2020-01-30 16:51:35 +01:00
83fe2757ef Tidy build file and docs 2020-01-30 16:51:20 +01:00
d21d278710 Merge branch 'faster' into 'master'
build: Strip clap of optional features

See merge request Librem5/squeekboard!311
2020-01-29 09:27:42 +00:00
e6ca914d65 Merge branch 'return' into 'master'
number: Fix keysym for Return

See merge request Librem5/squeekboard!310
2020-01-29 09:26:05 +00:00
0d96a647f9 Merge branch 'packaging' into 'master'
debian: Add missing commas

See merge request Librem5/squeekboard!316
2020-01-29 09:05:07 +00:00
852289b5e3 Merge branch 'switch' into 'master'
setup: Connect ui to the state manager

See merge request Librem5/squeekboard!319
2020-01-28 22:04:47 +00:00
1f5e9566e4 debian: Add missing commas 2020-01-28 21:38:58 +00:00
7a588460bf setup: Connect ui to the state manager
This ensures that the layout type information is accessible to the state manager when new layout information arrive.

The should be thought of as a stopgap measure. A proper solution would be to separate the state properly, and probably turn layout information coming from random places into messages that some object (thread?) collects and displays.
2020-01-28 21:32:47 +00:00
d654b9cc73 Merge branch 'ux' into 'master'
layout: terminal: Replace actions button with period on symbols view

See merge request Librem5/squeekboard!317
2020-01-28 20:55:25 +00:00
3ed601a7e8 layout: terminal: Replace actions button with period on symbols view
Commit ab67bd2c5c took things a bit too
far and completely removed the period button.
2020-01-28 20:51:15 +01:00
63d68c004a Merge branch 'fix_wide' into 'master'
layouts: Fix segfault on switching to wide

See merge request Librem5/squeekboard!312
2020-01-28 19:43:46 +00:00
34c6d2ff28 Merge branch 'fintsize' into 'master'
font: Use font from style context

See merge request Librem5/squeekboard!313
2020-01-28 19:22:22 +00:00
9368a188b3 Merge branch 'ux' into 'master'
Terminal layout UX tweaks

Closes #175

See merge request Librem5/squeekboard!314
2020-01-28 19:16:07 +00:00
ab67bd2c5c layout: terminal: Show actions button on all views 2020-01-28 19:34:03 +01:00
f834fafd67 layout: terminal: Swap positions of preferences and actions button
This makes it consistent with regular layouts.

Helps with #175
2020-01-28 19:33:58 +01:00
4b34f18d34 font: Only pass relevant data to label renderer
This will help factoring the function out
2020-01-28 18:13:19 +00:00
d5682de47c font: Use font from style context
As a consequence, some dependency on renderer is gone.
2020-01-28 18:13:15 +00:00
2ffbdde758 layouts: Fix segfault on switching to wide 2020-01-28 16:42:58 +00:00
ac360b610f Merge branch 'log' into 'master'
Unify logging

See merge request Librem5/squeekboard!308
2020-01-28 11:42:02 +00:00
acfa48886d build: Strip clap of optional features
This makes the build marginally faster at the cost of losing non-essential command line parsing in test_layout.
2020-01-25 17:25:02 +00:00
f326929634 Merge branch 'text_input' into 'master'
Text input integration

See merge request Librem5/squeekboard!302
2020-01-24 09:41:14 +00:00
dbb8331294 number: Fix keysym for Return 2020-01-23 15:43:36 +00:00
09075e57c8 Merge branch 'fix_ci' into 'master'
ci: Clean up `..` before it's searched for artifacts

See merge request Librem5/squeekboard!305
2020-01-21 19:52:18 +00:00
2b65beba44 press_key: Use proper logging 2020-01-20 15:40:30 +00:00
5129d42577 Merge remote-tracking branch 'upstream/master' into log 2020-01-20 15:40:01 +00:00
2ed4862db8 Merge branch '1.8' into 'master'
Release 1.8.0

See merge request Librem5/squeekboard!303
2020-01-20 14:21:04 +00:00
8d06815279 Merge branch 'cleanups' into 'master'
C-side Cleanups

See merge request Librem5/squeekboard!300
2020-01-19 12:57:40 +00:00
c75e085dc8 logging: Unified to remove random eprint calls 2020-01-17 12:25:39 +00:00
cc418c3609 imservice: Return something more resembling an Error on failure
The error type is expected to be printable by logging utilities.
2020-01-17 11:59:47 +00:00
ea84f4f031 logging: Try to improve common operations
This adds sugar for logging `Result`s with a handler, makes names evoke something closer to "logging" than "warning", tries to remove any redundant `Logging` where the module name will do, and introduces a type strictly for bad things happening.
2020-01-16 15:57:46 +00:00
38398395bc Merge branch 'dbus_error' into 'master'
dbus: Log error on dbus exit

See merge request Librem5/squeekboard!307
2020-01-15 17:48:05 +00:00
81e0c15db9 dbus: Log error on dbus exit 2020-01-15 17:06:00 +00:00
60c68dbf5a ci: Clean up .. before it's searched for artifacts
GitLab doesn't always clean up the `..` directory, leaving things that are lated picked up as artifacts. The new rule cleans up anything that looks like an artifact before fresh ones are generated.
2020-01-14 18:47:04 +00:00
f3d852f552 Merge branch 'handling' into 'master'
Centralize handling release events

See merge request Librem5/squeekboard!289
2020-01-14 18:38:43 +00:00
e3f31cc17f imservice: Rename commit_state to done to match protocol 2020-01-14 18:16:36 +00:00
02c24a50d2 submission: Remove wildcard reexport 2020-01-14 11:38:44 +00:00
26dbcdeb62 keyboard: Gather up keymap handling, drop layout 2020-01-13 13:53:54 +00:00
0ef02ebfa3 levelkeyboard: Drop unused manager references 2020-01-13 13:53:54 +00:00
0ce19b4269 keyboard: Cleanups of unused code 2020-01-13 13:53:54 +00:00
326bb9319f submission: Take over virtual_keyboard handling 2020-01-13 13:53:54 +00:00
aafecfac02 EekGtkKeyboard: Use a direct reference to EekboardContext 2020-01-13 13:53:54 +00:00
e5d416fd4f imservice: Limited scope of unsafe 2020-01-13 13:53:54 +00:00
785717d477 submission: Create a new wrapper over imservice 2020-01-13 13:53:48 +00:00
51f55fbff8 submission: Move away from virtual-keyboard 2020-01-11 16:20:09 +00:00
92c9572ac2 services: Split out layout management from EekboardContextService
Layout management was pointlessly bound with the EekboardContextService with inheritance. Splitting it out will make it easier to further break apart layout state management, settings, and input method in the future.
2020-01-11 15:33:26 +00:00
58b087e35a eekboard_context_service: Drop unused enable property 2020-01-09 20:13:22 +00:00
14d5881f1e key-emitter: Remove unused 2020-01-09 19:57:14 +00:00
7dd8bd54c2 context: Moved keymap setting together with its generation 2020-01-09 16:42:17 +00:00
4c2cef30f2 dbus: Rename handler from eekboard_service 2020-01-09 16:25:53 +00:00
3ecfd701d9 dbus: Remove unneeded gobjectness
Also removed the code linking dbus interface stop to application quit. DBus going missing was not handled, and isn't a fatal error anyway.
2020-01-09 16:13:09 +00:00
033a1cf200 dbus_service: Remove unused function 2020-01-09 15:53:49 +00:00
9f59279307 managers: Move visible flag to UI manager 2020-01-09 14:14:48 +00:00
7e72722a47 UI: Drop indirection for show/hide functions 2020-01-09 13:30:02 +00:00
375daa68c8 layout: Make handling presses uniform 2020-01-09 12:09:28 +00:00
34db364a62 layout: Centralize handling key releases 2020-01-08 18:52:09 +00:00
950310c8a5 keyboard: Introduce a KeyCode type wrapping u32 2020-01-08 18:52:09 +00:00
e77eccf7db action: Rename Level to View 2020-01-08 18:52:09 +00:00
52 changed files with 1435 additions and 1602 deletions

View File

@ -22,7 +22,8 @@ build_docs:
paths: paths:
- _build - _build
script: script:
- apt-get -y install python3-recommonmark python3-sphinx - apt-get -y install python3-pip python3-sphinx
- pip3 install recommonmark
- ./doc/build.sh _build - ./doc/build.sh _build
build_meson: build_meson:
@ -45,6 +46,7 @@ build_deb:
paths: paths:
- "*.deb" - "*.deb"
script: script:
- rm -f ../*.deb
- apt-get -y build-dep . - apt-get -y build-dep .
- apt-get -y install devscripts - apt-get -y install devscripts
- debuild -i -us -uc -b - debuild -i -us -uc -b
@ -59,6 +61,7 @@ build_deb:arm64:
paths: paths:
- "*.deb" - "*.deb"
script: script:
- rm -f ../*.deb
- apt-get -y build-dep . - apt-get -y build-dep .
- apt-get -y install devscripts - apt-get -y install devscripts
- debuild -i -us -uc -b - debuild -i -us -uc -b

79
Cargo.lock generated
View File

@ -2,20 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.6" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "atk-sys" name = "atk-sys"
version = "0.7.0" version = "0.7.0"
@ -27,16 +19,6 @@ dependencies = [
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.0.4" version = "1.0.4"
@ -77,18 +59,14 @@ name = "clap"
version = "2.32.0" version = "2.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "0.4.4" version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -254,14 +232,6 @@ dependencies = [
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "hermit-abi"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -328,7 +298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.7" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -339,7 +309,7 @@ name = "quote"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -347,16 +317,16 @@ name = "regex"
version = "1.1.9" version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.13" version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -393,9 +363,9 @@ name = "serde_derive"
version = "1.0.104" version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (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.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -403,23 +373,18 @@ name = "serde_yaml"
version = "0.8.11" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.2 (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.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.13" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -455,11 +420,6 @@ name = "utf8-ranges"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.8" version = "0.3.8"
@ -497,16 +457,14 @@ dependencies = [
] ]
[metadata] [metadata]
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5f56c476256dc249def911d6f7580b5fc7e875895b5d7ee88f5d602208035744"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7017e53393e713212aed7aea336b6553be4927f58c37070a56c2fe3d107e489" "checksum atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7017e53393e713212aed7aea336b6553be4927f58c37070a56c2fe3d107e489"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd940f0d609699e343ef71c4af5f66423afbf30d666f796dabd8fd15229cf5b6" "checksum cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd940f0d609699e343ef71c4af5f66423afbf30d666f796dabd8fd15229cf5b6"
"checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2" "checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2"
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9" "checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96" "checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96"
"checksum gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc3aa730cb4df3de5d9fed59f43afdf9e5fb2d3d10bfcbd04cec031435ce87f5" "checksum gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc3aa730cb4df3de5d9fed59f43afdf9e5fb2d3d10bfcbd04cec031435ce87f5"
@ -519,7 +477,6 @@ dependencies = [
"checksum gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08475e4a08f27e6e2287005950114735ed61cec2cb8c1187682a5aec8c69b715" "checksum gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08475e4a08f27e6e2287005950114735ed61cec2cb8c1187682a5aec8c69b715"
"checksum gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a6b30f194f09a17bb7ffa95c3ecdb405abd3b75ff981f831b1f6d18fe115ff" "checksum gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a6b30f194f09a17bb7ffa95c3ecdb405abd3b75ff981f831b1f6d18fe115ff"
"checksum gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d487d333a4b87072e6bf9f2e55befa0ebef01b9496c2e263c0f4a1ff3d6c04b1" "checksum gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d487d333a4b87072e6bf9f2e55befa0ebef01b9496c2e263c0f4a1ff3d6c04b1"
"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
@ -529,21 +486,19 @@ dependencies = [
"checksum pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2cb169402a3eb1ba034a7cc7d95b8b1c106e9be5ba4be79a5a93dc1a2795f4" "checksum pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2cb169402a3eb1ba034a7cc7d95b8b1c106e9be5ba4be79a5a93dc1a2795f4"
"checksum pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6eb49268e69dd0c1da5d3001a61aac08e2e9d2bfbe4ae4b19b9963c998f6453" "checksum pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6eb49268e69dd0c1da5d3001a61aac08e2e9d2bfbe4ae4b19b9963c998f6453"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" "checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" "checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad"
"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" "checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" "checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" "checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
"checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" "checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -4,7 +4,7 @@ version = "0.1.0"
[dependencies] [dependencies]
bitflags = "1.0.*" bitflags = "1.0.*"
clap = "2.32.*" clap = { version = "2.32.*", default-features = false }
maplit = "1.0.*" maplit = "1.0.*"
regex = "1.1.*" regex = "1.1.*"
serde = { version = "1.0.*", features = ["derive"] } serde = { version = "1.0.*", features = ["derive"] }

View File

@ -23,7 +23,7 @@ buttons:
Return: Return:
outline: outline7 outline: outline7
icon: "key-enter" icon: "key-enter"
keysym: "BackSpace" keysym: "Return"
asterisk: asterisk:
text: "*" text: "*"
numbersign: numbersign:

View File

@ -12,12 +12,12 @@ views:
- "q w e r t y u i o p" - "q w e r t y u i o p"
- "a s d f g h j k l" - "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace" - "Shift_L z x c v b n m BackSpace"
- "show_numbers show_actions space preferences Return" - "show_numbers preferences space show_actions Return"
upper: upper:
- "Q W E R T Y U I O P" - "Q W E R T Y U I O P"
- "A S D F G H J K L" - "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace" - "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space period Return" - "show_numbers preferences space show_actions Return"
numbers: numbers:
- "1 2 3 4 5 6 7 8 9 0" - "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )" - "@ # $ % & - _ + ( )"

View File

@ -2,6 +2,7 @@ sq_view {
background-color: rgba(0, 0, 0, 255); background-color: rgba(0, 0, 0, 255);
color: #ffffff; color: #ffffff;
font-family: cantarell, sans-serif; font-family: cantarell, sans-serif;
font-size: 25px;
} }
sq_view sq_button { sq_view sq_button {
@ -35,6 +36,10 @@ sq_button.locked {
color: #2b292f; color: #2b292f;
} }
sq_button.action {
font-size: 0.75em;
}
#Return { #Return {
background: #1c71d8; background: #1c71d8;
border-color: #1a5fb4; border-color: #1a5fb4;

View File

@ -2,6 +2,7 @@ sq_view {
background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/ background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/
color: @theme_text_color; /*#ffffff;*/ color: @theme_text_color; /*#ffffff;*/
font-family: cantarell, sans-serif; font-family: cantarell, sans-serif;
font-size: 25px;
} }
sq_view sq_button { sq_view sq_button {
@ -38,6 +39,10 @@ sq_button.locked {
color: @theme_bg_color; /*#2b292f;*/ color: @theme_bg_color; /*#2b292f;*/
} }
sq_button.action {
font-size: 0.75em;
}
#Return { #Return {
background: @theme_selected_bg_color; /* #1c71d8; */ background: @theme_selected_bg_color; /* #1c71d8; */
border-color: @borders; /*#1a5fb4;*/ border-color: @borders; /*#1a5fb4;*/

53
debian/changelog vendored
View File

@ -1,3 +1,56 @@
squeekboard (1.8.1) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* action: Rename Level to View
* keyboard: Introduce a KeyCode type wrapping u32
* layout: Centralize handling key releases
* layout: Make handling presses uniform
* UI: Drop indirection for show/hide functions
* managers: Move visible flag to UI manager
* dbus_service: Remove unused function
* dbus: Remove unneeded gobjectness
* dbus: Rename handler from eekboard_service
* context: Moved keymap setting together with its generation
* key-emitter: Remove unused
* eekboard_context_service: Drop unused enable property
* services: Split out layout management from EekboardContextService
* submission: Move away from virtual-keyboard
* submission: Create a new wrapper over imservice
* imservice: Limited scope of unsafe
* EekGtkKeyboard: Use a direct reference to EekboardContext
* submission: Take over virtual_keyboard handling
* keyboard: Cleanups of unused code
* levelkeyboard: Drop unused manager references
* keyboard: Gather up keymap handling, drop layout
* submission: Remove wildcard reexport
* imservice: Rename commit_state to done to match protocol
* ci: Clean up `..` before it's searched for artifacts
* dbus: Log error on dbus exit
* logging: Try to improve common operations
* imservice: Return something more resembling an Error on failure
* logging: Unified to remove random eprint calls
* press_key: Use proper logging
* number: Fix keysym for Return
* build: Strip clap of optional features
* layouts: Fix segfault on switching to wide
* font: Use font from style context
* font: Only pass relevant data to label renderer
[ Sebastian Krzyszkowiak ]
* layout: terminal: Swap positions of preferences and actions button
* layout: terminal: Show actions button on all views
* layout: terminal: Replace actions button with period on symbols view
[ Dorota Czaplejewicz ]
* setup: Connect ui to the state manager
* debian: Add missing commas
[ David Boddie ]
* Tidy build file and docs
* Use pip to install recommonmark
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Fri, 31 Jan 2020 09:59:12 +0000
squeekboard (1.8.0) amber-phone; urgency=medium squeekboard (1.8.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ] [ Dorota Czaplejewicz ]

8
debian/control vendored
View File

@ -35,8 +35,8 @@ Architecture: linux-any
Depends: Depends:
# for the Adwaita-dark theme # for the Adwaita-dark theme
gnome-themes-extra-data, gnome-themes-extra-data,
${shlibs:Depends} ${shlibs:Depends},
${misc:Depends} ${misc:Depends},
Description: On-screen keyboard for Wayland Description: On-screen keyboard for Wayland
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone. Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
@ -45,8 +45,8 @@ Architecture: linux-any
Depends: Depends:
python3, python3,
python3-gi, python3-gi,
${shlibs:Depends} ${shlibs:Depends},
${misc:Depends} ${misc:Depends},
Description: Resources for making Squeekboard layouts Description: Resources for making Squeekboard layouts
Tools for creating and testing Squeekboard layouts: Tools for creating and testing Squeekboard layouts:
. .

View File

@ -12,6 +12,10 @@ TARGET_DIR="${1:-./}"
SPHINX=sphinx-build SPHINX=sphinx-build
if [ ! -d $DOCS_DIR/_static ]; then
mkdir -p $DOCS_DIR/_static
fi
if ! which sphinx-build ; then if ! which sphinx-build ; then
SPHINX=sphinx-build-3 SPHINX=sphinx-build-3
fi fi

View File

@ -9,12 +9,12 @@ Contents
Introduction Introduction
------------ ------------
Squeekboard is the on-screen keyobard for the Librem 5 phone. For more information, look at the [README](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md). Squeekboard is the on-screen keyboard for the Librem 5 phone. For more information, look at the [README](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md).
Layouts Layouts
------- -------
Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The tutorial](/tutorial.md) explains the process in detail. Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The [tutorial](/tutorial.md) explains the process in detail.
Layouts are created using a text-based format, based on YAML. Layouts are created using a text-based format, based on YAML.

View File

@ -34,13 +34,15 @@ So at least I will try to start writing a short how-to here and edit this post a
* Additionally take a look at https://source.puri.sm/Librem5/squeekboard/blob/master/HACKING.md#testing * Additionally take a look at https://source.puri.sm/Librem5/squeekboard/blob/master/HACKING.md#testing
* You can either test it locally on your Linux system or use the [QEMU Librem 5 image ](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html) * You can either test it locally on your Linux system or use the [QEMU Librem 5 image ](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html)
* To test squeekboard locally, you need phoc. Either compile that from the sources as well or use the CI repository ci.puri.sm for Debian based systems: * To test squeekboard locally, you need phoc. Either compile that from the sources as well or use the CI repository ci.puri.sm for Debian based systems:
`deb [arch=amd64] http://ci.puri.sm/ scratch librem5` `deb [arch=amd64] http://ci.puri.sm/ scratch librem5`
Squeekboard can be installed from there as a Debian package, too (thats what I often do). But beware - there be dragons! You could bork your system with these packages and you should probably disable this repository again after installing what you need - these packages are not meant for production systems (or so I heard :wink: ) Squeekboard can be installed from there as a Debian package, too (thats what I often do). But beware - there be dragons! You could bork your system with these packages and you should probably disable this repository again after installing what you need - these packages are not meant for production systems (or so I heard :wink: )
**Creating the keyboard layout** **Creating the keyboard layout**
* To be written: For the time being, take a look at [Using non-latin language on Librem 5 ](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5) * To be written: For the time being, take a look at [Using non-latin language on Librem 5 ](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5)
* The correct name of the .yaml file can be found with the command `gsettings get org.gnome.desktop.input-sources sources` * The correct name of the .yaml file can be found with the command `gsettings get org.gnome.desktop.input-sources sources`
The output should be something like this: `[('xkb', 'us'), ('xkb', 'de')]` The output should be something like this: `[('xkb', 'us'), ('xkb', 'de')]`
So f.ex. “de.yaml” would be the correct name for the German keyboard layout. So f.ex. “de.yaml” would be the correct name for the German keyboard layout.
* The translations for the keyboard layout names in the different languages can be found at `data/langs/` * The translations for the keyboard layout names in the different languages can be found at `data/langs/`

View File

@ -39,6 +39,7 @@
#include "eekboard/eekboard-context-service.h" #include "eekboard/eekboard-context-service.h"
#include "src/layout.h" #include "src/layout.h"
#include "src/submission.h"
enum { enum {
PROP_0, PROP_0,
@ -54,6 +55,8 @@ enum {
typedef struct _EekGtkKeyboardPrivate typedef struct _EekGtkKeyboardPrivate
{ {
EekRenderer *renderer; EekRenderer *renderer;
EekboardContextService *eekboard_context; // unowned reference
struct submission *submission; // unowned reference
LevelKeyboard *keyboard; // unowned reference; it's kept in server-context (FIXME) LevelKeyboard *keyboard; // unowned reference; it's kept in server-context (FIXME)
GdkEventSequence *sequence; // unowned reference GdkEventSequence *sequence; // unowned reference
@ -122,7 +125,8 @@ static void depress(EekGtkKeyboard *self,
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
squeek_layout_depress(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, squeek_layout_depress(priv->keyboard->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, self); x, y, eek_renderer_get_transformation(priv->renderer), time, self);
} }
@ -130,18 +134,20 @@ static void drag(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time) gdouble x, gdouble y, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
squeek_layout_drag(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, squeek_layout_drag(priv->keyboard->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, x, y, eek_renderer_get_transformation(priv->renderer), time,
priv->keyboard->manager, self); priv->eekboard_context, self);
} }
static void release(EekGtkKeyboard *self, guint32 time) static void release(EekGtkKeyboard *self, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
squeek_layout_release(priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, squeek_layout_release(priv->keyboard->layout,
priv->submission,
eek_renderer_get_transformation(priv->renderer), time, eek_renderer_get_transformation(priv->renderer), time,
priv->keyboard->manager, self); priv->eekboard_context, self);
} }
static gboolean static gboolean
@ -229,7 +235,8 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
if (priv->keyboard) { if (priv->keyboard) {
squeek_layout_release_all_only( squeek_layout_release_all_only(
priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, priv->keyboard->layout,
priv->submission,
gdk_event_get_time(NULL)); gdk_event_get_time(NULL));
} }
@ -264,7 +271,8 @@ eek_gtk_keyboard_dispose (GObject *object)
if (priv->keyboard) { if (priv->keyboard) {
squeek_layout_release_all_only( squeek_layout_release_all_only(
priv->keyboard->layout, priv->keyboard->manager->virtual_keyboard, priv->keyboard->layout,
priv->submission,
gdk_event_get_time(NULL)); gdk_event_get_time(NULL));
priv->keyboard = NULL; priv->keyboard = NULL;
} }
@ -311,11 +319,14 @@ eek_gtk_keyboard_init (EekGtkKeyboard *self)
* Returns: a #GtkWidget * Returns: a #GtkWidget
*/ */
GtkWidget * GtkWidget *
eek_gtk_keyboard_new (LevelKeyboard *keyboard) eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice,
struct submission *submission)
{ {
EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL)); EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL));
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret); EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret);
priv->keyboard = keyboard; priv->keyboard = keyboard;
priv->eekboard_context = eekservice;
priv->submission = submission;
return GTK_WIDGET(ret); return GTK_WIDGET(ret);
} }

View File

@ -28,6 +28,9 @@
#include <glib.h> #include <glib.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "eek/eek-types.h"
struct submission;
typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs
G_BEGIN_DECLS G_BEGIN_DECLS
@ -45,7 +48,7 @@ struct _EekGtkKeyboardClass
}; };
GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST; GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST;
GtkWidget *eek_gtk_keyboard_new (LevelKeyboard *keyboard); GtkWidget *eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice, struct submission *submission);
G_END_DECLS G_END_DECLS
#endif /* EEK_GTK_KEYBOARD_H */ #endif /* EEK_GTK_KEYBOARD_H */

View File

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

View File

@ -40,16 +40,15 @@ struct _LevelKeyboard {
size_t keymap_len; // length of the data inside keymap_fd size_t keymap_len; // length of the data inside keymap_fd
guint id; // as a key to layout choices guint id; // as a key to layout choices
EekboardContextService *manager; // unowned reference
}; };
typedef struct _LevelKeyboard LevelKeyboard; typedef struct _LevelKeyboard LevelKeyboard;
gchar * eek_keyboard_get_keymap gchar * eek_keyboard_get_keymap
(LevelKeyboard *keyboard); (LevelKeyboard *keyboard);
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout); LevelKeyboard*
void level_keyboard_deinit(LevelKeyboard *self); level_keyboard_new (const gchar *keyboard_type,
enum squeek_arrangement_kind t);
void level_keyboard_free(LevelKeyboard *self); void level_keyboard_free(LevelKeyboard *self);
G_END_DECLS G_END_DECLS

View File

@ -42,22 +42,17 @@ typedef struct _EekRendererPrivate
GtkStyleContext *view_context; // owned GtkStyleContext *view_context; // owned
GtkStyleContext *button_context; // TODO: maybe move a copy to each button GtkStyleContext *button_context; // TODO: maybe move a copy to each button
gdouble border_width; // FIXME: border of what?
gdouble allocation_width; gdouble allocation_width;
gdouble allocation_height; gdouble allocation_height;
gint scale_factor; /* the outputs scale factor */ gint scale_factor; /* the outputs scale factor */
struct transformation widget_to_layout; struct transformation widget_to_layout;
PangoFontDescription *font; // owned reference
} EekRendererPrivate; } EekRendererPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT) G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
/* eek-keyboard-drawing.c */ /* eek-keyboard-drawing.c */
static void eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, GtkStyleContext *ctx, static void render_button_label (cairo_t *cr, GtkStyleContext *ctx,
const struct squeek_button *button); const gchar *label, EekBounds bounds);
void eek_render_button (EekRenderer *self, void eek_render_button (EekRenderer *self,
cairo_t *cr, const struct squeek_button *button, cairo_t *cr, const struct squeek_button *button,
@ -86,8 +81,7 @@ render_outline (cairo_t *cr,
position.x, position.y, position.width, position.height); position.x, position.y, position.width, position.height);
} }
static void render_button_in_context(EekRenderer *self, static void render_button_in_context(gint scale_factor,
gint scale_factor,
cairo_t *cr, cairo_t *cr,
GtkStyleContext *ctx, GtkStyleContext *ctx,
const struct squeek_button *button) { const struct squeek_button *button) {
@ -130,7 +124,11 @@ static void render_button_in_context(EekRenderer *self,
return; return;
} }
} }
eek_renderer_render_button_label (self, cr, ctx, button);
const gchar *label = squeek_button_get_label(button);
if (label) {
render_button_label (cr, ctx, label, squeek_button_get_bounds(button));
}
} }
void void
@ -162,7 +160,7 @@ eek_render_button (EekRenderer *self,
} }
gtk_style_context_add_class(ctx, outline_name); gtk_style_context_add_class(ctx, outline_name);
render_button_in_context(self, priv->scale_factor, cr, ctx, button); render_button_in_context(priv->scale_factor, cr, ctx, button);
// Save and restore functions don't work if gtk_render_* was used in between // Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL); gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
@ -173,43 +171,16 @@ eek_render_button (EekRenderer *self,
} }
static void static void
eek_renderer_render_button_label (EekRenderer *self, render_button_label (cairo_t *cr,
cairo_t *cr, GtkStyleContext *ctx,
GtkStyleContext *ctx, const gchar *label,
const struct squeek_button *button) EekBounds bounds)
{ {
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
const gchar *label = squeek_button_get_label(button);
if (!label) {
return;
}
PangoFontDescription *font; PangoFontDescription *font;
gdouble scale; gtk_style_context_get(ctx,
gtk_style_context_get_state(ctx),
"font", &font,
if (!priv->font) { NULL);
const PangoFontDescription *base_font;
gdouble size;
base_font = pango_context_get_font_description (priv->pcontext);
// FIXME: Base font size on the same size unit used for button sizing,
// and make the default about 1/3 of the current row height
size = 30000.0;
priv->font = pango_font_description_copy (base_font);
pango_font_description_set_size (priv->font, (gint)round(size * 0.6));
}
EekBounds bounds = squeek_button_get_bounds(button);
scale = MIN((bounds.width - priv->border_width) / bounds.width,
(bounds.height - priv->border_width) / bounds.height);
font = pango_font_description_copy (priv->font);
pango_font_description_set_size (font,
(gint)round(pango_font_description_get_size (font) * scale));
PangoLayout *layout = pango_cairo_create_layout (cr); PangoLayout *layout = pango_cairo_create_layout (cr);
pango_layout_set_font_description (layout, font); pango_layout_set_font_description (layout, font);
pango_font_description_free (font); pango_font_description_free (font);
@ -219,8 +190,7 @@ eek_renderer_render_button_label (EekRenderer *self,
if (line->resolved_dir == PANGO_DIRECTION_RTL) { if (line->resolved_dir == PANGO_DIRECTION_RTL) {
pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT); pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
} }
pango_layout_set_width (layout, pango_layout_set_width (layout, PANGO_SCALE * bounds.width);
PANGO_SCALE * bounds.width * scale);
PangoRectangle extents = { 0, }; PangoRectangle extents = { 0, };
pango_layout_get_extents (layout, NULL, &extents); pango_layout_get_extents (layout, NULL, &extents);
@ -331,7 +301,6 @@ eek_renderer_finalize (GObject *object)
g_object_unref(priv->css_provider); g_object_unref(priv->css_provider);
g_object_unref(priv->view_context); g_object_unref(priv->view_context);
g_object_unref(priv->button_context); g_object_unref(priv->button_context);
pango_font_description_free (priv->font);
G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object); G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object);
} }
@ -390,11 +359,9 @@ eek_renderer_init (EekRenderer *self)
priv->keyboard = NULL; priv->keyboard = NULL;
priv->pcontext = NULL; priv->pcontext = NULL;
priv->border_width = 1.0;
priv->allocation_width = 0.0; priv->allocation_width = 0.0;
priv->allocation_height = 0.0; priv->allocation_height = 0.0;
priv->scale_factor = 1; priv->scale_factor = 1;
priv->font = NULL;
GtkIconTheme *theme = gtk_icon_theme_get_default (); GtkIconTheme *theme = gtk_icon_theme_get_default ();

View File

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

View File

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

View File

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

View File

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

View File

@ -16,31 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/**
* SECTION:eekboard-context-service
* @short_description: base server implementation of eekboard input
* context service
*
* The #EekboardService class provides a base server side
* implementation of eekboard input context service.
*/
#include "config.h" #include "config.h"
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#define _XOPEN_SOURCE 500
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h> // TODO: this is Linux-specific
#include <xkbcommon/xkbcommon.h>
#include <gio/gio.h> #include <gio/gio.h>
#include "eekboard/key-emitter.h"
#include "wayland.h" #include "wayland.h"
#include "eek/eek-xml-layout.h" #include "eek/eek-keyboard.h"
#include "src/server-context-service.h" #include "src/server-context-service.h"
#include "eekboard/eekboard-context-service.h" #include "eekboard/eekboard-context-service.h"
@ -48,13 +32,10 @@
enum { enum {
PROP_0, // Magic: without this, keyboard is not useable in g_object_notify PROP_0, // Magic: without this, keyboard is not useable in g_object_notify
PROP_KEYBOARD, PROP_KEYBOARD,
PROP_VISIBLE,
PROP_LAST PROP_LAST
}; };
enum { enum {
ENABLED,
DISABLED,
DESTROYED, DESTROYED,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -65,105 +46,32 @@ static guint signals[LAST_SIGNAL] = { 0, };
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServicePrivate)) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServicePrivate))
struct _EekboardContextServicePrivate { struct _EekboardContextServicePrivate {
gboolean enabled;
gboolean visible;
LevelKeyboard *keyboard; // currently used keyboard LevelKeyboard *keyboard; // currently used keyboard
GHashTable *keyboard_hash; // a table of available keyboards, per layout GHashTable *keyboard_hash; // a table of available keyboards, per layout
char *overlay; char *overlay;
GSettings *settings; GSettings *settings; // Owned reference
uint32_t hint; uint32_t hint;
uint32_t purpose; uint32_t purpose;
// Maybe TODO: it's used only for fetching layout type.
// Maybe let UI push the type to this structure?
ServerContextService *ui; // unowned reference
/// Needed for keymap changes after keyboard updates
struct submission *submission; // unowned
}; };
G_DEFINE_TYPE_WITH_PRIVATE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT);
static LevelKeyboard *
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
const gchar *keyboard_type,
enum squeek_arrangement_kind t)
{
LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self, t);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
g_error("No context created");
}
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap)
g_error("Bad keymap:\n%s", keymap_str);
xkb_context_unref(context);
keyboard->keymap = keymap;
keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
keyboard->keymap_len = strlen(keymap_str) + 1;
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
char *r = &path[strlen(path) - 6];
getrandom(r, 6, GRND_NONBLOCK);
for (unsigned i = 0; i < 6; i++) {
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
}
int keymap_fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
if (keymap_fd < 0) {
g_error("Failed to set up keymap fd");
}
keyboard->keymap_fd = keymap_fd;
shm_unlink(path);
if (ftruncate(keymap_fd, (off_t)keyboard->keymap_len)) {
g_error("Failed to increase keymap fd size");
}
char *ptr = mmap(NULL, keyboard->keymap_len, PROT_WRITE, MAP_SHARED,
keymap_fd, 0);
if ((void*)ptr == (void*)-1) {
g_error("Failed to set up mmap");
}
strncpy(ptr, keymap_str, keyboard->keymap_len);
munmap(ptr, keyboard->keymap_len);
return keyboard;
}
static void
eekboard_context_service_real_show_keyboard (EekboardContextService *self)
{
self->priv->visible = TRUE;
}
static void
eekboard_context_service_real_hide_keyboard (EekboardContextService *self)
{
self->priv->visible = FALSE;
}
static void static void
eekboard_context_service_set_property (GObject *object, eekboard_context_service_set_property (GObject *object,
guint prop_id, guint prop_id,
const GValue *value, const GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object); (void)value;
switch (prop_id) { switch (prop_id) {
case PROP_KEYBOARD:
if (context->priv->keyboard)
g_object_unref (context->priv->keyboard);
context->priv->keyboard = g_value_get_object (value);
break;
case PROP_VISIBLE:
context->priv->visible = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -182,9 +90,6 @@ eekboard_context_service_get_property (GObject *object,
case PROP_KEYBOARD: case PROP_KEYBOARD:
g_value_set_object (value, context->priv->keyboard); g_value_set_object (value, context->priv->keyboard);
break; break;
case PROP_VISIBLE:
g_value_set_boolean (value, context->priv->visible);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -244,10 +149,15 @@ eekboard_context_service_update_layout(EekboardContextService *context, enum squ
} }
// generic part follows // generic part follows
LevelKeyboard *keyboard = eekboard_context_service_real_create_keyboard(context, keyboard_layout, t); LevelKeyboard *keyboard = level_keyboard_new(keyboard_layout, t);
// set as current // set as current
LevelKeyboard *previous_keyboard = context->priv->keyboard; LevelKeyboard *previous_keyboard = context->priv->keyboard;
context->priv->keyboard = keyboard; context->priv->keyboard = keyboard;
// Update the keymap if necessary.
// TODO: Update submission on change event
if (context->priv->submission) {
submission_set_keyboard(context->priv->submission, keyboard);
}
g_object_notify (G_OBJECT(context), "keyboard"); g_object_notify (G_OBJECT(context), "keyboard");
@ -258,7 +168,12 @@ eekboard_context_service_update_layout(EekboardContextService *context, enum squ
} }
static void update_layout_and_type(EekboardContextService *context) { static void update_layout_and_type(EekboardContextService *context) {
eekboard_context_service_update_layout(context, server_context_service_get_layout_type(context)); EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
enum squeek_arrangement_kind layout_kind = ARRANGEMENT_KIND_BASE;
if (priv->ui) {
layout_kind = server_context_service_get_layout_type(priv->ui);
}
eekboard_context_service_update_layout(context, layout_kind);
} }
static gboolean static gboolean
@ -279,12 +194,6 @@ static void
eekboard_context_service_constructed (GObject *object) eekboard_context_service_constructed (GObject *object)
{ {
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE (object); EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE (object);
context->virtual_keyboard = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
squeek_wayland->virtual_keyboard_manager,
squeek_wayland->seat);
if (!context->virtual_keyboard) {
g_error("Programmer error: Failed to receive a virtual keyboard instance");
}
update_layout_and_type(context); update_layout_and_type(context);
} }
@ -294,48 +203,10 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec; GParamSpec *pspec;
klass->show_keyboard = eekboard_context_service_real_show_keyboard;
klass->hide_keyboard = eekboard_context_service_real_hide_keyboard;
gobject_class->constructed = eekboard_context_service_constructed; gobject_class->constructed = eekboard_context_service_constructed;
gobject_class->set_property = eekboard_context_service_set_property; gobject_class->set_property = eekboard_context_service_set_property;
gobject_class->get_property = eekboard_context_service_get_property; gobject_class->get_property = eekboard_context_service_get_property;
gobject_class->dispose = eekboard_context_service_dispose; gobject_class->dispose = eekboard_context_service_dispose;
/**
* EekboardContextService::enabled:
* @context: an #EekboardContextService
*
* Emitted when @context is enabled.
*/
signals[ENABLED] =
g_signal_new (I_("enabled"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekboardContextServiceClass, enabled),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/**
* EekboardContextService::disabled:
* @context: an #EekboardContextService
*
* Emitted when @context is enabled.
*/
signals[DISABLED] =
g_signal_new (I_("disabled"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekboardContextServiceClass, disabled),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/** /**
* EekboardContextService::destroyed: * EekboardContextService::destroyed:
* @context: an #EekboardContextService * @context: an #EekboardContextService
@ -361,24 +232,10 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
pspec = g_param_spec_pointer("keyboard", pspec = g_param_spec_pointer("keyboard",
"Keyboard", "Keyboard",
"Keyboard", "Keyboard",
G_PARAM_READWRITE); G_PARAM_READABLE);
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_KEYBOARD, PROP_KEYBOARD,
pspec); pspec);
/**
* EekboardContextService:visible:
*
* Flag to indicate if keyboard is visible or not.
*/
pspec = g_param_spec_boolean ("visible",
"Visible",
"Visible",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_VISIBLE,
pspec);
} }
static void static void
@ -404,62 +261,6 @@ eekboard_context_service_init (EekboardContextService *self)
self->priv->overlay = NULL; self->priv->overlay = NULL;
} }
/**
* eekboard_context_service_enable:
* @context: an #EekboardContextService
*
* Enable @context. This function is called when @context is pushed
* by eekboard_service_push_context().
*/
void
eekboard_context_service_enable (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (!context->priv->enabled) {
context->priv->enabled = TRUE;
g_signal_emit (context, signals[ENABLED], 0);
}
}
/**
* eekboard_context_service_disable:
* @context: an #EekboardContextService
*
* Disable @context. This function is called when @context is pushed
* by eekboard_service_pop_context().
*/
void
eekboard_context_service_disable (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (context->priv->enabled) {
context->priv->enabled = FALSE;
g_signal_emit (context, signals[DISABLED], 0);
}
}
void
eekboard_context_service_show_keyboard (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (!context->priv->visible) {
EEKBOARD_CONTEXT_SERVICE_GET_CLASS(context)->show_keyboard (context);
}
}
void
eekboard_context_service_hide_keyboard (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (context->priv->visible) {
EEKBOARD_CONTEXT_SERVICE_GET_CLASS(context)->hide_keyboard (context);
}
}
/** /**
* eekboard_context_service_destroy: * eekboard_context_service_destroy:
* @context: an #EekboardContextService * @context: an #EekboardContextService
@ -471,9 +272,6 @@ eekboard_context_service_destroy (EekboardContextService *context)
{ {
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context)); g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
if (context->priv->enabled) {
eekboard_context_service_disable (context);
}
g_free(context->priv->overlay); g_free(context->priv->overlay);
g_signal_emit (context, signals[DESTROYED], 0); g_signal_emit (context, signals[DESTROYED], 0);
} }
@ -491,14 +289,6 @@ eekboard_context_service_get_keyboard (EekboardContextService *context)
return context->priv->keyboard; return context->priv->keyboard;
} }
void eekboard_context_service_set_keymap(EekboardContextService *context,
const LevelKeyboard *keyboard)
{
zwp_virtual_keyboard_v1_keymap(context->virtual_keyboard,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keyboard->keymap_fd, keyboard->keymap_len);
}
void eekboard_context_service_set_hint_purpose(EekboardContextService *context, void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint, uint32_t purpose) uint32_t hint, uint32_t purpose)
{ {
@ -521,3 +311,19 @@ const char*
eekboard_context_service_get_overlay(EekboardContextService *context) { eekboard_context_service_get_overlay(EekboardContextService *context) {
return context->priv->overlay; return context->priv->overlay;
} }
EekboardContextService *eekboard_context_service_new(void)
{
return g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
}
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
context->priv->submission = submission;
if (context->priv->submission) {
submission_set_keyboard(context->priv->submission, context->priv->keyboard);
}
}
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
context->priv->ui = ui;
}

View File

@ -22,7 +22,8 @@
#ifndef EEKBOARD_CONTEXT_SERVICE_H #ifndef EEKBOARD_CONTEXT_SERVICE_H
#define EEKBOARD_CONTEXT_SERVICE_H 1 #define EEKBOARD_CONTEXT_SERVICE_H 1
#include <eek/eek.h> #include "src/submission.h"
#include "src/layout.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h"
@ -46,6 +47,8 @@ typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
/** /**
* EekboardContextService: * EekboardContextService:
* *
* Handles layout state, gsettings, and virtual-keyboard.
*
* TODO: Restrict to managing keyboard layouts, and maybe button repeats, * TODO: Restrict to managing keyboard layouts, and maybe button repeats,
* and the virtual keyboard protocol. * and the virtual keyboard protocol.
* *
@ -56,15 +59,11 @@ struct _EekboardContextService {
GObject parent; GObject parent;
EekboardContextServicePrivate *priv; EekboardContextServicePrivate *priv;
struct zwp_virtual_keyboard_v1 *virtual_keyboard;
}; };
/** /**
* EekboardContextServiceClass: * EekboardContextServiceClass:
* @create_keyboard: virtual function for create a keyboard from string * @create_keyboard: virtual function for create a keyboard from string
* @show_keyboard: virtual function for show a keyboard
* @hide_keyboard: virtual function for hide a keyboard
* @enabled: class handler for #EekboardContextService::enabled signal * @enabled: class handler for #EekboardContextService::enabled signal
* @disabled: class handler for #EekboardContextService::disabled signal * @disabled: class handler for #EekboardContextService::disabled signal
*/ */
@ -75,12 +74,8 @@ struct _EekboardContextServiceClass {
/*< public >*/ /*< public >*/
struct squeek_view *(*create_keyboard) (EekboardContextService *self, struct squeek_view *(*create_keyboard) (EekboardContextService *self,
const gchar *keyboard_type); const gchar *keyboard_type);
void (*show_keyboard) (EekboardContextService *self);
void (*hide_keyboard) (EekboardContextService *self);
/* signals */ /* signals */
void (*enabled) (EekboardContextService *self);
void (*disabled) (EekboardContextService *self);
void (*destroyed) (EekboardContextService *self); void (*destroyed) (EekboardContextService *self);
/*< private >*/ /*< private >*/
@ -90,12 +85,9 @@ struct _EekboardContextServiceClass {
GType eekboard_context_service_get_type GType eekboard_context_service_get_type
(void) G_GNUC_CONST; (void) G_GNUC_CONST;
void eekboard_context_service_enable (EekboardContextService *context); EekboardContextService *eekboard_context_service_new(void);
void eekboard_context_service_disable (EekboardContextService *context); void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission);
void eekboard_context_service_show_keyboard void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
(EekboardContextService *context);
void eekboard_context_service_hide_keyboard
(EekboardContextService *context);
void eekboard_context_service_destroy (EekboardContextService *context); void eekboard_context_service_destroy (EekboardContextService *context);
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context); LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);

View File

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

View File

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

View File

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

View File

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

View File

@ -6,8 +6,8 @@ use std::ffi::CString;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct KeySym(pub String); pub struct KeySym(pub String);
/// Use to switch layouts /// Use to switch views
type Level = String; type View = String;
/// Use to send modified keypresses /// Use to send modified keypresses
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -20,12 +20,12 @@ pub enum Modifier {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Action { pub enum Action {
/// Switch to this view /// Switch to this view
SetLevel(Level), SetView(View),
/// Switch to a view and latch /// Switch to a view and latch
LockLevel { LockView {
lock: Level, lock: View,
/// When unlocked by pressing it or emitting a key /// When unlocked by pressing it or emitting a key
unlock: Level, unlock: View,
}, },
/// Set this modifier TODO: release? /// Set this modifier TODO: release?
SetModifier(Modifier), SetModifier(Modifier),

View File

@ -21,7 +21,7 @@ use ::keyboard::{
}; };
use ::layout; use ::layout;
use ::layout::ArrangementKind; use ::layout::ArrangementKind;
use ::logging::PrintWarnings; use ::logging;
use ::resources; use ::resources;
use ::util::c::as_str; use ::util::c::as_str;
use ::util::hash_map_map; use ::util::hash_map_map;
@ -31,7 +31,7 @@ use ::xdg;
use serde::Deserialize; use serde::Deserialize;
use std::io::BufReader; use std::io::BufReader;
use std::iter::FromIterator; use std::iter::FromIterator;
use ::logging::WarningHandler; use ::logging::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
@ -157,7 +157,7 @@ fn list_layout_sources(
fn load_layout_data(source: DataSource) fn load_layout_data(source: DataSource)
-> Result<::layout::LayoutData, LoadError> -> Result<::layout::LayoutData, LoadError>
{ {
let handler = PrintWarnings{}; let handler = logging::Print {};
match source { match source {
DataSource::File(path) => { DataSource::File(path) => {
Layout::from_file(path.clone()) Layout::from_file(path.clone())
@ -190,16 +190,21 @@ fn load_layout_data_with_fallback(
( (
LoadError::BadData(Error::Missing(e)), LoadError::BadData(Error::Missing(e)),
DataSource::File(file) DataSource::File(file)
) => eprintln!( // TODO: print in debug logging level ) => log_print!(
logging::Level::Debug,
"Tried file {:?}, but it's missing: {}", "Tried file {:?}, but it's missing: {}",
file, e file, e
), ),
(e, source) => eprintln!( (e, source) => log_print!(
logging::Level::Warning,
"Failed to load layout from {}: {}, skipping", "Failed to load layout from {}: {}, skipping",
source, e source, e
), ),
}, },
Ok(layout) => return (kind, layout), Ok(layout) => {
log_print!(logging::Level::Info, "Loaded layout {}", source);
return (kind, layout);
}
} }
} }
@ -330,7 +335,7 @@ impl Layout {
serde_yaml::from_reader(infile).map_err(Error::Yaml) serde_yaml::from_reader(infile).map_err(Error::Yaml)
} }
pub fn build<H: WarningHandler>(self, mut warning_handler: H) pub fn build<H: logging::Handler>(self, mut warning_handler: H)
-> (Result<::layout::LayoutData, FormattingError>, H) -> (Result<::layout::LayoutData, FormattingError>, H)
{ {
let button_names = self.views.values() let button_names = self.views.values()
@ -464,7 +469,7 @@ impl Layout {
} }
} }
fn create_action<H: WarningHandler>( fn create_action<H: logging::Handler>(
button_info: &HashMap<String, ButtonMeta>, button_info: &HashMap<String, ButtonMeta>,
name: &str, name: &str,
view_names: Vec<&String>, view_names: Vec<&String>,
@ -494,15 +499,18 @@ fn create_action<H: WarningHandler>(
(None, None, Some(text)) => SubmitData::Text(text.clone()), (None, None, Some(text)) => SubmitData::Text(text.clone()),
(None, None, None) => SubmitData::Text(name.into()), (None, None, None) => SubmitData::Text(name.into()),
_ => { _ => {
warning_handler.handle(&format!( warning_handler.handle(
"Button {} has more than one of (action, keysym, text)", logging::Level::Warning,
name &format!(
)); "Button {} has more than one of (action, keysym, text)",
name,
),
);
SubmitData::Text("".into()) SubmitData::Text("".into())
}, },
}; };
fn filter_view_name<H: WarningHandler>( fn filter_view_name<H: logging::Handler>(
button_name: &str, button_name: &str,
view_name: String, view_name: String,
view_names: &Vec<&String>, view_names: &Vec<&String>,
@ -511,10 +519,13 @@ fn create_action<H: WarningHandler>(
if view_names.contains(&&view_name) { if view_names.contains(&&view_name) {
view_name view_name
} else { } else {
warning_handler.handle(&format!("Button {} switches to missing view {}", warning_handler.handle(
button_name, logging::Level::Warning,
view_name, &format!("Button {} switches to missing view {}",
)); button_name,
view_name,
),
);
"base".into() "base".into()
} }
} }
@ -522,7 +533,7 @@ fn create_action<H: WarningHandler>(
match submission { match submission {
SubmitData::Action( SubmitData::Action(
Action::SetView(view_name) Action::SetView(view_name)
) => ::action::Action::SetLevel( ) => ::action::Action::SetView(
filter_view_name( filter_view_name(
name, view_name.clone(), &view_names, name, view_name.clone(), &view_names,
warning_handler, warning_handler,
@ -530,7 +541,7 @@ fn create_action<H: WarningHandler>(
), ),
SubmitData::Action(Action::Locking { SubmitData::Action(Action::Locking {
lock_view, unlock_view lock_view, unlock_view
}) => ::action::Action::LockLevel { }) => ::action::Action::LockView {
lock: filter_view_name( lock: filter_view_name(
name, name,
lock_view.clone(), lock_view.clone(),
@ -553,27 +564,24 @@ fn create_action<H: WarningHandler>(
match keysym_valid(keysym.as_str()) { match keysym_valid(keysym.as_str()) {
true => keysym.clone(), true => keysym.clone(),
false => { false => {
warning_handler.handle(&format!( warning_handler.handle(
"Keysym name invalid: {}", logging::Level::Warning,
keysym, &format!(
)); "Keysym name invalid: {}",
keysym,
),
);
"space".into() // placeholder "space".into() // placeholder
}, },
} }
)), )),
}, },
SubmitData::Text(text) => ::action::Action::Submit { SubmitData::Text(text) => ::action::Action::Submit {
text: { text: CString::new(text.clone()).or_warn(
CString::new(text.clone()) warning_handler,
.map_err(|e| { logging::Problem::Warning,
warning_handler.handle(&format!( &format!("Text {} contains problems", text),
"Text {} contains problems: {:?}", ),
text,
e
));
e
}).ok()
},
keys: text.chars().map(|codepoint| { keys: text.chars().map(|codepoint| {
let codepoint_string = codepoint.to_string(); let codepoint_string = codepoint.to_string();
::action::KeySym(match keysym_valid(codepoint_string.as_str()) { ::action::KeySym(match keysym_valid(codepoint_string.as_str()) {
@ -587,7 +595,7 @@ fn create_action<H: WarningHandler>(
/// TODO: Since this will receive user-provided data, /// TODO: Since this will receive user-provided data,
/// all .expect() on them should be turned into soft fails /// all .expect() on them should be turned into soft fails
fn create_button<H: WarningHandler>( fn create_button<H: logging::Handler>(
button_info: &HashMap<String, ButtonMeta>, button_info: &HashMap<String, ButtonMeta>,
outlines: &HashMap<String, Outline>, outlines: &HashMap<String, Outline>,
name: &str, name: &str,
@ -611,14 +619,11 @@ fn create_button<H: WarningHandler>(
} else if let Some(text) = &button_meta.text { } else if let Some(text) = &button_meta.text {
::layout::Label::Text( ::layout::Label::Text(
CString::new(text.as_str()) CString::new(text.as_str())
.unwrap_or_else(|e| { .or_warn(
warning_handler.handle(&format!( warning_handler,
"Text {} is invalid: {}", logging::Problem::Warning,
text, &format!("Text {} is invalid", text),
e, ).unwrap_or_else(|| CString::new("").unwrap())
));
CString::new("").unwrap()
})
) )
} else { } else {
::layout::Label::Text(cname.clone()) ::layout::Label::Text(cname.clone())
@ -629,7 +634,10 @@ fn create_button<H: WarningHandler>(
if outlines.contains_key(outline) { if outlines.contains_key(outline) {
outline.clone() outline.clone()
} else { } else {
warning_handler.handle(&format!("Outline named {} does not exist! Using default for button {}", outline, name)); warning_handler.handle(
logging::Level::Warning,
&format!("Outline named {} does not exist! Using default for button {}", outline, name)
);
"default".into() "default".into()
} }
} }
@ -638,12 +646,11 @@ fn create_button<H: WarningHandler>(
let outline = outlines.get(&outline_name) let outline = outlines.get(&outline_name)
.map(|outline| (*outline).clone()) .map(|outline| (*outline).clone())
.unwrap_or_else(|| { .or_warn(
warning_handler.handle( warning_handler,
&format!("No default outline defined! Using 1x1!") logging::Problem::Warning,
); "No default outline defined! Using 1x1!",
Outline { width: 1f64, height: 1f64 } ).unwrap_or(Outline { width: 1f64, height: 1f64 });
});
layout::Button { layout::Button {
name: cname, name: cname,
@ -663,7 +670,7 @@ mod tests {
use super::*; use super::*;
use std::error::Error as ErrorTrait; use std::error::Error as ErrorTrait;
use ::logging::PanicWarn; use ::logging::ProblemPanic;
#[test] #[test]
fn test_parse_path() { fn test_parse_path() {
@ -733,7 +740,7 @@ mod tests {
fn test_layout_punctuation() { fn test_layout_punctuation() {
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
.unwrap() .unwrap()
.build(PanicWarn).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -748,7 +755,7 @@ mod tests {
fn test_layout_unicode() { fn test_layout_unicode() {
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
.unwrap() .unwrap()
.build(PanicWarn).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -764,7 +771,7 @@ mod tests {
fn test_layout_unicode_multi() { fn test_layout_unicode_multi() {
let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
.unwrap() .unwrap()
.build(PanicWarn).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -779,7 +786,7 @@ mod tests {
#[test] #[test]
fn parsing_fallback() { fn parsing_fallback() {
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME) assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
.map(|layout| layout.build(PanicWarn).0.unwrap()) .map(|layout| layout.build(ProblemPanic).0.unwrap())
.is_ok() .is_ok()
); );
} }
@ -827,7 +834,7 @@ mod tests {
}, },
".", ".",
Vec::new(), Vec::new(),
&mut PanicWarn, &mut ProblemPanic,
), ),
::action::Action::Submit { ::action::Action::Submit {
text: Some(CString::new(".").unwrap()), text: Some(CString::new(".").unwrap()),
@ -840,7 +847,7 @@ mod tests {
fn test_layout_margins() { fn test_layout_margins() {
let out = Layout::from_file(PathBuf::from("tests/layout_margins.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_margins.yaml"))
.unwrap() .unwrap()
.build(PanicWarn).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.margins, out.margins,

124
src/dbus.c Normal file
View File

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

48
src/dbus.h Normal file
View File

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

View File

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

View File

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

View File

@ -1,58 +1,47 @@
use std::boxed::Box; use std::boxed::Box;
use std::ffi::CString; use std::ffi::CString;
use std::fmt;
use std::num::Wrapping; use std::num::Wrapping;
use std::string::String; use std::string::String;
use ::logging;
use ::util::c::into_cstring; use ::util::c::into_cstring;
// Traits // Traits
use std::convert::TryFrom; use std::convert::TryFrom;
use ::logging::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
use super::*; use super::*;
use std::os::raw::{c_char, c_void}; use std::os::raw::{c_char, c_void};
pub use ::submission::c::UIManager;
pub use ::submission::c::StateManager;
// The following defined in C // The following defined in C
/// struct zwp_input_method_v2* /// struct zwp_input_method_v2*
#[repr(transparent)] #[repr(transparent)]
pub struct InputMethod(*const c_void); pub struct InputMethod(*const c_void);
/// EekboardContextService*
#[repr(transparent)]
pub struct UIManager(*const c_void);
#[no_mangle] #[no_mangle]
extern "C" { extern "C" {
fn imservice_destroy_im(im: *mut c::InputMethod); fn imservice_destroy_im(im: *mut c::InputMethod);
fn eekboard_context_service_set_hint_purpose(imservice: *const UIManager, hint: u32, purpose: u32); #[allow(improper_ctypes)] // IMService will never be dereferenced in C
fn eekboard_context_service_show_keyboard(imservice: *const UIManager); pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
fn eekboard_context_service_hide_keyboard(imservice: *const UIManager); fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32);
fn server_context_service_show_keyboard(imservice: *const UIManager);
fn server_context_service_hide_keyboard(imservice: *const UIManager);
} }
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
#[no_mangle]
pub unsafe extern "C"
fn imservice_new(im: *const InputMethod, ui_manager: *const UIManager) -> *mut IMService {
Box::<IMService>::into_raw(Box::new(
IMService {
im: im,
ui_manager: ui_manager,
pending: IMProtocolState::default(),
current: IMProtocolState::default(),
preedit_string: String::new(),
serial: Wrapping(0u32),
}
))
}
// TODO: is unsafe needed here? // TODO: is unsafe needed here?
#[no_mangle] #[no_mangle]
pub unsafe extern "C" pub extern "C"
fn imservice_handle_input_method_activate(imservice: *mut IMService, fn imservice_handle_input_method_activate(imservice: *mut IMService,
im: *const InputMethod) im: *const InputMethod)
{ {
@ -65,7 +54,7 @@ pub mod c {
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" pub extern "C"
fn imservice_handle_input_method_deactivate(imservice: *mut IMService, fn imservice_handle_input_method_deactivate(imservice: *mut IMService,
im: *const InputMethod) im: *const InputMethod)
{ {
@ -77,7 +66,7 @@ pub mod c {
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" pub extern "C"
fn imservice_handle_surrounding_text(imservice: *mut IMService, fn imservice_handle_surrounding_text(imservice: *mut IMService,
im: *const InputMethod, im: *const InputMethod,
text: *const c_char, cursor: u32, _anchor: u32) text: *const c_char, cursor: u32, _anchor: u32)
@ -93,7 +82,7 @@ pub mod c {
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" pub extern "C"
fn imservice_handle_content_type(imservice: *mut IMService, fn imservice_handle_content_type(imservice: *mut IMService,
im: *const InputMethod, im: *const InputMethod,
hint: u32, purpose: u32) hint: u32, purpose: u32)
@ -101,23 +90,27 @@ pub mod c {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
imservice.pending = IMProtocolState { imservice.pending = IMProtocolState {
content_hint: { content_hint: {
ContentHint::from_bits(hint).unwrap_or_else(|| { ContentHint::from_bits(hint)
eprintln!("Warning: received invalid hint flags"); .or_print(
ContentHint::NONE logging::Problem::Warning,
}) "Received invalid hint flags",
)
.unwrap_or(ContentHint::NONE)
}, },
content_purpose: { content_purpose: {
ContentPurpose::try_from(purpose).unwrap_or_else(|_e| { ContentPurpose::try_from(purpose)
eprintln!("Warning: Received invalid purpose value"); .or_print(
ContentPurpose::Normal logging::Problem::Warning,
}) "Received invalid purpose value",
)
.unwrap_or(ContentPurpose::Normal)
}, },
..imservice.pending.clone() ..imservice.pending.clone()
}; };
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" pub extern "C"
fn imservice_handle_text_change_cause(imservice: *mut IMService, fn imservice_handle_text_change_cause(imservice: *mut IMService,
im: *const InputMethod, im: *const InputMethod,
cause: u32) cause: u32)
@ -125,56 +118,68 @@ pub mod c {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
imservice.pending = IMProtocolState { imservice.pending = IMProtocolState {
text_change_cause: { text_change_cause: {
ChangeCause::try_from(cause).unwrap_or_else(|_e| { ChangeCause::try_from(cause)
eprintln!("Warning: received invalid cause value"); .or_print(
ChangeCause::InputMethod logging::Problem::Warning,
}) "Received invalid cause value",
)
.unwrap_or(ChangeCause::InputMethod)
}, },
..imservice.pending.clone() ..imservice.pending.clone()
}; };
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" pub extern "C"
fn imservice_handle_commit_state(imservice: *mut IMService, fn imservice_handle_done(imservice: *mut IMService,
im: *const InputMethod) im: *const InputMethod)
{ {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
let active_changed = imservice.current.active ^ imservice.pending.active; let active_changed = imservice.current.active ^ imservice.pending.active;
imservice.serial += Wrapping(1u32);
imservice.current = imservice.pending.clone(); imservice.current = imservice.pending.clone();
imservice.pending = IMProtocolState { imservice.pending = IMProtocolState {
active: imservice.current.active, active: imservice.current.active,
..IMProtocolState::default() ..IMProtocolState::default()
}; };
if active_changed { if active_changed {
if imservice.current.active { if imservice.current.active {
eekboard_context_service_show_keyboard(imservice.ui_manager); if let Some(ui) = imservice.ui_manager {
eekboard_context_service_set_hint_purpose( unsafe { server_context_service_show_keyboard(ui); }
imservice.ui_manager, }
imservice.current.content_hint.bits(), unsafe {
imservice.current.content_purpose.clone() as u32); eekboard_context_service_set_hint_purpose(
imservice.state_manager,
imservice.current.content_hint.bits(),
imservice.current.content_purpose.clone() as u32,
);
}
} else { } else {
eekboard_context_service_hide_keyboard(imservice.ui_manager); if let Some(ui) = imservice.ui_manager {
unsafe { server_context_service_hide_keyboard(ui); }
}
} }
} }
} }
// TODO: this is really untested
#[no_mangle] #[no_mangle]
pub unsafe extern "C" pub extern "C"
fn imservice_handle_unavailable(imservice: *mut IMService, fn imservice_handle_unavailable(imservice: *mut IMService,
im: *mut InputMethod) im: *mut InputMethod)
{ {
let imservice = check_imservice(imservice, im).unwrap(); let imservice = check_imservice(imservice, im).unwrap();
imservice_destroy_im(im); unsafe { imservice_destroy_im(im); }
// no need to care about proper double-buffering, // no need to care about proper double-buffering,
// the keyboard is already decommissioned // the keyboard is already decommissioned
imservice.current.active = false; imservice.current.active = false;
eekboard_context_service_hide_keyboard(imservice.ui_manager); if let Some(ui) = imservice.ui_manager {
} unsafe { server_context_service_hide_keyboard(ui); }
}
}
// FIXME: destroy and deallocate // FIXME: destroy and deallocate
@ -246,10 +251,17 @@ pub enum ContentPurpose {
Terminal = 13, Terminal = 13,
} }
// Utilities from ::logging need a printable error type
pub struct UnrecognizedValue;
impl fmt::Display for UnrecognizedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unrecognized value")
}
}
impl TryFrom<u32> for ContentPurpose { impl TryFrom<u32> for ContentPurpose {
// There's only one way to fail: number not in protocol, type Error = UnrecognizedValue;
// so no special error type is needed
type Error = ();
fn try_from(num: u32) -> Result<Self, Self::Error> { fn try_from(num: u32) -> Result<Self, Self::Error> {
use self::ContentPurpose::*; use self::ContentPurpose::*;
match num { match num {
@ -267,7 +279,7 @@ impl TryFrom<u32> for ContentPurpose {
11 => Ok(Time), 11 => Ok(Time),
12 => Ok(Datetime), 12 => Ok(Datetime),
13 => Ok(Terminal), 13 => Ok(Terminal),
_ => Err(()), _ => Err(UnrecognizedValue),
} }
} }
} }
@ -280,14 +292,12 @@ pub enum ChangeCause {
} }
impl TryFrom<u32> for ChangeCause { impl TryFrom<u32> for ChangeCause {
// There's only one way to fail: number not in protocol, type Error = UnrecognizedValue;
// so no special error type is needed
type Error = ();
fn try_from(num: u32) -> Result<Self, Self::Error> { fn try_from(num: u32) -> Result<Self, Self::Error> {
match num { match num {
0 => Ok(ChangeCause::InputMethod), 0 => Ok(ChangeCause::InputMethod),
1 => Ok(ChangeCause::Other), 1 => Ok(ChangeCause::Other),
_ => Err(()) _ => Err(UnrecognizedValue)
} }
} }
} }
@ -320,10 +330,38 @@ pub struct IMService {
/// Owned reference (still created and destroyed in C) /// Owned reference (still created and destroyed in C)
pub im: *const c::InputMethod, pub im: *const c::InputMethod,
/// Unowned reference. Be careful, it's shared with C at large /// Unowned reference. Be careful, it's shared with C at large
ui_manager: *const c::UIManager, state_manager: *const c::StateManager,
/// Unowned reference. Be careful, it's shared with C at large
pub ui_manager: Option<*const c::UIManager>,
pending: IMProtocolState, pending: IMProtocolState,
current: IMProtocolState, // turn current into an idiomatic representation? current: IMProtocolState, // turn current into an idiomatic representation?
preedit_string: String, preedit_string: String,
serial: Wrapping<u32>, serial: Wrapping<u32>,
} }
impl IMService {
pub fn new(
im: *mut c::InputMethod,
state_manager: *const c::StateManager,
) -> Box<IMService> {
// IMService will be referenced to by C,
// so it needs to stay in the same place in memory via Box
let imservice = Box::new(IMService {
im,
ui_manager: None,
state_manager,
pending: IMProtocolState::default(),
current: IMProtocolState::default(),
preedit_string: String::new(),
serial: Wrapping(0u32),
});
unsafe {
c::imservice_connect_listeners(
im,
imservice.as_ref() as *const IMService,
);
}
imservice
}
}

View File

@ -7,6 +7,7 @@ use std::io;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action; use ::action::Action;
use ::logging;
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
@ -17,16 +18,39 @@ pub enum PressType {
Pressed = 1, Pressed = 1,
} }
pub type KeyCode = u32;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct KeyState { pub struct KeyState {
pub pressed: PressType, pub pressed: PressType,
pub locked: bool, pub locked: bool,
/// A cache of raw keycodes derived from Action::Sumbit given a keymap /// A cache of raw keycodes derived from Action::Sumbit given a keymap
pub keycodes: Vec<u32>, pub keycodes: Vec<KeyCode>,
/// Static description of what the key does when pressed or released /// Static description of what the key does when pressed or released
pub action: Action, pub action: Action,
} }
impl KeyState {
#[must_use]
pub fn into_activated(self) -> KeyState {
match self.action {
Action::LockView { lock: _, unlock: _ } => KeyState {
locked: self.locked ^ true,
..self
},
_ => self,
}
}
#[must_use]
pub fn into_released(self) -> KeyState {
KeyState {
pressed: PressType::Released,
..self
}
}
}
/// Sorts an iterator by converting it to a Vector and back /// Sorts an iterator by converting it to a Vector and back
fn sorted<'a, I: Iterator<Item=&'a str>>( fn sorted<'a, I: Iterator<Item=&'a str>>(
iter: I iter: I
@ -87,7 +111,12 @@ pub fn generate_keymap(
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
if let Action::Submit { text: _, keys } = &state.action { if let Action::Submit { text: _, keys } = &state.action {
if let 0 = keys.len() { eprintln!("Key {} has no keysyms", name); }; if let 0 = keys.len() {
log_print!(
logging::Level::Warning,
"Key {} has no keysyms", name,
);
};
for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) { for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
write!( write!(
buf, buf,

View File

@ -33,17 +33,22 @@ const char *squeek_layout_get_keymap(const struct squeek_layout*);
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *); enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
void squeek_layout_free(struct squeek_layout*); void squeek_layout_free(struct squeek_layout*);
void squeek_layout_release(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, void squeek_layout_release(struct squeek_layout *layout,
struct submission *submission,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, uint32_t timestamp,
EekboardContextService *manager, EekboardContextService *manager,
EekGtkKeyboard *ui_keyboard); EekGtkKeyboard *ui_keyboard);
void squeek_layout_release_all_only(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, uint32_t timestamp); void squeek_layout_release_all_only(struct squeek_layout *layout,
void squeek_layout_depress(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, struct submission *submission,
uint32_t timestamp);
void squeek_layout_depress(struct squeek_layout *layout,
struct submission *submission,
double x_widget, double y_widget, double x_widget, double y_widget,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, EekGtkKeyboard *ui_keyboard); uint32_t timestamp, EekGtkKeyboard *ui_keyboard);
void squeek_layout_drag(struct squeek_layout *layout, struct zwp_virtual_keyboard_v1 *virtual_keyboard, void squeek_layout_drag(struct squeek_layout *layout,
struct submission *submission,
double x_widget, double y_widget, double x_widget, double y_widget,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, EekboardContextService *manager, uint32_t timestamp, EekboardContextService *manager,

View File

@ -20,17 +20,21 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{ HashMap, HashSet }; use std::collections::{ HashMap, HashSet };
use std::ffi::CString; use std::ffi::CString;
use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::vec::Vec; use std::vec::Vec;
use ::action::Action; use ::action::Action;
use ::drawing; use ::drawing;
use ::keyboard::{ KeyState, PressType }; use ::keyboard::{ KeyState, PressType };
use ::logging;
use ::manager; use ::manager;
use ::submission::{ Timestamp, VirtualKeyboard }; use ::submission::{ Submission, Timestamp };
use ::util::find_max_double; use ::util::find_max_double;
// Traits
use std::borrow::Borrow; use std::borrow::Borrow;
use ::logging::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
@ -143,6 +147,11 @@ pub mod c {
} }
} }
} }
// This is constructed only in C, no need for warnings
#[allow(dead_code)]
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
@ -244,19 +253,12 @@ pub mod c {
pub mod procedures { pub mod procedures {
use super::*; use super::*;
use ::submission::c::ZwpVirtualKeyboardV1;
// This is constructed only in C, no need for warnings
#[allow(dead_code)]
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
/// Release pointer in the specified position /// Release pointer in the specified position
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_release( fn squeek_layout_release(
layout: *mut Layout, layout: *mut Layout,
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend submission: *mut Submission,
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
manager: manager::c::Manager, manager: manager::c::Manager,
@ -264,42 +266,49 @@ pub mod c {
) { ) {
let time = Timestamp(time); let time = Timestamp(time);
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let virtual_keyboard = VirtualKeyboard(virtual_keyboard); let submission = unsafe { &mut *submission };
let ui_backend = UIBackend {
widget_to_layout,
keyboard: ui_keyboard,
};
// The list must be copied, // The list must be copied,
// because it will be mutated in the loop // because it will be mutated in the loop
for key in layout.pressed_keys.clone() { for key in layout.pressed_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
seat::handle_release_key( seat::handle_release_key(
layout, layout,
&virtual_keyboard, submission,
&widget_to_layout, Some(&ui_backend),
time, time,
ui_keyboard, Some(manager),
manager,
key, key,
); );
} }
drawing::queue_redraw(ui_keyboard); drawing::queue_redraw(ui_keyboard);
} }
/// Release all buittons but don't redraw /// Release all buttons but don't redraw
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_release_all_only( fn squeek_layout_release_all_only(
layout: *mut Layout, layout: *mut Layout,
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend submission: *mut Submission,
time: u32, time: u32,
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let virtual_keyboard = VirtualKeyboard(virtual_keyboard); let submission = unsafe { &mut *submission };
// The list must be copied, // The list must be copied,
// because it will be mutated in the loop // because it will be mutated in the loop
for key in layout.pressed_keys.clone() { for key in layout.pressed_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
layout.release_key( seat::handle_release_key(
&virtual_keyboard, layout,
submission,
None, // don't update UI
Timestamp(time),
None, // don't switch layouts
&mut key.clone(), &mut key.clone(),
Timestamp(time)
); );
} }
} }
@ -308,13 +317,14 @@ pub mod c {
pub extern "C" pub extern "C"
fn squeek_layout_depress( fn squeek_layout_depress(
layout: *mut Layout, layout: *mut Layout,
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend submission: *mut Submission,
x_widget: f64, y_widget: f64, x_widget: f64, y_widget: f64,
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
ui_keyboard: EekGtkKeyboard, ui_keyboard: EekGtkKeyboard,
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = unsafe { &mut *submission };
let point = widget_to_layout.forward( let point = widget_to_layout.forward(
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
@ -325,11 +335,12 @@ pub mod c {
.map(|place| place.button.state.clone()) .map(|place| place.button.state.clone())
}; };
if let Some(mut state) = state { if let Some(state) = state {
layout.press_key( seat::handle_press_key(
&VirtualKeyboard(virtual_keyboard), layout,
&mut state, submission,
Timestamp(time), Timestamp(time),
&state,
); );
// maybe TODO: draw on the display buffer here // maybe TODO: draw on the display buffer here
drawing::queue_redraw(ui_keyboard); drawing::queue_redraw(ui_keyboard);
@ -343,7 +354,7 @@ pub mod c {
pub extern "C" pub extern "C"
fn squeek_layout_drag( fn squeek_layout_drag(
layout: *mut Layout, layout: *mut Layout,
virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend submission: *mut Submission,
x_widget: f64, y_widget: f64, x_widget: f64, y_widget: f64,
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
@ -352,9 +363,12 @@ pub mod c {
) { ) {
let time = Timestamp(time); let time = Timestamp(time);
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let virtual_keyboard = VirtualKeyboard(virtual_keyboard); let submission = unsafe { &mut *submission };
let ui_backend = UIBackend {
let point = widget_to_layout.forward( widget_to_layout,
keyboard: ui_keyboard,
};
let point = ui_backend.widget_to_layout.forward(
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
@ -369,7 +383,7 @@ pub mod c {
)}) )})
}; };
if let Some((mut state, _button, _view_position)) = button_info { if let Some((state, _button, _view_position)) = button_info {
let mut found = false; let mut found = false;
for wrapped_key in pressed { for wrapped_key in pressed {
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow(); let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
@ -378,17 +392,21 @@ pub mod c {
} else { } else {
seat::handle_release_key( seat::handle_release_key(
layout, layout,
&virtual_keyboard, submission,
&widget_to_layout, Some(&ui_backend),
time, time,
ui_keyboard, Some(manager),
manager,
key, key,
); );
} }
} }
if !found { if !found {
layout.press_key(&virtual_keyboard, &mut state, time); seat::handle_press_key(
layout,
submission,
time,
&state,
);
// maybe TODO: draw on the display buffer here // maybe TODO: draw on the display buffer here
} }
} else { } else {
@ -396,11 +414,10 @@ pub mod c {
let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow(); let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
seat::handle_release_key( seat::handle_release_key(
layout, layout,
&virtual_keyboard, submission,
&widget_to_layout, Some(&ui_backend),
time, time,
ui_keyboard, Some(manager),
manager,
key, key,
); );
} }
@ -612,8 +629,15 @@ pub struct LayoutData {
pub margins: Margins, pub margins: Margins,
} }
#[derive(Debug)]
struct NoSuchView; struct NoSuchView;
impl fmt::Display for NoSuchView {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "No such view")
}
}
// Unfortunately, changes are not atomic due to mutability :( // Unfortunately, changes are not atomic due to mutability :(
// An error will not be recoverable // An error will not be recoverable
// The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special. // The usage of &mut on Rc<RefCell<KeyState>> doesn't mean anything special.
@ -644,86 +668,6 @@ impl Layout {
} }
} }
fn release_key(
&mut self,
virtual_keyboard: &VirtualKeyboard,
mut key: &mut Rc<RefCell<KeyState>>,
time: Timestamp,
) {
if !self.pressed_keys.remove(&::util::Pointer(key.clone())) {
eprintln!("Warning: key {:?} was not pressed", key);
}
virtual_keyboard.switch(
&mut key.borrow_mut(),
PressType::Released,
time,
);
self.set_level_from_press(&mut key);
}
fn press_key(
&mut self,
virtual_keyboard: &VirtualKeyboard,
key: &mut Rc<RefCell<KeyState>>,
time: Timestamp,
) {
if !self.pressed_keys.insert(::util::Pointer(key.clone())) {
eprintln!("Warning: key {:?} was already pressed", key);
}
virtual_keyboard.switch(
&mut key.borrow_mut(),
PressType::Pressed,
time,
);
}
fn set_level_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
let keys = self.locked_keys.clone();
for key in &keys {
self.locked_keys.remove(key);
self.set_state_from_press(key.borrow());
}
// Don't handle the same key twice, but handle it at least once,
// because its press is the reason we're here
if !keys.contains(&::util::Pointer(key.clone())) {
self.set_state_from_press(key);
}
}
fn set_state_from_press(&mut self, key: &Rc<RefCell<KeyState>>) {
// Action should not hold a reference to key,
// because key is later borrowed for mutation. So action is cloned.
// RefCell::borrow() is covered up by (dyn Borrow)::borrow()
// if used like key.borrow() :(
let action = RefCell::borrow(key).action.clone();
let view_name = match action {
Action::SetLevel(name) => {
Some(name.clone())
},
Action::LockLevel { lock, unlock } => {
let locked = {
let mut key = key.borrow_mut();
key.locked ^= true;
key.locked
};
if locked {
self.locked_keys.insert(::util::Pointer(key.clone()));
}
Some(if locked { lock } else { unlock }.clone())
},
_ => None,
};
if let Some(view_name) = view_name {
if let Err(_e) = self.set_view(view_name.clone()) {
eprintln!("No such view: {}, ignoring switch", view_name)
};
};
}
/// Calculates size without margins /// Calculates size without margins
fn calculate_inner_size(&self) -> Size { fn calculate_inner_size(&self) -> Size {
Size { Size {
@ -848,44 +792,187 @@ mod procedures {
} }
} }
pub struct UIBackend {
widget_to_layout: c::Transformation,
keyboard: c::EekGtkKeyboard,
}
/// Top level procedures, dispatching to everything /// Top level procedures, dispatching to everything
mod seat { mod seat {
use super::*; use super::*;
// TODO: turn into release_button fn try_set_view(layout: &mut Layout, view_name: String) {
pub fn handle_release_key( layout.set_view(view_name.clone())
layout: &mut Layout, .or_print(
virtual_keyboard: &VirtualKeyboard, logging::Problem::Bug,
widget_to_layout: &c::Transformation, &format!("Bad view {}, ignoring", view_name),
time: Timestamp,
ui_keyboard: c::EekGtkKeyboard,
manager: manager::c::Manager,
key: &Rc<RefCell<KeyState>>,
) {
layout.release_key(virtual_keyboard, &mut key.clone(), time);
let view = layout.get_current_view();
let action = RefCell::borrow(key).action.clone();
if let Action::ShowPreferences = action {
let places = ::layout::procedures::find_key_places(
view, key
); );
// getting first item will cause mispositioning }
// with more than one button with the same key
// on the keyboard /// A vessel holding an obligation to switch view.
if let Some((offset, button)) = places.get(0) { /// Use with #[must_use]
let bounds = c::Bounds { struct ViewChange<'a> {
x: offset.x, y: offset.y, layout: &'a mut Layout,
width: button.size.width, view_name: Option<String>,
height: button.size.height, }
};
::popover::show( impl<'a> ViewChange<'a> {
ui_keyboard, fn choose_view(self, view_name: String) -> ViewChange<'a> {
widget_to_layout.reverse_bounds(bounds), ViewChange {
manager, view_name: Some(view_name),
); ..self
} }
} }
fn apply(self) {
if let Some(name) = self.view_name {
try_set_view(self.layout, name);
}
}
}
/// Find all impermanent view changes and undo them in an arbitrary order.
/// Return an obligation to actually switch the view.
/// The final view is the "unlock" view
/// from one of the currently stuck keys.
// As long as only one stuck button is used, this should be fine.
// This is guaranteed because pressing a lock button unlocks all others.
// TODO: Make some broader guarantee about the resulting view,
// e.g. by maintaining a stack of stuck keys.
#[must_use]
fn unstick_locks(layout: &mut Layout) -> ViewChange {
let mut new_view = None;
for key in layout.locked_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow();
let mut key = RefCell::borrow_mut(key);
match &key.action {
Action::LockView { lock: _, unlock: view } => {
new_view = Some(view.clone());
},
a => log_print!(
logging::Level::Bug,
"Non-locking action {:?} was found inside locked keys",
a,
),
};
key.locked = false;
}
ViewChange {
layout,
view_name: new_view,
}
}
pub fn handle_press_key(
layout: &mut Layout,
submission: &mut Submission,
time: Timestamp,
rckey: &Rc<RefCell<KeyState>>,
) {
if !layout.pressed_keys.insert(::util::Pointer(rckey.clone())) {
log_print!(
logging::Level::Bug,
"Key {:?} was already pressed", rckey,
);
}
let mut key = rckey.borrow_mut();
submission.virtual_keyboard.switch(
&key.keycodes,
PressType::Pressed,
time,
);
key.pressed = PressType::Pressed;
}
pub fn handle_release_key(
layout: &mut Layout,
submission: &mut Submission,
ui: Option<&UIBackend>,
time: Timestamp,
manager: Option<manager::c::Manager>,
rckey: &Rc<RefCell<KeyState>>,
) {
let key: KeyState = {
RefCell::borrow(rckey).clone()
};
let action = key.action.clone();
// update
let key = key.into_released();
let key = match action {
Action::LockView { lock: _, unlock: _ } => key.into_activated(),
_ => key,
};
// process changes
match action {
Action::Submit { text: _, keys: _ } => {
unstick_locks(layout).apply();
submission.virtual_keyboard.switch(
&key.keycodes,
PressType::Released,
time,
);
},
Action::SetView(view) => {
try_set_view(layout, view)
},
Action::LockView { lock, unlock } => {
// The button that triggered this will be in the right state
// due to commit at the end.
unstick_locks(layout)
// It doesn't matter what the resulting view should be,
// it's getting changed anyway.
.choose_view(
match key.locked {
true => lock.clone(),
false => unlock.clone(),
}
)
.apply()
},
// only show when UI is present
Action::ShowPreferences => if let Some(ui) = &ui {
// only show when layout manager is available
if let Some(manager) = manager {
let view = layout.get_current_view();
let places = ::layout::procedures::find_key_places(
view, &rckey,
);
// Getting first item will cause mispositioning
// with more than one button with the same key
// on the keyboard.
if let Some((position, button)) = places.get(0) {
let bounds = c::Bounds {
x: position.x,
y: position.y,
width: button.size.width,
height: button.size.height,
};
::popover::show(
ui.keyboard,
ui.widget_to_layout.reverse_bounds(bounds),
manager,
);
}
}
},
Action::SetModifier(_) => log_print!(
logging::Level::Bug,
"Modifiers unsupported",
),
};
let pointer = ::util::Pointer(rckey.clone());
// Apply state changes
layout.pressed_keys.remove(&pointer);
if key.locked {
layout.locked_keys.insert(pointer);
} else {
layout.locked_keys.remove(&pointer);
}
// Commit activated button state changes
RefCell::replace(rckey, key);
} }
} }
@ -900,7 +987,7 @@ mod test {
pressed: PressType::Released, pressed: PressType::Released,
locked: false, locked: false,
keycodes: Vec::new(), keycodes: Vec::new(),
action: Action::SetLevel("default".into()), action: Action::SetView("default".into()),
})) }))
} }

View File

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

View File

@ -8,6 +8,7 @@
use std::cmp; use std::cmp;
use std::ffi::{ CStr, CString }; use std::ffi::{ CStr, CString };
use std::fmt;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::ptr; use std::ptr;
use std::str::Utf8Error; use std::str::Utf8Error;
@ -47,6 +48,12 @@ pub enum Error {
NoInfo, NoInfo,
} }
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self, f)
}
}
pub struct XkbInfo(c::GnomeXkbInfo); pub struct XkbInfo(c::GnomeXkbInfo);
impl XkbInfo { impl XkbInfo {

View File

@ -26,13 +26,15 @@
* 4. logging to an immutable destination type * 4. logging to an immutable destination type
* *
* Same as above, except it can be parallelized. * Same as above, except it can be parallelized.
* Logs being outputs, they get returned
* instead of being misleadingly passed back through arguments.
* It seems more difficult to pass the logger around, * It seems more difficult to pass the logger around,
* but this may be a solved problem from the area of functional programming. * but this may be a solved problem from the area of functional programming.
* *
* This library generally aims at the approach in 3. * This library generally aims at the approach in 3.
* */ * */
use std::error::Error; use std::fmt::Display;
/// Levels are not in order. /// Levels are not in order.
pub enum Level { pub enum Level {
@ -66,18 +68,84 @@ pub enum Level {
Debug, Debug,
} }
/// Sugar for logging errors in results. impl Level {
/// Approach 2. fn as_str(&self) -> &'static str {
pub trait Warn { match self {
type Value; Level::Panic => "Panic",
fn or_warn(self, msg: &str) -> Option<Self::Value>; Level::Bug => "Bug",
Level::Error => "Error",
Level::Warning => "Warning",
Level::Surprise => "Surprise",
Level::Info => "Info",
Level::Debug => "Debug",
}
}
} }
impl<T, E: Error> Warn for Result<T, E> { impl From<Problem> for Level {
fn from(problem: Problem) -> Level {
use self::Level::*;
match problem {
Problem::Panic => Panic,
Problem::Bug => Bug,
Problem::Error => Error,
Problem::Warning => Warning,
Problem::Surprise => Surprise,
}
}
}
/// Only levels which indicate problems
/// To use with `Result::Err` handlers,
/// which are needed only when something went off the optimal path.
/// A separate type ensures that `Err`
/// can't end up misclassified as a benign event like `Info`.
pub enum Problem {
Panic,
Bug,
Error,
Warning,
Surprise,
}
/// Sugar for approach 2
// TODO: avoid, deprecate.
// Handler instances should be long lived, not one per call.
macro_rules! log_print {
($level:expr, $($arg:tt)*) => (::logging::print($level, &format!($($arg)*)))
}
/// Approach 2
pub fn print(level: Level, message: &str) {
Print{}.handle(level, message)
}
/// Sugar for logging errors in results.
pub trait Warn where Self: Sized {
type Value;
/// Approach 2.
fn or_print(self, level: Problem, message: &str) -> Option<Self::Value> {
self.or_warn(&mut Print {}, level.into(), message)
}
/// Approach 3.
fn or_warn<H: Handler>(
self,
handler: &mut H,
level: Problem,
message: &str,
) -> Option<Self::Value>;
}
impl<T, E: Display> Warn for Result<T, E> {
type Value = T; type Value = T;
fn or_warn(self, msg: &str) -> Option<T> { fn or_warn<H: Handler>(
self,
handler: &mut H,
level: Problem,
message: &str,
) -> Option<T> {
self.map_err(|e| { self.map_err(|e| {
eprintln!("{}: {}", msg, e); handler.handle(level.into(), &format!("{}: {}", message, e));
e e
}).ok() }).ok()
} }
@ -85,9 +153,14 @@ impl<T, E: Error> Warn for Result<T, E> {
impl<T> Warn for Option<T> { impl<T> Warn for Option<T> {
type Value = T; type Value = T;
fn or_warn(self, msg: &str) -> Option<T> { fn or_warn<H: Handler>(
self,
handler: &mut H,
level: Problem,
message: &str,
) -> Option<T> {
self.or_else(|| { self.or_else(|| {
eprintln!("{}", msg); handler.handle(level.into(), message);
None None
}) })
} }
@ -95,26 +168,34 @@ impl<T> Warn for Option<T> {
/// A mutable handler for text warnings. /// A mutable handler for text warnings.
/// Approach 3. /// Approach 3.
pub trait WarningHandler { pub trait Handler {
/// Handle a warning /// Handle a log message
fn handle(&mut self, warning: &str); fn handle(&mut self, level: Level, message: &str);
} }
/// Prints warnings to stderr /// Prints info to stdout, everything else to stderr
pub struct PrintWarnings; pub struct Print;
impl WarningHandler for PrintWarnings { impl Handler for Print {
fn handle(&mut self, warning: &str) { fn handle(&mut self, level: Level, message: &str) {
eprintln!("{}", warning); match level {
Level::Info => println!("Info: {}", message),
l => eprintln!("{}: {}", l.as_str(), message),
}
} }
} }
/// Warning handler that will panic at any warning. /// Warning handler that will panic
/// at any warning, error, surprise, bug, or panic.
/// Don't use except in tests /// Don't use except in tests
pub struct PanicWarn; pub struct ProblemPanic;
impl WarningHandler for PanicWarn { impl Handler for ProblemPanic {
fn handle(&mut self, warning: &str) { fn handle(&mut self, level: Level, message: &str) {
panic!("{}", warning); use self::Level::*;
match level {
Panic | Bug | Error | Warning | Surprise => panic!("{}", message),
l => Print{}.handle(l, message),
}
} }
} }

View File

@ -12,6 +12,7 @@ config_h = configure_file(
sources = [ sources = [
config_h, config_h,
'dbus.c',
'imservice.c', 'imservice.c',
'server-context-service.c', 'server-context-service.c',
'wayland.c', 'wayland.c',
@ -21,12 +22,9 @@ sources = [
'../eek/eek-keyboard.c', '../eek/eek-keyboard.c',
'../eek/eek-renderer.c', '../eek/eek-renderer.c',
'../eek/eek-types.c', '../eek/eek-types.c',
'../eek/eek-xml-layout.c',
'../eek/layersurface.c', '../eek/layersurface.c',
dbus_src, dbus_src,
'../eekboard/key-emitter.c',
'../eekboard/eekboard-context-service.c', '../eekboard/eekboard-context-service.c',
'../eekboard/eekboard-service.c',
# '../eekboard/eekboard-xklutil.c', # '../eekboard/eekboard-xklutil.c',
squeekboard_resources, squeekboard_resources,
wl_proto_sources, wl_proto_sources,

View File

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

View File

@ -7,6 +7,7 @@ use ::layout::c::{ Bounds, EekGtkKeyboard };
use ::locale; use ::locale;
use ::locale::{ OwnedTranslation, Translation, compare_current_locale }; use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
use ::locale_config::system_locale; use ::locale_config::system_locale;
use ::logging;
use ::manager; use ::manager;
use ::resources; use ::resources;
@ -221,14 +222,10 @@ fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
LayoutId::System { name, kind: _ } => { LayoutId::System { name, kind: _ } => {
xkb_translator.get_display_name(name) xkb_translator.get_display_name(name)
.map(|s| Status::Translated(OwnedTranslation(s))) .map(|s| Status::Translated(OwnedTranslation(s)))
.unwrap_or_else(|e| { .or_print(
eprintln!( logging::Problem::Surprise,
"No display name for xkb layout {}: {:?}", &format!("No display name for xkb layout {}", name),
name, ).unwrap_or_else(|| Status::Remaining(name.clone()))
e,
);
Status::Remaining(name.clone())
})
}, },
LayoutId::Local(name) => Status::Remaining(name.clone()), LayoutId::Local(name) => Status::Remaining(name.clone()),
}); });
@ -242,10 +239,13 @@ fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
.as_ref() .as_ref()
.to_owned() .to_owned()
) )
.or_warn("No locale detected") .or_print(logging::Problem::Surprise, "No locale detected")
.and_then(|lang| { .and_then(|lang| {
resources::get_layout_names(lang.as_str()) resources::get_layout_names(lang.as_str())
.or_warn(&format!("No translations for locale {}", lang)) .or_print(
logging::Problem::Surprise,
&format!("No translations for locale {}", lang),
)
}); });
match builtin_translations { match builtin_translations {
@ -361,10 +361,10 @@ pub fn show(
match state { match state {
Some(v) => { Some(v) => {
v.get::<String>() v.get::<String>()
.or_else(|| { .or_print(
eprintln!("Variant is not string: {:?}", v); logging::Problem::Bug,
None &format!("Variant is not string: {:?}", v)
}) )
.map(|state| { .map(|state| {
let (_id, layout) = choices.iter() let (_id, layout) = choices.iter()
.find( .find(
@ -376,7 +376,10 @@ pub fn show(
) )
}); });
}, },
None => eprintln!("No variant selected"), None => log_print!(
logging::Level::Debug,
"No variant selected",
),
}; };
menu_inner.popdown(); menu_inner.popdown();
}); });

View File

@ -23,24 +23,31 @@
#include "eek/eek.h" #include "eek/eek.h"
#include "eek/eek-gtk-keyboard.h" #include "eek/eek-gtk-keyboard.h"
#include "eek/layersurface.h" #include "eek/layersurface.h"
#include "eekboard/eekboard-context-service.h"
#include "submission.h"
#include "wayland.h" #include "wayland.h"
#include "server-context-service.h" #include "server-context-service.h"
enum { enum {
PROP_0, PROP_0,
PROP_SIZE_CONSTRAINT_LANDSCAPE, PROP_SIZE_CONSTRAINT_LANDSCAPE,
PROP_SIZE_CONSTRAINT_PORTRAIT, PROP_SIZE_CONSTRAINT_PORTRAIT,
PROP_VISIBLE,
PROP_LAST PROP_LAST
}; };
typedef struct _ServerContextServiceClass ServerContextServiceClass; typedef struct _ServerContextServiceClass ServerContextServiceClass;
struct _ServerContextService { struct _ServerContextService {
EekboardContextService parent; GObject parent;
EekboardContextService *state; // unowned
/// Needed for instantiating the widget
struct submission *submission; // unowned
gboolean visible;
PhoshLayerSurface *window; PhoshLayerSurface *window;
GtkWidget *widget; GtkWidget *widget; // nullable
guint hiding; guint hiding;
guint last_requested_height; guint last_requested_height;
enum squeek_arrangement_kind last_type; enum squeek_arrangement_kind last_type;
@ -50,10 +57,10 @@ struct _ServerContextService {
}; };
struct _ServerContextServiceClass { struct _ServerContextServiceClass {
EekboardContextServiceClass parent_class; GObjectClass parent_class;
}; };
G_DEFINE_TYPE (ServerContextService, server_context_service, EEKBOARD_TYPE_CONTEXT_SERVICE); G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT);
static void static void
on_destroy (GtkWidget *widget, gpointer user_data) on_destroy (GtkWidget *widget, gpointer user_data)
@ -65,7 +72,7 @@ on_destroy (GtkWidget *widget, gpointer user_data)
context->window = NULL; context->window = NULL;
context->widget = NULL; context->widget = NULL;
eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context)); //eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context));
} }
static void static void
@ -76,17 +83,6 @@ on_notify_keyboard (GObject *object,
GParamSpec *spec, GParamSpec *spec,
ServerContextService *context) ServerContextService *context)
{ {
const LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (EEKBOARD_CONTEXT_SERVICE(context));
if (!keyboard)
g_error("Programmer error: keyboard layout was unset!");
// The keymap will get set even if the window is hidden.
// It's not perfect,
// but simpler than adding a check in the window showing procedure
eekboard_context_service_set_keymap(EEKBOARD_CONTEXT_SERVICE(context),
keyboard);
/* Recreate the keyboard widget to keep in sync with the keymap. */ /* Recreate the keyboard widget to keep in sync with the keymap. */
if (context->window) if (context->window)
make_widget(context); make_widget(context);
@ -95,8 +91,8 @@ on_notify_keyboard (GObject *object,
g_object_get (context, "visible", &visible, NULL); g_object_get (context, "visible", &visible, NULL);
if (visible) { if (visible) {
eekboard_context_service_hide_keyboard(EEKBOARD_CONTEXT_SERVICE(context)); server_context_service_hide_keyboard(context);
eekboard_context_service_show_keyboard(EEKBOARD_CONTEXT_SERVICE(context)); server_context_service_show_keyboard(context);
} }
} }
@ -149,7 +145,7 @@ on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context)
enum squeek_arrangement_kind new_type = get_type((uint32_t)width, (uint32_t)height); enum squeek_arrangement_kind new_type = get_type((uint32_t)width, (uint32_t)height);
if (context->last_type != new_type) { if (context->last_type != new_type) {
context->last_type = new_type; context->last_type = new_type;
eekboard_context_service_update_layout(EEKBOARD_CONTEXT_SERVICE(context), context->last_type); eekboard_context_service_update_layout(context->state, context->last_type);
} }
guint desired_height = calculate_height(width); guint desired_height = calculate_height(width);
@ -229,35 +225,15 @@ make_widget (ServerContextService *context)
context->widget = NULL; context->widget = NULL;
} }
LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (EEKBOARD_CONTEXT_SERVICE(context)); LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (context->state);
context->widget = eek_gtk_keyboard_new (keyboard); context->widget = eek_gtk_keyboard_new (keyboard, context->state, context->submission);
gtk_widget_set_has_tooltip (context->widget, TRUE); gtk_widget_set_has_tooltip (context->widget, TRUE);
gtk_container_add (GTK_CONTAINER(context->window), context->widget); gtk_container_add (GTK_CONTAINER(context->window), context->widget);
gtk_widget_show (context->widget); gtk_widget_show (context->widget);
} }
static void
server_context_service_real_show_keyboard (EekboardContextService *_context)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
if (context->hiding) {
g_source_remove (context->hiding);
context->hiding = 0;
}
if (!context->window)
make_window (context);
if (!context->widget)
make_widget (context);
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
show_keyboard (_context);
gtk_widget_show (GTK_WIDGET(context->window));
}
static gboolean static gboolean
on_hide (ServerContextService *context) on_hide (ServerContextService *context)
{ {
@ -268,20 +244,49 @@ on_hide (ServerContextService *context)
} }
static void static void
server_context_service_real_hide_keyboard (EekboardContextService *_context) server_context_service_real_show_keyboard (ServerContextService *context)
{ {
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context); if (context->hiding) {
g_source_remove (context->hiding);
context->hiding = 0;
}
if (!context->hiding) if (!context->window)
context->hiding = g_timeout_add (200, (GSourceFunc) on_hide, context); make_window (context);
if (!context->widget)
make_widget (context);
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)-> context->visible = TRUE;
hide_keyboard (_context); gtk_widget_show (GTK_WIDGET(context->window));
} }
static void static void
server_context_service_real_destroyed (EekboardContextService *_context) server_context_service_real_hide_keyboard (ServerContextService *context)
{ {
if (!context->hiding)
context->hiding = g_timeout_add (200, (GSourceFunc) on_hide, context);
context->visible = FALSE;
}
void
server_context_service_show_keyboard (ServerContextService *context)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(context));
if (!context->visible) {
server_context_service_real_show_keyboard (context);
}
}
void
server_context_service_hide_keyboard (ServerContextService *context)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(context));
if (context->visible) {
server_context_service_real_hide_keyboard (context);
}
} }
static void static void
@ -306,6 +311,9 @@ server_context_service_set_property (GObject *object,
&context->size_constraint_portrait[0], &context->size_constraint_portrait[0],
&context->size_constraint_portrait[1]); &context->size_constraint_portrait[1]);
break; break;
case PROP_VISIBLE:
context->visible = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -313,6 +321,23 @@ server_context_service_set_property (GObject *object,
} }
} }
static void
server_context_service_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
switch (prop_id) {
case PROP_VISIBLE:
g_value_set_boolean (value, context->visible);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void static void
server_context_service_dispose (GObject *object) server_context_service_dispose (GObject *object)
{ {
@ -327,15 +352,11 @@ server_context_service_dispose (GObject *object)
static void static void
server_context_service_class_init (ServerContextServiceClass *klass) server_context_service_class_init (ServerContextServiceClass *klass)
{ {
EekboardContextServiceClass *context_class = EEKBOARD_CONTEXT_SERVICE_CLASS(klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec; GParamSpec *pspec;
context_class->show_keyboard = server_context_service_real_show_keyboard;
context_class->hide_keyboard = server_context_service_real_hide_keyboard;
context_class->destroyed = server_context_service_real_destroyed;
gobject_class->set_property = server_context_service_set_property; gobject_class->set_property = server_context_service_set_property;
gobject_class->get_property = server_context_service_get_property;
gobject_class->dispose = server_context_service_dispose; gobject_class->dispose = server_context_service_dispose;
pspec = g_param_spec_variant ("size-constraint-landscape", pspec = g_param_spec_variant ("size-constraint-landscape",
@ -357,24 +378,39 @@ server_context_service_class_init (ServerContextServiceClass *klass)
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_SIZE_CONSTRAINT_PORTRAIT, PROP_SIZE_CONSTRAINT_PORTRAIT,
pspec); pspec);
/**
* Flag to indicate if keyboard is visible or not.
*/
pspec = g_param_spec_boolean ("visible",
"Visible",
"Visible",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_VISIBLE,
pspec);
} }
static void static void
server_context_service_init (ServerContextService *context) server_context_service_init (ServerContextService *state) {
(void)state;
}
ServerContextService *
server_context_service_new (EekboardContextService *state, struct submission *submission)
{ {
g_signal_connect (context, ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
ui->submission = submission;
ui->state = state;
g_signal_connect (state,
"notify::keyboard", "notify::keyboard",
G_CALLBACK(on_notify_keyboard), G_CALLBACK(on_notify_keyboard),
context); ui);
return ui;
} }
EekboardContextService * enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *service)
server_context_service_new ()
{ {
return EEKBOARD_CONTEXT_SERVICE(g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL)); return service->last_type;
}
enum squeek_arrangement_kind server_context_service_get_layout_type(EekboardContextService *service)
{
return SERVER_CONTEXT_SERVICE(service)->last_type;
} }

View File

@ -18,8 +18,8 @@
#ifndef SERVER_CONTEXT_SERVICE_H #ifndef SERVER_CONTEXT_SERVICE_H
#define SERVER_CONTEXT_SERVICE_H 1 #define SERVER_CONTEXT_SERVICE_H 1
#include "eekboard/eekboard-service.h"
#include "src/layout.h" #include "src/layout.h"
#include "src/submission.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -30,12 +30,16 @@ G_BEGIN_DECLS
#define SERVER_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SERVER_TYPE_CONTEXT_SERVICE)) #define SERVER_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SERVER_TYPE_CONTEXT_SERVICE))
#define SERVER_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass)) #define SERVER_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass))
/** Manages the liecycle of the window displaying layouts. */ /** Manages the lifecycle of the window displaying layouts. */
typedef struct _ServerContextService ServerContextService; typedef struct _ServerContextService ServerContextService;
EekboardContextService *server_context_service_new (); GType server_context_service_get_type
enum squeek_arrangement_kind server_context_service_get_layout_type(EekboardContextService*); (void) G_GNUC_CONST;
ServerContextService *server_context_service_new(EekboardContextService *state, struct submission *submission);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
void server_context_service_show_keyboard (ServerContextService *context);
void server_context_service_hide_keyboard (ServerContextService *context);
G_END_DECLS G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */ #endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -25,10 +25,11 @@
#include "config.h" #include "config.h"
#include "eekboard/eekboard-service.h"
#include "eek/eek.h" #include "eek/eek.h"
#include "imservice.h" #include "eekboard/eekboard-context-service.h"
#include "dbus.h"
#include "outputs.h" #include "outputs.h"
#include "submission.h"
#include "server-context-service.h" #include "server-context-service.h"
#include "wayland.h" #include "wayland.h"
@ -38,8 +39,10 @@
/// Global application state /// Global application state
struct squeekboard { struct squeekboard {
struct squeek_wayland wayland; struct squeek_wayland wayland;
EekboardContextService *context; DBusHandler *dbus_handler;
struct imservice *imservice; EekboardContextService *settings_context;
ServerContextService *ui_context;
struct submission *submission;
}; };
@ -64,31 +67,14 @@ on_name_lost (GDBusConnection *connection,
gpointer user_data) gpointer user_data)
{ {
// TODO: could conceivable continue working // TODO: could conceivable continue working
// if intrnal changes stop sending dbus changes
(void)connection; (void)connection;
(void)name; (void)name;
(void)user_data; (void)user_data;
g_error("DBus unavailable, unclear how to continue.");
exit (1); exit (1);
} }
static void
on_destroyed (EekboardService *service,
gpointer user_data)
{
(void)service;
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
static EekboardContextService *create_context() {
EekboardContextService *context = server_context_service_new ();
g_object_set_data_full (G_OBJECT(context),
"owner", g_strdup ("sender"),
(GDestroyNotify)g_free);
eekboard_context_service_enable (context);
return context;
}
// Wayland // Wayland
static void static void
@ -209,7 +195,11 @@ main (int argc, char **argv)
exit(1); exit(1);
} }
instance.context = create_context(); if (!instance.wayland.input_method_manager) {
g_warning("Wayland input method interface not available");
}
instance.settings_context = eekboard_context_service_new();
// set up dbus // set up dbus
@ -257,17 +247,16 @@ main (int argc, char **argv)
break; break;
} }
EekboardService *service = eekboard_service_new (connection, EEKBOARD_SERVICE_PATH); DBusHandler *service = dbus_handler_new(connection, DBUS_SERVICE_PATH);
if (service == NULL) { if (service == NULL) {
g_printerr ("Can't create dbus server\n"); g_printerr ("Can't create dbus server\n");
exit (1); exit (1);
} else {
eekboard_service_set_context(service, instance.context);
} }
instance.dbus_handler = service;
guint owner_id = g_bus_own_name_on_connection (connection, guint owner_id = g_bus_own_name_on_connection (connection,
EEKBOARD_SERVICE_INTERFACE, DBUS_SERVICE_INTERFACE,
G_BUS_NAME_OWNER_FLAGS_NONE, G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired, on_name_acquired,
on_name_lost, on_name_lost,
@ -278,24 +267,33 @@ main (int argc, char **argv)
exit (1); exit (1);
} }
struct imservice *imservice = NULL; instance.submission = get_submission(instance.wayland.input_method_manager,
if (instance.wayland.input_method_manager) { instance.wayland.virtual_keyboard_manager,
imservice = get_imservice(instance.context, instance.wayland.seat,
instance.wayland.input_method_manager, instance.settings_context);
instance.wayland.seat);
if (imservice) { eekboard_context_service_set_submission(instance.settings_context, instance.submission);
instance.imservice = imservice;
} else { ServerContextService *ui_context = server_context_service_new(
g_warning("Failed to register as an input method"); instance.settings_context,
} instance.submission);
if (!ui_context) {
g_error("Could not initialize GUI");
exit(1);
} }
instance.ui_context = ui_context;
if (instance.submission) {
submission_set_ui(instance.submission, instance.ui_context);
}
if (instance.dbus_handler) {
dbus_handler_set_ui_context(instance.dbus_handler, instance.ui_context);
}
eekboard_context_service_set_ui(instance.settings_context, instance.ui_context);
session_register(); session_register();
GMainLoop *loop = g_main_loop_new (NULL, FALSE); GMainLoop *loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (service, "destroyed", G_CALLBACK(on_destroyed), loop);
g_main_loop_run (loop); g_main_loop_run (loop);
g_bus_unown_name (owner_id); g_bus_unown_name (owner_id);

View File

@ -19,6 +19,7 @@
/*! CSS data loading. */ /*! CSS data loading. */
use std::env; use std::env;
use ::logging;
use glib::object::ObjectExt; use glib::object::ObjectExt;
use logging::Warn; use logging::Warn;
@ -84,7 +85,10 @@ fn get_theme_name(settings: &gtk::Settings) -> GtkTheme {
match &e { match &e {
env::VarError::NotPresent => {}, env::VarError::NotPresent => {},
// maybe TODO: forward this warning? // maybe TODO: forward this warning?
e => eprintln!("GTK_THEME variable invalid: {}", e), e => log_print!(
logging::Level::Surprise,
"GTK_THEME variable invalid: {}", e,
),
}; };
e e
}).ok(); }).ok();
@ -94,15 +98,13 @@ fn get_theme_name(settings: &gtk::Settings) -> GtkTheme {
None => GtkTheme { None => GtkTheme {
name: { name: {
settings.get_property("gtk-theme-name") settings.get_property("gtk-theme-name")
// maybe TODO: is this worth a warning? .or_print(logging::Problem::Surprise, "No theme name")
.or_warn("No theme name")
.and_then(|value| value.get::<String>()) .and_then(|value| value.get::<String>())
.unwrap_or(DEFAULT_THEME_NAME.into()) .unwrap_or(DEFAULT_THEME_NAME.into())
}, },
variant: { variant: {
settings.get_property("gtk-application-prefer-dark-theme") settings.get_property("gtk-application-prefer-dark-theme")
// maybe TODO: is this worth a warning? .or_print(logging::Problem::Surprise, "No settings key")
.or_warn("No settings key")
.and_then(|value| value.get::<bool>()) .and_then(|value| value.get::<bool>())
.and_then(|dark_preferred| match dark_preferred { .and_then(|dark_preferred| match dark_preferred {
true => Some("dark".into()), true => Some("dark".into()),

19
src/submission.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __SUBMISSION_H
#define __SUBMISSION_H
#include "input-method-unstable-v2-client-protocol.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "eek/eek-types.h"
struct submission;
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
struct wl_seat *seat,
EekboardContextService *state);
// Defined in Rust
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state);
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
#endif

View File

@ -1,69 +1,100 @@
/*! Managing the events belonging to virtual-keyboard interface. */ /*! Managing the state of text input in the application.
*
* This is a library module.
*
* It needs to combine text-input and virtual-keyboard protocols
* to achieve a consistent view of the text-input state,
* and to submit exactly what the user wanted.
*
* It must also not get tripped up by sudden disappearances of interfaces.
*
* The virtual-keyboard interface is always present.
*
* The text-input interface may not be presented,
* and, for simplicity, no further attempt to claim it is made.
*
* The text-input interface may be enabled and disabled at arbitrary times,
* and those events SHOULD NOT cause any lost events.
* */
use ::keyboard::{ KeyState, PressType }; use ::imservice::IMService;
use ::vkeyboard::VirtualKeyboard;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
use super::*;
use std::os::raw::c_void; use std::os::raw::c_void;
use ::imservice::c::InputMethod;
use ::layout::c::LevelKeyboard;
use ::vkeyboard::c::ZwpVirtualKeyboardV1;
// The following defined in C
/// ServerContextService*
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy)] pub struct UIManager(*const c_void);
pub struct ZwpVirtualKeyboardV1(*const c_void);
/// EekboardContextService*
#[repr(transparent)]
pub struct StateManager(*const c_void);
#[no_mangle] #[no_mangle]
extern "C" { pub extern "C"
/// Checks if point falls within bounds, fn submission_new(
/// which are relative to origin and rotated by angle (I think) im: *mut InputMethod,
pub fn eek_virtual_keyboard_v1_key( vk: ZwpVirtualKeyboardV1,
virtual_keyboard: ZwpVirtualKeyboardV1, state_manager: *const StateManager
timestamp: u32, ) -> *mut Submission {
keycode: u32, let imservice = if im.is_null() {
press: u32, None
); } else {
Some(IMService::new(im, state_manager))
};
// TODO: add vkeyboard too
Box::<Submission>::into_raw(Box::new(
Submission {
imservice,
virtual_keyboard: VirtualKeyboard(vk),
}
))
}
/// Use to initialize the UI reference
#[no_mangle]
pub extern "C"
fn submission_set_ui(submission: *mut Submission, ui_manager: *const UIManager) {
if submission.is_null() {
panic!("Null submission pointer");
}
let submission: &mut Submission = unsafe { &mut *submission };
if let Some(ref mut imservice) = &mut submission.imservice {
imservice.ui_manager = if ui_manager.is_null() {
None
} else {
Some(ui_manager)
}
};
}
#[no_mangle]
pub extern "C"
fn submission_set_keyboard(submission: *mut Submission, keyboard: LevelKeyboard) {
if submission.is_null() {
panic!("Null submission pointer");
}
let submission: &mut Submission = unsafe { &mut *submission };
submission.virtual_keyboard.update_keymap(keyboard);
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Timestamp(pub u32); pub struct Timestamp(pub u32);
/// Layout-independent backend. TODO: Have one instance per program or seat pub struct Submission {
pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1); // used by C callbacks internally, TODO: make use with virtual keyboard
#[allow(dead_code)]
impl VirtualKeyboard { imservice: Option<Box<IMService>>,
// TODO: split out keyboard state management pub virtual_keyboard: VirtualKeyboard,
pub fn switch(
&self,
key: &mut KeyState,
action: PressType,
timestamp: Timestamp,
) {
key.pressed = action.clone();
let keycodes_count = key.keycodes.len();
for keycode in key.keycodes.iter() {
let keycode = keycode - 8;
match (&key.pressed, keycodes_count) {
// Pressing a key made out of a single keycode is simple:
// press on press, release on release.
(_, 1) => unsafe {
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, action.clone() as u32
);
},
// A key made of multiple keycodes
// has to submit them one after the other
(PressType::Pressed, _) => unsafe {
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, PressType::Pressed as u32
);
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, PressType::Released as u32
);
},
// Design choice here: submit multiple all at press time
// and do nothing at release time
(PressType::Released, _) => {},
}
}
}
} }

View File

@ -1,17 +1,22 @@
/*! Testing functionality */ /*! Testing functionality */
use ::data::Layout; use ::data::Layout;
use ::logging;
use xkbcommon::xkb; use xkbcommon::xkb;
use ::logging::WarningHandler;
pub struct CountAndPrint(u32); pub struct CountAndPrint(u32);
impl WarningHandler for CountAndPrint { impl logging::Handler for CountAndPrint {
fn handle(&mut self, warning: &str) { fn handle(&mut self, level: logging::Level, warning: &str) {
self.0 = self.0 + 1; use logging::Level::*;
println!("{}", warning); match level {
Panic | Bug | Error | Warning | Surprise => {
self.0 += 1;
},
_ => {}
}
logging::Print{}.handle(level, warning)
} }
} }
@ -34,7 +39,7 @@ fn check_layout(layout: Layout) {
let (layout, handler) = layout.build(handler); let (layout, handler) = layout.build(handler);
if handler.0 > 0 { if handler.0 > 0 {
println!("{} mistakes in layout", handler.0) println!("{} problems while parsing layout", handler.0)
} }
let layout = layout.expect("layout broken"); let layout = layout.expect("layout broken");

79
src/vkeyboard.rs Normal file
View File

@ -0,0 +1,79 @@
/*! Managing the events belonging to virtual-keyboard interface. */
use ::keyboard::{ KeyCode, PressType };
use ::layout::c::LevelKeyboard;
use ::submission::Timestamp;
/// Gathers stuff defined in C or called by C
pub mod c {
use super::*;
use std::os::raw::c_void;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct ZwpVirtualKeyboardV1(*const c_void);
#[no_mangle]
extern "C" {
pub fn eek_virtual_keyboard_v1_key(
virtual_keyboard: ZwpVirtualKeyboardV1,
timestamp: u32,
keycode: u32,
press: u32,
);
pub fn eek_virtual_keyboard_update_keymap(
virtual_keyboard: ZwpVirtualKeyboardV1,
keyboard: LevelKeyboard,
);
}
}
/// Layout-independent backend. TODO: Have one instance per program or seat
pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1);
impl VirtualKeyboard {
// TODO: split out keyboard state management
pub fn switch(
&self,
keycodes: &Vec<KeyCode>,
action: PressType,
timestamp: Timestamp,
) {
let keycodes_count = keycodes.len();
for keycode in keycodes.iter() {
let keycode = keycode - 8;
match (action, keycodes_count) {
// Pressing a key made out of a single keycode is simple:
// press on press, release on release.
(_, 1) => unsafe {
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, action.clone() as u32
);
},
// A key made of multiple keycodes
// has to submit them one after the other
(PressType::Pressed, _) => unsafe {
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, PressType::Pressed as u32
);
c::eek_virtual_keyboard_v1_key(
self.0, timestamp.0, keycode, PressType::Released as u32
);
},
// Design choice here: submit multiple all at press time
// and do nothing at release time
(PressType::Released, _) => {},
}
}
}
pub fn update_keymap(&self, keyboard: LevelKeyboard) {
unsafe {
c::eek_virtual_keyboard_update_keymap(
self.0,
keyboard,
);
}
}
}

View File

@ -1,3 +1,5 @@
#include "eek/eek-keyboard.h"
#include "wayland.h" #include "wayland.h"
struct squeek_wayland *squeek_wayland = NULL; struct squeek_wayland *squeek_wayland = NULL;
@ -11,6 +13,13 @@ eek_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard
zwp_virtual_keyboard_v1_key(zwp_virtual_keyboard_v1, time, key, state); zwp_virtual_keyboard_v1_key(zwp_virtual_keyboard_v1, time, key, state);
} }
void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, const LevelKeyboard *keyboard) {
zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keyboard->keymap_fd, keyboard->keymap_len);
}
int squeek_output_add_listener(struct wl_output *wl_output, int squeek_output_add_listener(struct wl_output *wl_output,
const struct wl_output_listener *listener, void *data) { const struct wl_output_listener *listener, void *data) {
return wl_output_add_listener(wl_output, listener, data); return wl_output_add_listener(wl_output, listener, data);