Compare commits

...

117 Commits

Author SHA1 Message Date
cb338129ca Release 1.18.0 "Dunbar's number"
New translations:
- Portugese
- Serbian
- Hungarian
- Turkish
- Polish
- Spanish
- Italian
- Korean
- Occitan
- Georgian

Updated layouts: greek, polytonic

Bug ixes and improvements:
- better panel sizing
- possible to enable debugging messages while Squeekboard is running
- input method state fix
2022-04-25 13:14:00 +00:00
9e8a243439 Update Cargo lock 2022-04-25 13:00:44 +00:00
dcz
d3e0ee8c0d Merge branch 'dc' into 'master'
docs improvements

See merge request World/Phosh/squeekboard!547
2022-04-25 12:14:32 +00:00
2e2c8ab2cb Add Georgian translation 2022-04-23 16:52:05 +00:00
dcz
36474d3e9d Merge branch 'output_fix' into 'master'
Panel handling cleanup

See merge request World/Phosh/squeekboard!529
2022-04-21 07:28:38 +00:00
dcz
e6438503a5 Merge branch 'fix-i386-build' into 'master'
layout: fix build on i386

See merge request World/Phosh/squeekboard!548
2022-04-20 11:22:39 +00:00
58c7fe98b8 layout: fix build on i386
Due to the (lack of) precision of floating-point values, comparison
results may differ slightly between architectures, leading to the
`check_stretching` test failing when building for i386. This can be
fixed by adjusting the value against which we compare the ratio between
x/y scaling factors in `calculate_transformation`.
2022-04-20 12:43:20 +02:00
6867f48bf9 docs: Make index more logical 2022-04-17 07:35:32 +00:00
71942f7221 docs: Link to reference 2022-04-17 07:31:29 +00:00
c486ad1eb3 docs: Update location 2022-04-17 07:25:56 +00:00
05e7cde8fa Add Occitan translation 2022-04-17 07:21:42 +00:00
dcz
9adb593e8e Merge branch 'docs' into 'master'
Add docs to gitlab pages

See merge request World/Phosh/squeekboard!545
2022-04-17 06:24:37 +00:00
073326f31b Merge branch 'zbus' into 'master'
cargo: Add zbus to newer Debian

See merge request World/Phosh/squeekboard!546
2022-04-15 22:36:05 +00:00
dae324f86d cargo: Add zbus to newer Debian 2022-04-13 18:06:13 +00:00
2eec3372f3 panel: Split away panel handling
This reduces ServerContextService to a mere handler of "docked mode" gsetting.
2022-04-13 16:49:33 +00:00
dcz
f6724c0948 Merge branch 'debug' into 'master'
Enable debugging at runtime

See merge request World/Phosh/squeekboard!526
2022-04-13 16:40:36 +00:00
af8b688d94 Add Korean translation 2022-04-13 09:31:50 +00:00
dcz
d21dba6a8f Merge branch 'clap' into 'master'
build: Update clap on newer Debian

See merge request World/Phosh/squeekboard!537
2022-04-12 14:11:02 +00:00
0e2d459d5a CI: Add gitlab pages deployment 2022-04-09 18:34:14 +00:00
89ad302255 CI: Build Rust code reference 2022-04-09 18:34:14 +00:00
dcz
494f9442c4 Merge branch 'allow-stretching' into 'master'
layout: allow stretching the layout by a small amount

See merge request World/Phosh/squeekboard!544
2022-04-09 18:05:20 +00:00
68087a125c layout: allow stretching the layout by a small amount
Due to the way the panel size is calculated, there might be a small
empty space on the sides or top of the layout. This can be an issue,
especially when this empty space is located on the sides, as touch
events in this area are not taken into account.

By allowing a small difference in horizontal and vertical scaling, we
can ensure the panel occupies the whole display width in cases where
this would be problematic.
2022-04-09 16:43:06 +02:00
dcz
323fd7ea14 Merge branch 'phys_size' into 'master'
Physically-based sizing

See merge request World/Phosh/squeekboard!543
2022-04-08 17:30:04 +00:00
dcz
3167cfce9c Merge branch 'gr' into 'master'
layouts: Register gr_wide

See merge request World/Phosh/squeekboard!539
2022-04-07 15:16:03 +00:00
8ea6f6d5c1 layouts: Register gr_wide 2022-04-07 14:59:06 +00:00
dcz
3b70116a15 Merge branch 'gro' into 'master'
layouts: Add Greek Polytonic

See merge request World/Phosh/squeekboard!540
2022-04-07 14:33:23 +00:00
397f5e126e state: Add sizing unit test 2022-04-07 14:30:46 +00:00
14d7d5d4e0 Clean up size types 2022-04-06 16:03:31 +00:00
6528879fed state: Derive panel size from physical click target size 2022-04-06 15:53:37 +00:00
57aeeaa882 output: Store physical size 2022-04-06 08:58:41 +00:00
bbceba7e9b debug: Add dbus interface to control debug prints 2022-04-05 14:19:52 +00:00
dcz
5a210712f6 Merge branch 'fix' into 'master'
Fix scaling to set height

See merge request World/Phosh/squeekboard!535
2022-04-05 11:55:55 +00:00
bb8bba163e layouts: Add Greek Polytonic
By Antonis Tsolomitis <atsol@aegean.gr>
2022-04-05 11:41:27 +00:00
83b0d1553f state: fix "wide mode" detection in portrait orientation
We need to check if we should use the wide layout based on the
*logical* display width, not its *physical* resolution.
2022-04-05 10:16:22 +00:00
dcz
a1664630ed Merge branch 'testing_updated_gr_and_new_gr_wide' into 'master'
Update gr.yaml to take advantage of more space per symbol. Creation of a wide variant...

See merge request World/Phosh/squeekboard!532
2022-04-05 10:02:05 +00:00
529ac89150 Update gr.yaml to take advantage of more space per symbol. Creation of a wide variant... 2022-04-05 10:02:05 +00:00
479f1befc9 Merge branch 'dont-reset-state-on-done' into HEAD 2022-04-05 09:59:56 +00:00
29b30fbe22 panel: Use scaling to set height 2022-04-05 09:26:11 +00:00
1ccc663c48 build: Update clap on newer Debian 2022-04-05 09:17:04 +00:00
dcz
7c43528ebf Merge branch 'fix_meson' into 'master'
build: Replace missing crates.io dependency with Purism-hosted one

See merge request World/Phosh/squeekboard!536
2022-04-05 09:16:12 +00:00
7f4c823c1e ci: Allow failure on sid 2022-04-04 17:54:31 +00:00
d19050e06d build: Replace missing crates.io dependency with Purism-hosted one 2022-04-04 17:48:54 +00:00
b5142ac765 Add Italian translation 2022-04-04 07:07:48 +00:00
b456889fe9 Add Spanish translation 2022-03-23 11:19:44 +00:00
3fdbcf905b Add Polish translation 2022-03-20 13:14:03 +00:00
8a2de2fdf2 Add Turkish translation 2022-03-19 09:34:34 +00:00
bd390894c5 Add Hungarian translation 2022-03-16 00:57:01 +00:00
04018a8c06 Do not reset pending state on zwp_input_method_v2.done 2022-03-15 15:51:09 -04:00
b4cd5659cb Add Serbian translation 2022-03-12 12:03:51 +00:00
dcz
59c3da0344 Merge branch 'v1.17.0' into 'master'
Release 1.17.0 "Ergodicity"

See merge request World/Phosh/squeekboard!531
2022-03-08 10:15:13 +00:00
c5eb41292c Add Portuguese translation 2022-02-26 18:28:13 +00:00
c912b73c4b Release 1.17.0 "Ergodicity"
New translations:
- Hebrew (thanks Yosef Or Boczko)
- Galician (Fran Dieguez)

New Romanian layout (thanks Cosmin Humeniuc)

Others:
- Updated visual design
- Sizing system rework
- Fix crash without dbus (thanks William Wold)
2022-02-26 11:10:49 +00:00
7b8ba7ab82 cargo: Update lockfile 2022-02-26 11:05:57 +00:00
dcz
dd2871b6bb Merge branch 'output' into 'master'
Derive panel size from outputs

See merge request World/Phosh/squeekboard!528
2022-02-26 10:49:52 +00:00
dcz
216deecd33 Merge branch 'dbus-check-null' into 'master'
Check if dbus handler is null before using

See merge request World/Phosh/squeekboard!530
2022-02-26 10:49:12 +00:00
22cc85b2e2 Add Hebrew translation 2022-02-14 16:05:29 +00:00
0b9350d19b Check if dbus handler is null before using 2022-02-08 10:56:00 -05:00
aad71116c5 Add Galician translation 2022-02-04 15:22:34 +00:00
16d6871422 panel: Apply a hard limit of 1/2 height 2022-02-04 09:40:21 +00:00
78ff02e255 output: Use new source of panel height information
This removed duplicate calculation of ideal height as well.
2022-02-04 09:40:21 +00:00
a3f91701d0 outputs: Remove ui manager 2022-02-04 09:40:21 +00:00
697be64418 visibility: Forward panel height information to window creation 2022-02-04 09:40:21 +00:00
a4b67c65ff Don't reach for globals to choose output
This actually removes the size request from panel creation. Incidentally, this still works becuae the following configure event gets the sizes from glib.
2022-02-04 09:40:21 +00:00
f040e708a4 Carry output information on visible command all the way to C 2022-02-04 09:40:18 +00:00
e6c19a1e6a deps: Vendor assert_matches
The library is small and simple enough to be considered "finished". In addition, it doesn't seem to be shipped by Debian.

In relation to its usefulness, it's little effrt to copy it.
2022-02-04 09:38:06 +00:00
dcz
98ecce518b Merge branch 'tiny' into 'master'
Output sensing

See merge request World/Phosh/squeekboard!524
2022-02-02 17:41:14 +00:00
dcz
500375dcf2 Merge branch 'doc' into 'master'
docs: Detail the release process better

See merge request World/Phosh/squeekboard!523
2022-02-01 16:33:03 +00:00
4e04bc306f Update Brazilian Portuguese translation 2022-01-31 07:48:41 +00:00
dcz
dfcb3ce020 Merge branch 'layout_ro' into 'master'
Add Romanian layout

See merge request World/Phosh/squeekboard!525
2022-01-30 17:52:57 +00:00
dcz
5cf007f419 Merge branch 'Torbuntu-master-patch-58062' into 'master'
Make compatible with latest cargo deps

See merge request World/Phosh/squeekboard!527
2022-01-30 17:47:57 +00:00
Tor
417fe35e91 Make compatible with latest cargo deps 2022-01-30 17:47:57 +00:00
3f598086b7 Store preferred output 2022-01-30 12:43:17 +00:00
dcz
dfe2c60646 Merge branch 'snwh-master-patch-66054' into 'master'
data: Update stylesheet with upstream design

Closes #322

See merge request World/Phosh/squeekboard!520
2022-01-29 11:36:08 +00:00
8ec79428a9 data: Update stylesheet with upstream design
- adjust main font size
- adjust margins and border radii
- drop borders from keys
- put more items in common.css
- update dark and light style definitions
- use mixing of fg color wherever possible
2022-01-28 12:26:23 -03:30
3b0b8bea0d Save outputs state 2022-01-28 15:26:22 +00:00
ba00ec86a1 Add Romanian layout 2022-01-27 20:42:17 +02:00
f15f97d4c9 outputs: Handle removal
Currrently, Squeekboard doesn't do anything with this information.

It still expects one output to be present, or it will crash.
2022-01-26 15:19:58 +00:00
d3eb68ed5a outputs: Notify the state manager about changes 2022-01-26 15:19:58 +00:00
14a485deba outputs: Clean up for more Rust usage 2022-01-26 15:19:58 +00:00
236f7d4daf ffi: Remove unnecessary pointers to InputMethod
InputMethod is already a pointer.
2022-01-26 15:19:58 +00:00
f4f44a49ae wayland: Move initialization to the Rust side
This will help make the init procedure safer, by limiting the number of Rust objects that need to be carried to the C side and may be mangled on the way there.

The second benefit is that it allows outputs to become part of new state management.
2022-01-26 15:19:58 +00:00
1b72cbdfaa docstrings: Clarify the purpose of Receiver 2022-01-26 15:19:58 +00:00
ff3f7228b5 cleanup: Remove unused header lines 2022-01-26 15:19:58 +00:00
b6ce4151bd Merge branch 'rel' into 'master'
Release 1.16.0 "Clostridium botulinum"

See merge request World/Phosh/squeekboard!522
2022-01-25 12:15:22 +00:00
d49ce45de0 Release 1.16.0 "Clostridium botulinum"
This release contains fixes that make the project's dependencies more reliable, and therefore less likely to blow up when the world changes.

Apart from that, there are a lot of extra translations:
- Brazilian Portugese (Rafael Fontenelle)
- Ukrainian (Yuri Chornoivan)
- Swedish (Luna Jernberg)
- Friulian (Fabio Tomat)
- Romanian (Daniel Șerbănescu)
- Slovenian (Matej Urbančič)
- Dutch (Nathan Follens)
- Finnish (Jiri Grönroos)
- Persian (Danial Behzadi)
- Catalan (Jordi Mas i Hernandez)
2022-01-25 11:26:26 +00:00
a341fca43a cargo: Bump dependencies 2022-01-25 11:23:25 +00:00
db6109b298 docs: Detail the release process better 2022-01-25 11:22:31 +00:00
dcz
145d12d01a Merge branch 'fix' into 'master'
CI: Use byzantium as the base

See merge request World/Phosh/squeekboard!521
2022-01-24 19:34:16 +00:00
506df8cf15 CI: Use byzantium as the base
Bookworm diverged from Byzantium, leading to incompatible dependencies.
2022-01-24 19:12:51 +00:00
dcz
21ecbb3ef3 Merge branch 'entry4' into 'master'
Add a GTK4 keyboard hint test tool

See merge request World/Phosh/squeekboard!518
2022-01-21 13:15:24 +00:00
29da31af20 Add Catalan translation 2022-01-20 14:36:58 +00:00
23b35733cb Add entry test using GTK4
This helps debugging GTK4 interaction issues
2022-01-20 10:36:13 +01:00
55cd225c74 entry: Add another input hint 2022-01-20 10:17:11 +01:00
e55ae67da6 entry: Only activate purpose timer when focused
This avoids verbose printing when the widget is never focused
2022-01-20 10:17:11 +01:00
79bc670ad0 entry: Mark as executable
Saves some typing
2022-01-20 10:17:11 +01:00
dcz
ebc2dd39f6 Merge branch 'vers' into 'master'
build: Pin transitive dependencies

Closes #321

See merge request World/Phosh/squeekboard!519
2022-01-19 17:06:27 +00:00
dcz
71768e27c0 Merge branch 'noreg' into 'master'
ci: Use bookworm image

See merge request World/Phosh/squeekboard!510
2022-01-19 16:49:21 +00:00
aaac755869 cargo: Update Cargo.lock with pinned dependencies 2022-01-19 16:33:33 +00:00
0430ba9213 build: Pin transitive dependencies
gtk-sys and cairo-sys were set to "anything" in the hope that the dependency resolver in cargo would use the same version as other dependencies, which consume objects from them.
That's not the case though, and they went out of sync.
This pins the dependencies to what is de facto being used.
2022-01-19 16:33:33 +00:00
0c17924c50 Add Persian translation 2022-01-11 14:31:39 +00:00
5286ff50a5 Add Finnish translation 2021-12-26 15:15:43 +00:00
3ee2185714 Add Dutch translation 2021-12-25 13:55:12 +00:00
7ea30819aa Add Slovenian translation 2021-12-23 15:18:45 +00:00
3647581cd7 Add Romanian translation 2021-12-22 19:14:12 +00:00
dcz
d76b385316 Merge branch 'droidbittin-master-patch-70834' into 'master'
Update LINGUAS

See merge request World/Phosh/squeekboard!516
2021-12-22 14:45:01 +00:00
3240006516 Add Friulian translation 2021-12-22 14:07:20 +00:00
2d0aa7aef1 Update LINGUAS 2021-12-22 14:02:56 +00:00
dcz
4dd4c8c319 Merge branch 'master' into 'master'
Add Swedish Translation

See merge request World/Phosh/squeekboard!515
2021-12-22 13:33:27 +00:00
324438acac Update sv.po 2021-12-22 13:17:58 +00:00
a643b05f57 Add Swedish Translation 2021-12-22 13:16:51 +00:00
7adf325831 Add Ukrainian translation 2021-12-22 12:47:09 +00:00
b85903cb21 Add Brazilian Portuguese translation 2021-12-22 12:39:07 +00:00
dcz
67d2f8d8e6 Merge branch 'po-popover' into 'master'
po: Fix ui file name

See merge request World/Phosh/squeekboard!514
2021-12-22 10:36:38 +00:00
6979b6d08d po: Fix ui file name
The file was renamed to match the code

Fixes: 2aa9cf2 ("popover: Make the ui file match the code file name")
2021-12-22 09:39:12 +01:00
44b9c8f869 Merge branch 'rel' into 'master'
Release 1.15.0 "Feedback loop"

See merge request World/Phosh/squeekboard!513
2021-12-21 15:48:51 +00:00
6dae43b437 ci: Use bookworm image
The image for x86_64 is a custom one from the registry.
2021-12-17 14:04:58 +00:00
8eb1c9b4a7 build: Remove regex crate 2021-12-17 14:04:22 +00:00
89 changed files with 4338 additions and 919 deletions

View File

@ -1,15 +1,13 @@
image: debian:bullseye
image: pureos/byzantium
stages:
- build
- test
- deploy
before_script:
- apt-get -y update
- apt-get -y install wget ca-certificates gnupg
- echo "deb [trusted=yes] http://ci.puri.sm/ bullseyeci main" > /etc/apt/sources.list.d/ci.list
- wget -O- https://ci.puri.sm/ci-repo.key | apt-key add -
- apt-get -y update
- apt-get -y install ca-certificates
build_docs:
stage: build
@ -56,6 +54,7 @@ build_deb:
- cp ../*.deb .
build_deb:arm64:
image: pureos/byzantium
tags:
- aarch64
stage: build
@ -74,6 +73,44 @@ build_deb:arm64:
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:future:
image: debian:sid
allow_failure: true
tags:
- aarch64
stage: build
artifacts:
paths:
- '*.deb'
script:
- rm -f ../*.deb
- mv debian/control-newer debian/control
- apt-get -y build-dep .
- apt-get -y install devscripts
- REV=$(git log -1 --format=%h)
- VER=$(dpkg-parsechangelog -SVersion)
- DEBFULLNAME="Librem5 CI"
- EMAIL="librem5-builds@lists.community.puri.sm"
- dch -v"$VER+librem5ci$CI_PIPELINE_ID.$REV" "$MSG"
- debuild -i -us -uc -b
- cp ../*.deb .
build_reference:
stage: build
needs:
- job: build_meson
artifacts: true
artifacts:
paths:
- _build/doc
script:
- apt-get -y install cargo
- cd _build
- ../cargo.sh doc --no-deps --document-private-items
except:
variables:
- $PKG_ONLY == "1"
test_lintian:
stage: test
needs:
@ -122,3 +159,17 @@ check_release:
except:
variables:
- $PKG_ONLY == "1"
pages:
stage: deploy
needs:
- build_docs
- build_reference
script:
- mv _build/ public/
artifacts:
paths:
- public
only:
refs:
- master

View File

@ -1,11 +1,17 @@
# Dependencies which change based on build flags
bitflags = "1.2.*"
clap = { version = "2.33.*", default-features = false }
regex = { version = "1.3.*", default-features = false, features = ["std", "unicode-case"] }
zbus = "1.0.*"
zvariant = "2.0.*"
# Newer versions seem to confuse the version of Cargo on Debian Bullseye
zvariant_derive = "2.0.*"
[dependencies.cairo-rs]
version = "0.7.*"
[dependencies.cairo-sys-rs]
version = "0.9"
[dependencies.gdk]
version = "0.11.*"
@ -17,6 +23,14 @@ features = ["v2_44"]
version = "0.8.*"
features = ["v2_44"]
[dependencies.glib-sys]
version = "*"
features = ["v2_44"]
[dependencies.gtk]
version = "0.7.*"
features = ["v3_22"]
[dependencies.gtk-sys]
version = "0.9"
features = ["v3_22"]

View File

@ -1,22 +0,0 @@
# Dependencies which change based on build flags
bitflags = "1.0.*"
clap = { version = "2.32.*", default-features = false }
regex = { version = "1.1.*", default-features = false, features = ['use_std'] }
[dependencies.cairo-rs]
version = "0.5.*"
[dependencies.gdk]
version = "0.9.*"
[dependencies.gio]
version = "0.5.*"
features = ["v2_44"]
[dependencies.glib]
version = "0.6.*"
features = ["v2_44"]
[dependencies.gtk]
version = "0.5.*"
features = ["v3_22"]

37
Cargo.deps.newer Normal file
View File

@ -0,0 +1,37 @@
# Dependencies which change based on build flags
# For the newer-than-Byzantium config
bitflags = "1.3.*"
clap = { version = "3.1.*", features=["std"], default-features = false }
zbus = "1.9.*"
zvariant = "2.10.*"
# Newer versions seem to confuse the version of Cargo on Debian Bullseye
zvariant_derive = "2.10.*"
[dependencies.cairo-rs]
version = "0.14.*"
[dependencies.cairo-sys-rs]
version = "0.14.*"
[dependencies.gdk]
version = "0.14.*"
[dependencies.gio]
version = "0.14.*"
features = ["v2_58"]
[dependencies.glib]
version = "0.14.*"
features = ["v2_58"]
[dependencies.glib-sys]
version = "0.14.*"
features = ["v2_58"]
[dependencies.gtk]
version = "0.14.*"
features = ["v3_22"]
[dependencies.gtk-sys]
version = "0.14.*"
features = ["v3_22"]

4
Cargo.deps.online Normal file
View File

@ -0,0 +1,4 @@
# Dependencies which are only used with online, crates.io builds.
[patch.crates-io]
# Dependency was yanked, but gio 0.7 needs it.
fragile = { git = "https://source.puri.sm/dorota.czaplejewicz/fragile.git", tag = "0.3.0" }

255
Cargo.lock generated
View File

@ -30,9 +30,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
@ -40,6 +40,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cairo-rs"
version = "0.7.1"
@ -67,15 +73,27 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.72"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
version = "2.33.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "826bf7bc84f9435630275cb8e802a4a0ec792b615969934bd16d42ffed10f207"
dependencies = [
"bitflags",
"textwrap",
@ -83,16 +101,50 @@ dependencies = [
]
[[package]]
name = "dtoa"
version = "0.4.8"
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "enumflags2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fastrand"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
[[package]]
name = "fragile"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
source = "git+https://source.puri.sm/dorota.czaplejewicz/fragile.git?tag=0.3.0#51048ca11824279c2114c77fef5bcb950838fc09"
[[package]]
name = "gdk"
@ -273,14 +325,23 @@ checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "indexmap"
version = "1.7.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -289,9 +350,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.108"
version = "0.2.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
[[package]]
name = "linked-hash-map"
@ -315,6 +376,19 @@ dependencies = [
"winapi",
]
[[package]]
name = "nix"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
dependencies = [
"bitflags",
"cc",
"cfg-if 0.1.10",
"libc",
"void",
]
[[package]]
name = "pango"
version = "0.7.0"
@ -344,43 +418,37 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.22"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
"toml",
]
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rs"
version = "0.1.0"
@ -396,26 +464,51 @@ dependencies = [
"gtk",
"gtk-sys",
"maplit",
"regex",
"serde",
"serde_yaml",
"xkbcommon",
"zbus",
"zvariant",
"zvariant_derive",
]
[[package]]
name = "serde"
version = "1.0.130"
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "serde"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_repr"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
dependencies = [
"proc-macro2",
"quote",
@ -424,21 +517,21 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.8.21"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
dependencies = [
"dtoa",
"indexmap",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "syn"
version = "1.0.82"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
dependencies = [
"proc-macro2",
"quote",
@ -454,6 +547,15 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
@ -466,6 +568,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "winapi"
version = "0.3.9"
@ -506,3 +614,56 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "zbus"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cb97c72cbfd5c7537ca730eeb810da7348f345ba67ab7673bcbe0d81c076427"
dependencies = [
"byteorder",
"derivative",
"enumflags2",
"fastrand",
"nix",
"scoped-tls",
"serde",
"serde_repr",
"zbus_macros",
"zvariant",
"zvariant_derive",
]
[[package]]
name = "zbus_macros"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0c1f2a20a4cb90922b44d3bebd232b246e52b3dd95ed5bea8aec83cde3a5a8a"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zvariant"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0bf85e67d1a3780cb1c56c80227532354f21907cba14805a773eb507b444580"
dependencies = [
"byteorder",
"enumflags2",
"serde",
]
[[package]]
name = "zvariant_derive"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d68726e8c12757384a8d1485080527e263dea67d91f19e97cd71b9292f22d7c5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@ -17,22 +17,9 @@ name = "test_layout"
path = "@path@/examples/test_layout.rs"
[features]
gio_v0_5 = []
gtk_v0_5 = []
rustc_less_1_36 = []
glib_v0_14 = []
# Dependencies which don't change based on build flags
[dependencies.cairo-sys-rs]
version = "*"
[dependencies.glib-sys]
version = "*"
features = ["v2_44"]
[dependencies.gtk-sys]
version = "*"
features = ["v3_22"]
[dependencies]
maplit = "1.0.*"
serde = { version = "1.0.*", features = ["derive"] }

View File

@ -86,4 +86,4 @@ It's strongly recommended to support:
Developing
----------
See [`doc/hacking.md`](doc/hacking.md) for this copy, or the [official documentation](https://developer.puri.sm/projects/squeekboard/) for the current release.
See [`doc/hacking.md`](doc/hacking.md) for this copy, or the [official documentation](https://world.pages.gitlab.gnome.org/Phosh/squeekboard) for the current release.

View File

@ -1,6 +1,33 @@
/* Theme independent style */
/* Theme independent styles */
sq_view {
font-family: cantarell, sans-serif;
font-size: 1.5em;
}
sq_button {
border-radius: 4px;
margin: 2px;
}
sq_view.wide sq_button {
margin: 3px;
}
sq_button.latched,
sq_button.locked {
font-weight: bold;
}
sq_button.action {
font-size: 0.75em;
}
sq_button.small {
font-size: 0.5em;
}
sq_view.pin sq_button {
border-radius: 0px;
margin: 1px 1px 1px 1px;
}
}

View File

@ -0,0 +1,326 @@
# Greek polytonic layout by Antonis Tsolomitis
# University of the Aegean, Department of Mathematics, atsol@aegean.gr
# March 2022
#
---
outlines:
default: { width: 40, height: 60 }
altline: { width: 52.67, height: 60 }
wide: { width: 62, height: 60 }
extrawide: { width: 66, height: 60 }
spaceline: { width: 140, height: 60 }
special: { width: 44, height: 60 }
views:
base:
- "semicolon ς ε ρ τ υ θ ι ο π"
- "α σ δ φ γ η ξ κ λ show_accents"
- "Shift_L ζ χ ψ ω β ν μ BackSpace"
- "show_numbers preferences space period comma Return"
upper:
- "colon EuroSign Ε Ρ Τ Υ Θ Ι Ο Π"
- "Α Σ Δ Φ Γ Η Ξ Κ Λ show_accents"
- "Shift_L Ζ Χ Ψ Ω Β Ν Μ BackSpace"
- "show_numbers preferences space exclam period_upper Return"
accents:
- "show_psiliordasiaandvaria show_psiliordasiaandoxia show_psiliordasia show_bariaorperispomeni show_oxia"
- "show_PsiliOrDasiaAndVaria show_PsiliOrDasiaAndOxia show_PsiliOrDasia show_BariaOrPerispomeni show_Oxia show_base"
- "show_PsiliOrDasiaAndPerispomeni show_psiliordasiaandperispomeni ᾿ BackSpace"
- "show_numbers preferences space Return"
oxia:
- "ά έ ή ί ϊ ΐ ό ύ ϋ ώ"
- "show_Oxia ᾳ ᾴ ῃ ῄ ῳ ῴ show_base"
- "Ϗ ϐ ϑ ϗ ϖ ΰ ϕ — BackSpace"
- "show_numbers preferences space eis_l eis_r Return"
Oxia:
- "Ά Έ Ή Ί Ϊ Ό Ύ Ϋ Ώ"
- "show_oxia ᾼ ῌ ῼ show_base"
- "Ϗ ϐ ϑ ϗ ϖ ϕ — BackSpace"
- "show_numbers preferences space eis_l eis_r Return"
bariaorperispomeni:
- "ὰ ὲ ὴ ὶ ῒ ὸ ὺ ὼ ῐ ῑ"
- "show_BariaOrPerispomeni ᾳ ᾲ ῃ ῂ ῳ ῲ ῠ show_base"
- "ᾶ ᾷ ῆ ῖ ῗ ῦ ῧ ῶ ῡ BackSpace"
- "show_numbers preferences space ῇ ῷ Return"
BariaOrPerispomeni:
- "Ὰ Ὲ Ὴ Ὶ Ὸ Ὺ Ὼ"
- "show_bariaorperispomeni ᾼ ῌ ῼ show_base"
- "show_numbers preferences space BackSpace Return"
psiliordasia:
- "ἀ ἐ ἠ ἰ ὀ ὐ ὠ ᾀ ᾐ ᾠ"
- "show_PsiliOrDasia ἁ ἑ ἡ ἱ ὁ ὑ ὡ show_base"
- "ᾁ ᾑ ᾡ ῤ ῥ BackSpace"
- "show_numbers preferences space Return"
PsiliOrDasia:
- "Ἀ Ἐ Ἠ Ἰ Ὀ Ὠ ᾈ ᾘ ᾨ"
- "show_psiliordasia Ἁ Ἑ Ἡ Ἱ Ὁ Ὑ Ὡ show_base"
- "ᾉ ᾙ ᾩ Ῥ BackSpace"
- "show_numbers preferences space Return"
psiliordasiaandoxia:
- "ἄ ἔ ἤ ἴ ὄ ὔ ὤ ᾄ ᾔ ᾤ"
- "show_PsiliOrDasiaAndOxia ἅ ἕ ἥ ἵ ὅ ὕ ὥ show_base"
- "ᾅ ᾕ ᾥ BackSpace"
- "show_numbers preferences space Return"
PsiliOrDasiaAndOxia:
- "Ἄ Ἔ Ἤ Ἴ Ὄ Ὤ ᾌ ᾜ ᾬ"
- "show_psiliordasiaandoxia Ἅ Ἕ Ἥ Ἵ Ὅ Ὕ Ὥ show_base"
- "ᾍ ᾝ ᾭ BackSpace"
- "show_numbers preferences space Return"
psiliordasiaandvaria:
- "ἂ ἒ ἢ ἲ ὂ ὒ ὢ ᾂ ᾒ ᾢ"
- "show_PsiliOrDasiaAndVaria ἃ ἓ ἣ ἳ ὃ ὓ ὣ show_base"
- "ᾃ ᾓ ᾣ BackSpace"
- "show_numbers preferences space Return"
PsiliOrDasiaAndVaria:
- "Ἂ Ἒ Ἢ Ἲ Ὂ Ὢ ᾊ ᾚ ᾪ"
- "show_psiliordasiaandvaria Ἃ Ἓ Ἣ Ἳ Ὃ Ὓ Ὣ show_base"
- "ᾋ ᾛ ᾫ BackSpace"
- "show_numbers preferences space Return"
psiliordasiaandperispomeni:
- "ἆ ἦ ἶ ὖ ὦ ᾆ ᾖ ᾦ"
- "show_PsiliOrDasiaAndPerispomeni ἇ ἧ ἷ ὗ ὧ show_base"
- "ᾇ ᾗ ᾧ BackSpace"
- "show_numbers preferences space Return"
PsiliOrDasiaAndPerispomeni:
- "Ἆ Ἦ Ἶ Ὦ ᾎ ᾞ ᾮ"
- "show_psiliordasiaandperispomeni Ἇ Ἧ Ἷ Ὗ Ὧ show_base"
- "ᾏ ᾟ ᾯ BackSpace"
- "show_numbers preferences space Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "at numbersign dollar percent ampersand minus underscore plus parenleft parenright"
- "show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace"
- "show_letters preferences space period comma Return"
symbols:
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
- "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
- "show_letters preferences space period comma Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: "erase"
preferences:
action: "show_prefs"
outline: "altline"
icon: "keyboard-mode-symbolic"
show_oxia:
action:
set_view: "oxia"
outline: "extrawide"
label: " ΅"
show_Oxia:
action:
set_view: "Oxia"
outline: "extrawide"
label: "´¨↑"
show_bariaorperispomeni:
action:
set_view: "bariaorperispomeni"
outline: "extrawide"
label: " "
show_BariaOrPerispomeni:
action:
set_view: "BariaOrPerispomeni"
outline: "extrawide"
label: "`῀↑"
show_psiliordasia:
action:
set_view: "psiliordasia"
outline: "extrawide"
label: "᾿ "
show_PsiliOrDasia:
action:
set_view: "PsiliOrDasia"
outline: "extrawide"
label: "᾿῾↑"
show_psiliordasiaandoxia:
action:
set_view: "psiliordasiaandoxia"
outline: "extrawide"
label: "῎ ῞"
show_PsiliOrDasiaAndOxia:
action:
set_view: "PsiliOrDasiaAndOxia"
outline: "extrawide"
label: "῎῞↑"
show_psiliordasiaandvaria:
action:
set_view: "psiliordasiaandvaria"
outline: "extrawide"
label: "῍ ῝"
show_PsiliOrDasiaAndVaria:
action:
set_view: "PsiliOrDasiaAndVaria"
outline: "extrawide"
label: "῍῝↑"
show_psiliordasiaandperispomeni:
action:
set_view: "psiliordasiaandperispomeni"
outline: "extrawide"
label: "῏ ῟"
show_PsiliOrDasiaAndPerispomeni:
action:
set_view: "PsiliOrDasiaAndPerispomeni"
outline: "extrawide"
label: "῏῟↑"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "ΑΒΓ"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_accents:
action:
locking:
lock_view: "accents"
unlock_view: "base"
outline: "altline"
label: "ᾦ"
show_base:
action:
set_view: "base"
outline: "altline"
label: "αι"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
period:
outline: "special"
text: "."
period_upper:
outline: "special"
text: "·"
comma:
outline: "special"
text: ","
colon:
outline: "special"
text: ":"
semicolon:
outline: "special"
text: ";"
exclam:
outline: "special"
text: "!"
eis_l:
outline: "special"
text: "«"
eis_r:
outline: "special"
text: "»"
aring:
text: "å"
Aring:
text: "Å"
oslash:
text: "ø"
Oslash:
text: "Ø"
ae:
text: "æ"
AE:
text: "Æ"
asterisk:
text: "*"
asciitilde:
text: "~"
quoteleft:
text: "`"
bar:
text: "|"
U00B7:
text: "·"
squareroot:
text: "√"
Greek_pi:
text: "π"
division:
text: "÷"
multiply:
text: "×"
paragraph:
text: "¶"
Greek_tau:
text: "τ"
copyright:
text: "©"
numbersign:
text: "#"
U00AE:
text: "®"
at:
text: "@"
dollar:
text: "$"
U00A3:
text: "£"
percent:
text: "%"
EuroSign:
outline: "special"
text: "€"
ampersand:
text: "&"
U00A5:
text: "¥"
minus:
text: "-"
asciicircum:
text: "^"
underscore:
text: "_"
degree:
text: "°"
plus:
text: "+"
equal:
text: "="
parenleft:
text: "("
parenright:
text: ")"
braceleft:
text: "{"
braceright:
text: "}"
backslash:
text: "\\"
slash:
text: "/"
quotedbl:
text: "\""
quoteright:
text: "'"
less:
text: "<"
greater:
text: ">"
question:
text: "?"
bracketleft:
text: "["
bracketright:
text: "]"

View File

@ -1,40 +1,41 @@
# Greek layout created by Antonis Tsolomitis
# Greek layout originally created by Antonis Tsolomitis
# University of the Aegean, Department of Mathematics, atsol@aegean.gr
# Sep 2019
# Edited by Sotiris Papadopoulos, sotirios.papadopoulos@inserm.fr
---
outlines:
default: { width: 32, height: 52 }
altline: { width: 48.39024, height: 52 }
wide: { width: 62, height: 52 }
outline7: { width: 88.97561, height: 52 }
spaceline: { width: 150.5853, height: 52 }
default: { width: 40, height: 60 }
altline: { width: 52.67, height: 60 }
wide: { width: 62, height: 60 }
spaceline: { width: 140, height: 60 }
special: { width: 44, height: 60 }
views:
base:
- "; ς ε ρ τ υ θ ι ο π !"
- "semicolon ς ε ρ τ υ θ ι ο π"
- "α σ δ φ γ η ξ κ λ show_accented"
- "Shift_L ζ χ ψ ω β ν μ , BackSpace"
- "show_numbers preferences space period Return"
- "Shift_L ζ χ ψ ω β ν μ BackSpace"
- "show_numbers preferences space period comma Return"
upper:
- ": EuroSign Ε Ρ Τ Υ Θ Ι Ο Π"
- "colon exclam Ε Ρ Τ Υ Θ Ι Ο Π"
- "Α Σ Δ Φ Γ Η Ξ Κ Λ show_accented"
- "Shift_L Ζ Χ Ψ Ω Β Ν Μ · BackSpace"
- "show_numbers preferences space « » Return"
- "Shift_L Ζ Χ Ψ Ω Β Ν Μ BackSpace"
- "show_numbers preferences space period_upper apostrophe Return"
accented:
- "ά έ ή ί ό ύ ώ ϊ ϋ ΐ"
- "ΰ Ά Έ Ή Ί Ό Ύ Ώ Ϊ show_base"
- "Ϋ Ϗ ϐ ϑ ϕ ϖ ϗ — BackSpace"
- "show_numbers preferences space quoteleft quoteright Return"
- "ά έ ή ί ϊ ΐ ό ύ ϋ ώ "
- "Ά Έ Ή Ί Ϊ Ό Ύ Ϋ Ώ show_base"
- "Ϗ ϐ ϑ ϗ ϖ ΰ ϕ — BackSpace"
- "show_numbers preferences space eis_l eis_r Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "at numbersign dollar percent ampersand minus underscore plus parenleft parenright"
- "show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace"
- "show_letters preferences space period Return"
- "show_letters preferences space period comma Return"
symbols:
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
- "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
- "show_letters preferences space period Return"
- "show_letters preferences space period comma Return"
buttons:
Shift_L:
action:
@ -54,12 +55,12 @@ buttons:
show_numbers:
action:
set_view: "numbers"
outline: "altline"
outline: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "altline"
outline: "wide"
label: "ΑΒΓ"
show_symbols:
action:
@ -78,16 +79,40 @@ buttons:
set_view: "base"
outline: "altline"
label: "αι"
period:
outline: "altline"
text: "."
space:
outline: spaceline
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
period:
outline: "special"
text: "."
period_upper:
outline: "special"
text: "·"
comma:
outline: "special"
text: ","
colon:
outline: "special"
text: ":"
semicolon:
outline: "special"
text: ";"
apostrophe:
outline: "special"
text: "᾿"
exclam:
outline: "special"
text: "!"
eis_l:
outline: "special"
text: "«"
eis_r:
outline: "special"
text: "»"
aring:
text: "å"
Aring:
@ -162,8 +187,6 @@ buttons:
text: "{"
braceright:
text: "}"
comma:
text: ","
backslash:
text: "\\"
slash:
@ -176,12 +199,6 @@ buttons:
text: "<"
greater:
text: ">"
colon:
text: ":"
semicolon:
text: ";"
exclam:
text: "!"
question:
text: "?"
bracketleft:

204
data/keyboards/gr_wide.yaml Normal file
View File

@ -0,0 +1,204 @@
# Creaed by Sotiris Papadopoulos, sotirios.papadopoulos@inserm.fr
---
outlines:
default: { width: 80, height: 60 }
altline: { width: 110, height: 60 }
wide: { width: 120, height: 60 }
spaceline: { width: 250, height: 60 }
special: { width: 75, height: 60 }
views:
base:
- "semicolon ς ε ρ τ υ θ ι ο π"
- "α σ δ φ γ η ξ κ λ show_accented"
- "Shift_L ζ χ ψ ω β ν μ BackSpace"
- "show_numbers preferences space period comma Return"
upper:
- "colon exclam Ε Ρ Τ Υ Θ Ι Ο Π"
- "Α Σ Δ Φ Γ Η Ξ Κ Λ show_accented"
- "Shift_L Ζ Χ Ψ Ω Β Ν Μ BackSpace"
- "show_numbers preferences space period_upper apostrophe Return"
accented:
- "ά έ ή ί ϊ ΐ ό ύ ϋ ώ "
- "Ά Έ Ή Ί Ϊ Ό Ύ Ϋ Ώ show_base"
- "Ϗ ϐ ϑ ϗ ΰ ϕ ϖ — BackSpace"
- "show_numbers preferences space eis_l eis_r Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "at numbersign dollar percent ampersand minus underscore plus parenleft parenright"
- "show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace"
- "show_letters preferences space period comma Return"
symbols:
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
- "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
- "show_letters preferences space period comma Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: "erase"
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "ΑΒΓ"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_accented:
action:
locking:
lock_view: "accented"
unlock_view: "base"
outline: "altline"
label: "άΐ"
show_base:
action:
set_view: "base"
outline: "altline"
label: "αι"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
period:
outline: "special"
text: "."
period_upper:
outline: "special"
text: "·"
comma:
outline: "special"
text: ","
colon:
outline: "special"
text: ":"
semicolon:
outline: "special"
text: ";"
apostrophe:
outline: "special"
text: "᾿"
exclam:
outline: "special"
text: "!"
eis_l:
outline: "special"
text: "«"
eis_r:
outline: "special"
text: "»"
aring:
text: "å"
Aring:
text: "Å"
oslash:
text: "ø"
Oslash:
text: "Ø"
ae:
text: "æ"
AE:
text: "Æ"
asterisk:
text: "*"
asciitilde:
text: "~"
quoteleft:
text: "`"
bar:
text: "|"
U00B7:
text: "·"
squareroot:
text: "√"
Greek_pi:
text: "π"
division:
text: "÷"
multiply:
text: "×"
paragraph:
text: "¶"
Greek_tau:
text: "τ"
copyright:
text: "©"
numbersign:
text: "#"
U00AE:
text: "®"
at:
text: "@"
dollar:
text: "$"
U00A3:
text: "£"
percent:
text: "%"
EuroSign:
text: "€"
ampersand:
text: "&"
U00A5:
text: "¥"
minus:
text: "-"
asciicircum:
text: "^"
underscore:
text: "_"
degree:
text: "°"
plus:
text: "+"
equal:
text: "="
parenleft:
text: "("
parenright:
text: ")"
braceleft:
text: "{"
braceright:
text: "}"
backslash:
text: "\\"
slash:
text: "/"
quotedbl:
text: "\""
quoteright:
text: "'"
less:
text: "<"
greater:
text: ">"
question:
text: "?"
bracketleft:
text: "["
bracketright:
text: "]"

89
data/keyboards/ro.yaml Normal file
View File

@ -0,0 +1,89 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 62, height: 52 }
spaceline: { width: 99.67, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "q w e r t y u i o p"
- "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace"
- "show_numbers show_eschars preferences space . Return"
upper:
- "Q W E R T Y U I O P"
- "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers show_eschars preferences space , Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters show_eschars preferences space . Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters show_eschars preferences space , Return"
eschars:
- "ă â î ș ț á é í ó ü"
- "Ă Â Î Ș Ț Á É Í Ó Ü"
- "show_numbers_from_symbols „ ” « » ― { } BackSpace"
- "show_letters show_eschars preferences space . Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: "erase"
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "ăĂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

View File

@ -0,0 +1,89 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 108, height: 42 }
spaceline: { width: 153, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "q w e r t y u i o p"
- "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace"
- "show_numbers show_eschars preferences space . Return"
upper:
- "Q W E R T Y U I O P"
- "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers show_eschars preferences space , Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters show_eschars preferences space . Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters show_eschars preferences space , Return"
eschars:
- "ă â î ș ț á é í ó ü"
- "Ă Â Î Ș Ț Á É Í Ó Ü"
- "show_numbers_from_symbols „ ” « » ― { } BackSpace"
- "show_letters show_eschars preferences space . Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: "erase"
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "ăĂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

View File

@ -1,34 +1,22 @@
/* Adwaita-dark style keyboard */
sq_view {
background-color: rgba(0, 0, 0, 255);
color: #ffffff;
font-family: cantarell, sans-serif;
font-size: 25px;
}
sq_view sq_button {
color: #deddda;
background: #464448;
border-style: solid;
border-width: 1px;
border-color: #5e5c64;
border-radius: 3px;
margin: 4px 2px 4px 2px;
}
sq_view.wide sq_button {
margin: 1px 1px 1px 1px;
}
sq_button:active {
background: #747077;
border-color: #96949d;
}
sq_button.altline,
sq_button.special,
sq_button.wide {
background: #2b292f;
border-color: #3e3a44;
}
sq_button.latched {
@ -41,22 +29,12 @@ sq_button.locked {
color: #1c71d8;
}
sq_button.action {
font-size: 0.75em;
}
sq_button.small {
font-size: 0.5em;
}
#Return {
background: #1c71d8;
border-color: #1a5fb4;
}
#Return:active {
background: #1c71d8;
border-color: #3584e4;
}
@import url("resource:///sm/puri/squeekboard/common.css");

View File

@ -1,65 +1,47 @@
/* Keyboard style */
sq_view {
background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/
color: @theme_text_color; /*#ffffff;*/
font-family: cantarell, sans-serif;
font-size: 25px;
background-color: mix(@theme_base_color, @theme_fg_color, 0.1);
box-shadow:inset 0 1px 0 0 mix(@borders, @theme_base_color, 0.8);
}
sq_view sq_button {
color: @theme_fg_color; /*#deddda;*/
background: mix(@theme_bg_color, @theme_base_color, -0.5); /* #464448; */
border-style: solid;
border-width: 1px;
border-color: @borders; /* #5e5c64;*/
border-radius: 3px;
margin: 4px 2px 4px 2px;
sq_button {
color: @theme_fg_color;
background: alpha(@theme_fg_color, 0.07);
box-shadow: 0 1px 0 0 rgba(0,0,0,0.2);
}
sq_view.wide sq_button {
margin: 1px 1px 1px 1px;
}
sq_button:active,
sq_button.altline:active,
sq_button.special:active,
sq_button.wide:active {
background: mix(@theme_bg_color, @theme_selected_bg_color, 0.4);/* #747077; */
border-color: mix(@borders, @theme_selected_fg_color, 0.5);/* #96949d; */
sq_button:active {
background: alpha(@theme_fg_color, 0.11);
}
sq_button.altline,
sq_button.special,
sq_button.wide {
background: mix(@theme_bg_color, @theme_base_color, 0.5); /*#2b292f;*/
border-color: @borders; /* #3e3a44; */
sq_button.special {
background: alpha(@theme_fg_color, 0.15);
}
sq_button.altline:active,
sq_button.special:active {
background: alpha(@theme_fg_color, 0.2);
}
sq_button.latched {
background: @theme_fg_color; /*#ffffff;*/
color: @theme_bg_color; /*#2b292f;*/
background: alpha(@theme_fg_color, 0.2);
color: alpha(@theme_fg_color, 0.8);
}
sq_button.locked {
background: @theme_fg_color; /*#ffffff;*/
color: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#2b292f;*/
}
sq_button.action {
font-size: 0.75em;
}
sq_button.small {
font-size: 0.5em;
background: alpha(@theme_fg_color, 0.5);
color: @theme_base_color;
}
#Return {
background: @theme_selected_bg_color; /* #1c71d8; */
border-color: @borders; /*#1a5fb4;*/
background: @theme_selected_bg_color;
color: @theme_selected_fg_color;
}
#Return:active {
background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/
border-color: @borders; /*#3584e4;*/
background: mix(@theme_selected_bg_color, black, 0.2);
color: mix(@theme_selected_fg_color, black, 0.2);
}
@import url("resource:///sm/puri/squeekboard/common.css");

1
debian/cargo/config vendored
View File

@ -9,4 +9,3 @@ replace-with = 'vendored-sources'
[source.vendored-sources]
directory = '/usr/share/cargo/registry'

162
debian/changelog vendored
View File

@ -1,3 +1,165 @@
squeekboard (1.18.0-1) experimental; urgency=medium
[ Hugo Carvalho ]
* Add Portuguese translation
[ Мирослав Николић ]
* Add Serbian translation
[ William Wold ]
* Do not reset pending state on zwp_input_method_v2.done
[ Balázs Úr ]
* Add Hungarian translation
[ Emin Tufan Çetin ]
* Add Turkish translation
[ Piotr Drąg ]
* Add Polish translation
[ Pablo Correa Gómez ]
* Add Spanish translation
[ Vittorio Monti ]
* Add Italian translation
[ Dorota Czaplejewicz ]
* build: Replace missing crates.io dependency with Purism-hosted one
* ci: Allow failure on sid
* build: Update clap on newer Debian
* panel: Use scaling to set height
* layouts: Add Greek Polytonic
* debug: Add dbus interface to control debug prints
* output: Store physical size
* state: Derive panel size from physical click target size
* Clean up size types
* state: Add sizing unit test
* layouts: Register gr_wide
* CI: Build Rust code reference
* CI: Add gitlab pages deployment
* panel: Split away panel handling
* cargo: Add zbus to newer Debian
* docs: Update location
* docs: Link to reference
* docs: Make index more logical
* Update Cargo lock
[ Sotiris Papadopoulos ]
* Update gr.yaml to take advantage of more space per symbol. Creation of a wide variant...
[ Arnaud Ferraris ]
* state: fix "wide mode" detection in portrait orientation
* layout: allow stretching the layout by a small amount
* layout: fix build on i386
[ Sungjoon Moon ]
* Add Korean translation
[ Quentin PAGÈS ]
* Add Occitan translation
[ Zurab Kargareteli ]
* Add Georgian translation
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 25 Apr 2022 13:12:36 +0000
squeekboard (1.17.0-1) experimental; urgency=medium
[ Dorota Czaplejewicz ]
* docs: Detail the release process better
* cleanup: Remove unused header lines
* docstrings: Clarify the purpose of Receiver
* wayland: Move initialization to the Rust side
* ffi: Remove unnecessary pointers to InputMethod
* outputs: Clean up for more Rust usage
* outputs: Notify the state manager about changes
* outputs: Handle removal
* Save outputs state
* Store preferred output
* deps: Vendor assert_matches
* Carry output information on visible command all the way to C
* Don't reach for globals to choose output
* visibility: Forward panel height information to window creation
* outputs: Remove ui manager
* output: Use new source of panel height information
* panel: Apply a hard limit of 1/2 height
* cargo: Update lockfile
[ Cosmin Humeniuc ]
* Add Romanian layout
[ Sam Hewitt ]
* data: Update stylesheet with upstream design
[ Tor ]
* Make compatible with latest cargo deps
[ Luís Fernando Stürmer da Rosa ]
* Update Brazilian Portuguese translation
[ Fran Dieguez ]
* Add Galician translation
[ William Wold ]
* Check if dbus handler is null before using
[ Yosef Or Boczko ]
* Add Hebrew translation
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 25 Jan 2022 11:24:04 +0000
squeekboard (1.16.0-1) experimental; urgency=medium
[ Dorota Czaplejewicz ]
* build: Remove regex crate
* ci: Use bookworm image
* build: Pin transitive dependencies
* cargo: Update Cargo.lock with pinned dependencies
* CI: Use byzantium as the base
* cargo: Bump dependencies
[ Guido Günther ]
* po: Fix ui file name
* entry: Mark as executable
* entry: Only activate purpose timer when focused
* entry: Add another input hint
* Add entry test using GTK4
[ Rafael Fontenelle ]
* Add Brazilian Portuguese translation
[ Yuri Chornoivan ]
* Add Ukrainian translation
[ Luna Jernberg ]
* Add Swedish Translation
* Update sv.po
* Update LINGUAS
[ Fabio Tomat ]
* Add Friulian translation
[ Daniel Șerbănescu ]
* Add Romanian translation
[ Matej Urbančič ]
* Add Slovenian translation
[ Nathan Follens ]
* Add Dutch translation
[ Jiri Grönroos ]
* Add Finnish translation
[ Danial Behzadi ]
* Add Persian translation
[ Jordi Mas i Hernandez ]
* Add Catalan translation
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 25 Jan 2022 11:24:04 +0000
squeekboard (1.15.0-1) experimental; urgency=medium
[ Khaled Eldoheiri ]

2
debian/control vendored
View File

@ -20,10 +20,10 @@ Build-Depends:
librust-gtk+v3-22-dev (>= 0.5),
librust-gtk-sys-dev,
librust-maplit-1-dev (>= 1.0),
librust-regex-1-dev (>= 1.1),
librust-serde-derive-1-dev (>= 1.0),
librust-serde-yaml-0.8-dev (>= 0.8),
librust-xkbcommon-0.4+wayland-dev (>= 0.4),
librust-zbus-dev (>=1.0),
libwayland-dev (>= 1.16),
lsb-release,
python3,

59
debian/control-newer vendored Normal file
View File

@ -0,0 +1,59 @@
Source: squeekboard
Section: x11
Priority: optional
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
Build-Depends:
cargo,
debhelper-compat (= 13),
meson (>=0.51.0),
ninja-build,
pkg-config,
libglib2.0-dev,
libgnome-desktop-3-dev,
libgtk-3-dev,
libfeedback-dev,
librust-bitflags-dev (>= 1.0),
librust-clap-dev (>= 2.32),
librust-gio+v2-58-dev,
librust-glib+v2-58-dev,
librust-glib-sys-dev,
librust-gtk+v3-22-dev (>= 0.5),
librust-gtk-sys-dev,
librust-maplit-1-dev (>= 1.0),
librust-serde-derive-1-dev (>= 1.0),
librust-serde-yaml-0.8-dev (>= 0.8),
librust-xkbcommon-0.4+wayland-dev (>= 0.4),
librust-zbus-dev (>= 1.9),
libwayland-dev (>= 1.16),
lsb-release,
python3,
python3-ruamel.yaml,
rustc,
wayland-protocols (>= 1.14),
Standards-Version: 4.1.3
Homepage: https://source.puri.sm/Librem5/squeekboard
Package: squeekboard
Architecture: linux-any
Depends:
# for the Adwaita-dark theme
gnome-themes-extra-data,
${shlibs:Depends},
${misc:Depends},
Breaks:
librem5-base (<< 24),
Description: On-screen keyboard for Wayland
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
Package: squeekboard-devel
Architecture: linux-any
Depends:
python3,
python3-gi,
${shlibs:Depends},
${misc:Depends},
Description: Resources for making Squeekboard layouts
Tools for creating and testing Squeekboard layouts:
.
* squeekboard-entry
* squeekboard-test-layout

8
debian/rules vendored
View File

@ -25,10 +25,10 @@ export RUSTFLAGS = --remap-path-prefix=$(CURDIR)=/remap-pwd $(xgot)
distrel := $(shell lsb_release --codename --short)
ifneq (,$(filter $(distrel),buster amber))
legacy = true
ifneq (,$(filter $(distrel),sid))
newer = true
else
legacy = false
newer = false
endif
%:
@ -38,6 +38,6 @@ endif
# causing Cargo to refuse to build with a crates.io copy
override_dh_auto_configure:
[ ! -f Cargo.lock ] || rm Cargo.lock
dh_auto_configure -- -Dlegacy=$(legacy)
dh_auto_configure -- -Dnewer=$(newer) -Donline=false
override_dh_autoreconf:

View File

@ -102,6 +102,17 @@ contain a comma separated list of:
Coding
------
### Reference docs
Reference documentation can be generated using:
```
cd squeekboard_build/
../squeekboard_source/cargo.sh doc --no-deps --document-private-items
```
as well as found [online](https://world.pages.gitlab.gnome.org/Phosh/squeekboard/doc/rs/).
### Project structure
Rust modules should be split into 2 categories: libraries, and user interface. They differ in the way they do error handling.
@ -205,6 +216,7 @@ While the file is not actually used, it's a good idea to save the config in case
```
cd squeekboard-build
.../squeekboard-source/cargo.sh update
ninja test
cp ./Cargo.lock .../squeekboard-source
```

View File

@ -1,13 +1,6 @@
Welcome to squeekboard's documentation!
=======================================
Contents
--------
* [Tutorial](tutorial.md)
* [Contributing](hacking.md)
* [Switching views](views.md)
Introduction
------------
@ -22,9 +15,15 @@ Layouts are created using a text-based format, based on YAML.
TODO: Provide a description of the format.
### Views
Squeekboard layouts are separated into *views* and use a *room metaphor* to [switch views](views.md).
Contributions
-------------
Anyone is free to modify *squeekboard*. See the [contributing document](hacking.md).
### Code documentation
To expose the structure of Squeekboard in detail, there's a [code reference](doc/rs).

View File

@ -55,6 +55,8 @@ typedef struct _EekGtkKeyboardPrivate
GdkEventSequence *sequence; // unowned reference
LfbEvent *event;
gulong kb_signal;
} EekGtkKeyboardPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA)
@ -307,12 +309,19 @@ eek_gtk_keyboard_set_property (GObject *object,
}
}
// This may actually get called multiple times in a row
// if both a parent object and its parent get destroyed
static void
eek_gtk_keyboard_dispose (GObject *object)
{
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (object);
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (priv->kb_signal != 0) {
g_signal_handler_disconnect(priv->eekboard_context, priv->kb_signal);
priv->kb_signal = 0;
}
if (priv->renderer) {
eek_renderer_free(priv->renderer);
priv->renderer = NULL;
@ -418,12 +427,13 @@ eek_gtk_keyboard_new (EekboardContextService *eekservice,
.widget_to_layout = {
.origin_x = 0,
.origin_y = 0,
.scale = 1,
.scale_x = 1,
.scale_y = 1,
},
};
priv->render_geometry = initial_geometry;
g_signal_connect (eekservice,
priv->kb_signal = g_signal_connect (eekservice,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ret);

View File

@ -219,7 +219,7 @@ eek_renderer_render_keyboard (EekRenderer *self,
cairo_save(cr);
cairo_translate (cr, geometry.widget_to_layout.origin_x, geometry.widget_to_layout.origin_y);
cairo_scale (cr, geometry.widget_to_layout.scale, geometry.widget_to_layout.scale);
cairo_scale (cr, geometry.widget_to_layout.scale_x, geometry.widget_to_layout.scale_y);
squeek_draw_layout_base_view(keyboard->layout, self, cr);
squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);

View File

@ -87,7 +87,8 @@ void eek_bounds_free (EekBounds *bounds);
struct transformation {
gdouble origin_x;
gdouble origin_y;
gdouble scale;
gdouble scale_x;
gdouble scale_y;
};
G_END_DECLS

View File

@ -60,10 +60,10 @@ struct _EekboardContextService {
LevelKeyboard *keyboard; // currently used keyboard
GSettings *settings; // Owned reference
// 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
/// Needed for keymap changes after keyboard updates.
// TODO: can the main loop access submission to change the key maps instead?
// This should probably land together with passing buttons through state,
// to avoid race conditions between setting buttons and key maps.
struct submission *submission; // unowned
};
@ -297,6 +297,8 @@ eekboard_context_service_get_keyboard (EekboardContextService *context)
return context->keyboard;
}
// Used from Rust.
// TODO: move hint management to Rust entirely
void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint, uint32_t purpose)
{
@ -340,7 +342,3 @@ void eekboard_context_service_set_submission(EekboardContextService *context, st
submission_use_layout(context->submission, context->keyboard->layout, time);
}
}
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
context->ui = ui;
}

View File

@ -39,16 +39,12 @@ G_DECLARE_FINAL_TYPE(EekboardContextService, eekboard_context_service, EEKBOARD,
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state);
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission);
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
void eekboard_context_service_destroy (EekboardContextService *context);
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
void eekboard_context_service_set_keymap(EekboardContextService *context,
const LevelKeyboard *keyboard);
void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint,
uint32_t purpose);
void
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *layout, uint32_t timestamp);
G_END_DECLS

View File

@ -1,7 +1,7 @@
project(
'squeekboard',
'c', 'rust',
version: '1.15.0',
version: '1.18.0',
license: 'GPLv3',
meson_version: '>=0.51.0',
default_options: [
@ -96,19 +96,23 @@ cargo_toml_base = configure_file(
configuration: path_data,
)
cargo_patch = []
cargo_deps = files('Cargo.deps')
if get_option('legacy') == true
cargo_build_flags += ['--features', 'gtk_v0_5,gio_v0_5,rustc_less_1_36']
cargo_deps = files('Cargo.deps.legacy')
if get_option('newer') == true
cargo_build_flags += ['--features', 'glib_v0_14']
cargo_deps = files('Cargo.deps.newer')
else
cargo_deps = files('Cargo.deps')
if get_option('online') == true
cargo_patch = [files('Cargo.deps.online')]
endif
endif
cat = find_program('cat')
cargo_toml = custom_target(
'Cargo.toml',
output: 'Cargo.toml',
command: [cat, cargo_toml_base, cargo_deps],
command: [cat, cargo_toml_base, cargo_deps] + cargo_patch,
capture: true,
)

View File

@ -7,10 +7,14 @@ option('tests',
type: 'boolean', value: true,
description: 'Whether to compile unit tests')
option('legacy',
option('newer',
type: 'boolean', value: false,
description: 'Build with Deban Buster versions of dependencies')
description: 'Build with dependencies newer than those of Byzantium')
option('online',
type: 'boolean', value: true,
description: 'Pull packages from the internet while building, as opposed to a local regstry.')
option('strict',
type: 'boolean', value: true,
description: 'Turn more warnings into errors')

View File

@ -1 +1,23 @@
ca
de
es
fa
fi
fur
gl
he
hu
it
ka
ko
nl
oc
pl
pt
pt_BR
ro
sl
sr
tr
uk
sv

View File

@ -1,2 +1,2 @@
data/popup.ui
data/popover.ui
data/sm.puri.Squeekboard.desktop.in.in

45
po/ca.po Normal file
View File

@ -0,0 +1,45 @@
# Catalan translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# maite <maite.guix@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-01-11 14:31+0000\n"
"PO-Revision-Date: 2022-01-20 10:53+0100\n"
"Last-Translator: maite guix <maite.guix@me.com>\n"
"Language-Team: Catalan <gnome@llistes.softcatala.org>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Configuració del teclat"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Teclat virtual"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Teclat en pantalla"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Un teclat virtual en pantalla"

47
po/es.po Normal file
View File

@ -0,0 +1,47 @@
# Spanish translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Pablo Correa Gómez <ablocorrea@hotmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-03-20 13:14+0000\n"
"PO-Revision-Date: 2022-03-22 21:33+0100\n"
"Last-Translator: Pablo Correa Gómez <ablocorrea@hotmail.com>\n"
"Language-Team: Spanish; Castilian <gnome-es-list@gnome.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 3.36.0\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Ajustes de teclado"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Teclado en pantala"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Un teclado virtual en pantalla"

45
po/fa.po Normal file
View File

@ -0,0 +1,45 @@
# Persian translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Danial Behzadi <dani.behzi@ubuntu.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-26 15:15+0000\n"
"PO-Revision-Date: 2022-01-11 18:01+0330\n"
"Language-Team: Persian <fa@li.org>\n"
"Language: fa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Danial Behzadi <dani.behzi@ubuntu.com>\n"
"X-Generator: Poedit 3.0\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "شکلک"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "پایانه"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "تنظیمات صفحه‌کلید"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "اسکوییک‌برد"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "صفحه‌کلید لمسی"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "یک صفحهٔ کلید لمسی مجازی"

46
po/fi.po Normal file
View File

@ -0,0 +1,46 @@
# Finnish translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Jiri Grönroos <jiri.gronroos@iki.fi>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-25 13:55+0000\n"
"PO-Revision-Date: 2021-12-26 17:15+0200\n"
"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Pääte"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Näppäimistön asetukset"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Näyttönäppäimistö"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Virtuaalinen näyttönäppäimistö"

45
po/fur.po Normal file
View File

@ -0,0 +1,45 @@
# Friulian translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Fabio Tomat <f.t.public@gmail.com>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-22 13:33+0000\n"
"PO-Revision-Date: 2021-12-22 15:06+0100\n"
"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
"Language-Team: Friulian <fur@li.org>\n"
"Language: fur\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminâl"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Impostazions tastiere"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Tastiere a visôr"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Une tastiere virtuâl a visôr"

52
po/gl.po Normal file
View File

@ -0,0 +1,52 @@
# Galician translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Fran Diéguez <frandieguez@gnome.org>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-02-02 17:41+0000\n"
"PO-Revision-Date: 2022-02-04 16:18+0100\n"
"Last-Translator: Fran Diéguez <frandieguez@gnome.org>\n"
"Language-Team: Galician <proxecto@trasno.gal>\n"
"Language: gl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-DL-Team: gl\n"
"X-DL-Module: squeekboard\n"
"X-DL-Branch: master\n"
"X-DL-Domain: po\n"
"X-DL-State: Translating\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 41.0\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoticono"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Preferencias de teclado"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Teclado en pantalla"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Un teclado en pantalla virtual"

46
po/he.po Normal file
View File

@ -0,0 +1,46 @@
# Hebrew translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Yosef Or Boczko <yoseforb@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-02-04 15:22+0000\n"
"PO-Revision-Date: 2022-02-14 18:05+0200\n"
"Last-Translator: Yosef Or Boczko <yoseforb@gmail.com>\n"
"Language-Team: Hebrew <>\n"
"Language: he\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 40.0\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "רגשון"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "מסוף"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "הגדרות מקלדת"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "מקלדת על המסך"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "מקלדת מדומה על המסך"

46
po/hu.po Normal file
View File

@ -0,0 +1,46 @@
# Hungarian translation for squeekboard.
# Copyright (C) 2022 Free Software Foundation, Inc.
# This file is distributed under the same license as the squeekboard package.
#
# Balázs Úr <ur.balazs at fsf dot hu>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/issues"
"\n"
"POT-Creation-Date: 2022-03-12 12:04+0000\n"
"PO-Revision-Date: 2022-03-16 01:55+0100\n"
"Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n"
"Language-Team: Hungarian <gnome-hu-list at gnome dot org>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 19.12.3\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emodzsi"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminál"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Billentyűzetbeállítások"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Képernyő-billentyűzet"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Egy virtuális képernyő-billentyűzet"

47
po/it.po Normal file
View File

@ -0,0 +1,47 @@
# Italian translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Vittorio <postav@pm.me>, 2021.
# Vittorio Monti <postav@pm.me>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-22 19:14+0000\n"
"PO-Revision-Date: 2021-12-22 20:37+0100\n"
"Last-Translator: Vittorio Monti <postav@pm.me>\n"
"Language-Team: Italian <gnome-it-list@gnome.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 3.30.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminale"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Impostazioni tastiera"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Tastiera su schermo"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Una tastiera virtuale su schermo"

46
po/ka.po Normal file
View File

@ -0,0 +1,46 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-02-04 15:22+0000\n"
"PO-Revision-Date: 2022-02-08 03:15+0100\n"
"Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n"
"Language-Team: \n"
"Language: ka\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "ემოჯი"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "ტერმინალი"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "კლავიატურის მორგება"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "ეკრანის კლავიატურა"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "ეკრანზე ვირტუალური კლავიატურის ჩვენება"

46
po/ko.po Normal file
View File

@ -0,0 +1,46 @@
# Korean translation for squeekboard.
# Copyright (C) 2022 squeekboard'S COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Moon Sungjoon <sumoon@seoulsaram.org>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-04-09 18:43+0000\n"
"PO-Revision-Date: 2022-04-11 19:23+0900\n"
"Last-Translator: Moon Sungjoon <sumoon@seoulsaram.org>\n"
"Language-Team: \n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "이모지"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "터미널"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "키보드 설정"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "스퀴크 보드"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "화상 키보드"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "가상 키보드"

48
po/nl.po Normal file
View File

@ -0,0 +1,48 @@
# Dutch translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Jan Jasper de Kroon <jajadekroon@gmail.com>, 2021.
# Nathan Follens <nfollens@gnome.org>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-23 15:18+0000\n"
"PO-Revision-Date: 2021-12-25 14:04+0100\n"
"Last-Translator: Nathan Follens <nfollens@gnome.org>\n"
"Language-Team: Dutch <gnome-nl-list@gnome.org>\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Toetsenbordinstellingen"
# Dit is de naam van de applicatie
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Schermtoetsenbord"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Een virtueel schermtoetsenbord"

45
po/oc.po Normal file
View File

@ -0,0 +1,45 @@
# Occitan translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Quentin PAGÈS <pages_quentin@hotmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-04-17 06:24+0000\n"
"PO-Revision-Date: 2022-04-17 09:17+0200\n"
"Last-Translator: Quentin PAGÈS\n"
"Language-Team: Occitan <totenoc@gmail.com>\n"
"Language: oc\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Paramètres del clavièr"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Clavièr visual"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Un clavièr virtual sus lecran"

47
po/pl.po Normal file
View File

@ -0,0 +1,47 @@
# Polish translation for squeekboard.
# Copyright © 2022 the squeekboard authors.
# This file is distributed under the same license as the squeekboard package.
# Piotr Drąg <piotrdrag@gmail.com>, 2022.
# Aviary.pl <community-poland@mozilla.org>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-03-19 09:34+0000\n"
"PO-Revision-Date: 2022-03-20 14:12+0100\n"
"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
"Language-Team: Polish <community-poland@mozilla.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Ustawienia klawiatury"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Klawiatura ekranowa"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Wirtualna klawiatura ekranowa"

46
po/pt.po Normal file
View File

@ -0,0 +1,46 @@
# Portuguese translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Hugo Carvalho <hugokarvalho@hotmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-02-26 10:49+0000\n"
"PO-Revision-Date: 2022-02-26 18:27+0000\n"
"Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
"Language-Team: Portuguese <pt@li.org>\n"
"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Definições do teclado"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Teclado no ecrã"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Um teclado virtual no ecrã"

47
po/pt_BR.po Normal file
View File

@ -0,0 +1,47 @@
# Brazilian Portuguese translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Rafael Fontenelle <rafaelff@gnome.org>, 2021.
# Luís Fernando Stürmer da Rosa <luisfsr@dismail.de>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-22 12:39+0000\n"
"PO-Revision-Date: 2022-01-30 12:34-0300\n"
"Last-Translator: Luís Fernando Stürmer da Rosa <luisfsr@dismail.de>\n"
"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.0\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Configurações do teclado"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Teclado virtual"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Um teclado virtual"

47
po/ro.po Normal file
View File

@ -0,0 +1,47 @@
# Romanian translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# libre <eposta1@pm.me>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-22 14:45+0000\n"
"PO-Revision-Date: 2021-12-22 20:05+0100\n"
"Last-Translator: libre <eposta1@pm.me>\n"
"Language-Team: Romanian <gnomero-list@lists.sourceforge.net>\n"
"Language: ro\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
"20)) ? 1 : 2);;\n"
"X-Generator: Gtranslator 3.30.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Opțiuni tastatură"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Tastatură pe ecran"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "O tastatură virtuală pe ecran"

49
po/sl.po Normal file
View File

@ -0,0 +1,49 @@
# Slovenian translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
#
# Matej Urbančič <mateju@src.gnome.org>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-22 19:14+0000\n"
"PO-Revision-Date: 2021-12-23 16:17+0100\n"
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
"Language: sl_SI\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n"
"%100==4 ? 3 : 0);\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Generator: Poedit 3.0.1\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Izrazne ikone"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Nastavitve tipkovnice"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Cvilkovnica"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Zaslonska tipkovnica"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Navidezna zaslonska tipkovnica"

45
po/sr.po Normal file
View File

@ -0,0 +1,45 @@
# Serbian translation for squeekboard.
# Copyright © 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Мирослав Николић <miroslavnikolic@rocketmail.com>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-03-08 10:15+0000\n"
"PO-Revision-Date: 2022-03-12 13:00+0200\n"
"Last-Translator: Мирослав Николић <miroslavnikolic@rocketmail.com>\n"
"Language-Team: Serbian <(nothing)>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : n"
"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Емоџи"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Терминал"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Поставке тастатуре"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Сквик-табла"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Тастатура на екрану"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Виртуелна тастатура на екрану"

48
po/sv.po Normal file
View File

@ -0,0 +1,48 @@
# Swedish translations for squeekboard package.
# Copyright (C) 2021 THE squeekboard'S COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Automatically generated, 2021.
#
# Luna Jernberg <droidbittin@gmail.com>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2021-12-22 12:47+0000\n"
"PO-Revision-Date: 2021-12-22 14:15+0100\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0\n"
"Last-Translator: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: sv\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Terminal"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Tangentbordsinställningar"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Skärmtangentbord"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Ett virtuellt skärmtangentbord"

46
po/tr.po Normal file
View File

@ -0,0 +1,46 @@
# Turkish translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Emin Tufan Çetin <etcetin@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/"
"issues\n"
"POT-Creation-Date: 2022-03-16 00:57+0000\n"
"PO-Revision-Date: 2022-03-19 12:33+0300\n"
"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
"Language-Team: Turkish <gnometurk@gnome.org>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 2.4.3\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr "Emoji"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr "Uçbirim"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr "Klavye Ayarları"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr "Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr "Ekran Klavyesi"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Sanal ekran klavyesi"

51
po/uk.po Normal file
View File

@ -0,0 +1,51 @@
# Ukrainian translation for squeekboard.
# Copyright (C) 2021 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
#
# Yuri Chornoivan <yurchor@ukr.net>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: squeekboard master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/Phosh/squeekboard/issues\n"
"POT-Creation-Date: 2021-12-22 10:36+0000\n"
"PO-Revision-Date: 2021-12-22 14:46+0200\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Lokalize 20.12.0\n"
#. translators: This is a emmoji keyboard layout
#: data/popover.ui:6
msgid "Emoji"
msgstr ""
"Емодзі"
#. translators: This is a terminal keyboard layout
#: data/popover.ui:12
msgid "Terminal"
msgstr ""
"Термінал"
#: data/popover.ui:18
msgid "Keyboard Settings"
msgstr ""
"Параметри клавіатури"
#: data/sm.puri.Squeekboard.desktop.in.in:3
msgid "Squeekboard"
msgstr ""
"Squeekboard"
#: data/sm.puri.Squeekboard.desktop.in.in:4
msgid "On Screen Keyboard"
msgstr ""
"Екранна клавіатура"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr ""
"Екранна віртуальна клавіатура"

View File

@ -6,12 +6,18 @@
use std::time::Duration;
use crate::outputs::OutputId;
use crate::panel::PixelSize;
/// The keyboard should hide after this has elapsed to prevent flickering.
pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200);
/// The outwardly visible state of visibility
#[derive(PartialEq, Debug, Clone)]
pub enum Outcome {
Visible,
Visible {
output: OutputId,
height: PixelSize,
},
Hidden,
}

358
src/assert_matches.rs Normal file
View File

@ -0,0 +1,358 @@
/* Taken from https://github.com/murarth/assert_matches
*
* git commit: 26b8b40a12823c068a829ba475d0eccc13dfc221
*
* assert_matches is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
*
Copyright (c) 2016 Murarth
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
//! Provides a macro, `assert_matches!`, which tests whether a value
//! matches a given pattern, causing a panic if the match fails.
//!
//! See the macro [`assert_matches!`] documentation for more information.
//!
//! Also provides a debug-only counterpart, [`debug_assert_matches!`].
//!
//! See the macro [`debug_assert_matches!`] documentation for more information
//! about this macro.
//!
//! [`assert_matches!`]: macro.assert_matches.html
//! [`debug_assert_matches!`]: macro.debug_assert_matches.html
#![deny(missing_docs)]
#![cfg_attr(not(test), no_std)]
/// Asserts that an expression matches a given pattern.
///
/// A guard expression may be supplied to add further restrictions to the
/// expected value of the expression.
///
/// A `match` arm may be supplied to perform additional assertions or to yield
/// a value from the macro invocation.
///
/// # Examples
///
/// ```
/// #[macro_use] extern crate assert_matches;
///
/// #[derive(Debug)]
/// enum Foo {
/// A(i32),
/// B(&'static str),
/// }
///
/// # fn main() {
/// let a = Foo::A(1);
///
/// // Assert that `a` matches the pattern `Foo::A(_)`.
/// assert_matches!(a, Foo::A(_));
///
/// // Assert that `a` matches the pattern and
/// // that the contained value meets the condition `i > 0`.
/// assert_matches!(a, Foo::A(i) if i > 0);
///
/// let b = Foo::B("foobar");
///
/// // Assert that `b` matches the pattern `Foo::B(_)`.
/// assert_matches!(b, Foo::B(s) => {
/// // Perform additional assertions on the variable binding `s`.
/// assert!(s.starts_with("foo"));
/// assert!(s.ends_with("bar"));
/// });
///
/// // Assert that `b` matches the pattern and yield the string `s`.
/// let s = assert_matches!(b, Foo::B(s) => s);
///
/// // Perform an assertion on the value `s`.
/// assert_eq!(s, "foobar");
/// # }
/// ```
#[macro_export]
macro_rules! assert_matches {
( $e:expr , $($pat:pat)|+ ) => {
match $e {
$($pat)|+ => (),
ref e => panic!("assertion failed: `{:?}` does not match `{}`",
e, stringify!($($pat)|+))
}
};
( $e:expr , $($pat:pat)|+ if $cond:expr ) => {
match $e {
$($pat)|+ if $cond => (),
ref e => panic!("assertion failed: `{:?}` does not match `{}`",
e, stringify!($($pat)|+ if $cond))
}
};
( $e:expr , $($pat:pat)|+ => $arm:expr ) => {
match $e {
$($pat)|+ => $arm,
ref e => panic!("assertion failed: `{:?}` does not match `{}`",
e, stringify!($($pat)|+))
}
};
( $e:expr , $($pat:pat)|+ if $cond:expr => $arm:expr ) => {
match $e {
$($pat)|+ if $cond => $arm,
ref e => panic!("assertion failed: `{:?}` does not match `{}`",
e, stringify!($($pat)|+ if $cond))
}
};
( $e:expr , $($pat:pat)|+ , $($arg:tt)* ) => {
match $e {
$($pat)|+ => (),
ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
e, stringify!($($pat)|+), format_args!($($arg)*))
}
};
( $e:expr , $($pat:pat)|+ if $cond:expr , $($arg:tt)* ) => {
match $e {
$($pat)|+ if $cond => (),
ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
e, stringify!($($pat)|+ if $cond), format_args!($($arg)*))
}
};
( $e:expr , $($pat:pat)|+ => $arm:expr , $($arg:tt)* ) => {
match $e {
$($pat)|+ => $arm,
ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
e, stringify!($($pat)|+), format_args!($($arg)*))
}
};
( $e:expr , $($pat:pat)|+ if $cond:expr => $arm:expr , $($arg:tt)* ) => {
match $e {
$($pat)|+ if $cond => $arm,
ref e => panic!("assertion failed: `{:?}` does not match `{}`: {}",
e, stringify!($($pat)|+ if $cond), format_args!($($arg)*))
}
};
}
/// Asserts that an expression matches a given pattern.
///
/// Unlike [`assert_matches!`], `debug_assert_matches!` statements are only enabled
/// in non-optimized builds by default. An optimized build will omit all
/// `debug_assert_matches!` statements unless `-C debug-assertions` is passed
/// to the compiler.
///
/// See the macro [`assert_matches!`] documentation for more information.
///
/// [`assert_matches!`]: macro.assert_matches.html
#[macro_export(local_inner_macros)]
macro_rules! debug_assert_matches {
( $($tt:tt)* ) => { {
if _assert_matches_cfg!(debug_assertions) {
assert_matches!($($tt)*);
}
} }
}
#[doc(hidden)]
#[macro_export]
macro_rules! _assert_matches_cfg {
( $($tt:tt)* ) => { cfg!($($tt)*) }
}
#[cfg(test)]
mod test {
use std::panic::{catch_unwind, UnwindSafe};
#[derive(Debug)]
enum Foo {
A(i32),
B(&'static str),
C(&'static str),
}
#[test]
fn test_assert_succeed() {
let a = Foo::A(123);
assert_matches!(a, Foo::A(_));
assert_matches!(a, Foo::A(123));
assert_matches!(a, Foo::A(i) if i == 123);
assert_matches!(a, Foo::A(42) | Foo::A(123));
let b = Foo::B("foo");
assert_matches!(b, Foo::B(_));
assert_matches!(b, Foo::B("foo"));
assert_matches!(b, Foo::B(s) if s == "foo");
assert_matches!(b, Foo::B(s) => assert_eq!(s, "foo"));
assert_matches!(b, Foo::B(s) => { assert_eq!(s, "foo"); assert!(true) });
assert_matches!(b, Foo::B(s) if s == "foo" => assert_eq!(s, "foo"));
assert_matches!(b, Foo::B(s) if s == "foo" => { assert_eq!(s, "foo"); assert!(true) });
let c = Foo::C("foo");
assert_matches!(c, Foo::B(_) | Foo::C(_));
assert_matches!(c, Foo::B("foo") | Foo::C("foo"));
assert_matches!(c, Foo::B(s) | Foo::C(s) if s == "foo");
assert_matches!(c, Foo::B(s) | Foo::C(s) => assert_eq!(s, "foo"));
assert_matches!(c, Foo::B(s) | Foo::C(s) => { assert_eq!(s, "foo"); assert!(true) });
assert_matches!(c, Foo::B(s) | Foo::C(s) if s == "foo" => assert_eq!(s, "foo"));
assert_matches!(c, Foo::B(s) | Foo::C(s) if s == "foo" => { assert_eq!(s, "foo"); assert!(true) });
}
#[test]
#[should_panic]
fn test_assert_panic_0() {
let a = Foo::A(123);
assert_matches!(a, Foo::B(_));
}
#[test]
#[should_panic]
fn test_assert_panic_1() {
let b = Foo::B("foo");
assert_matches!(b, Foo::B("bar"));
}
#[test]
#[should_panic]
fn test_assert_panic_2() {
let b = Foo::B("foo");
assert_matches!(b, Foo::B(s) if s == "bar");
}
#[test]
#[should_panic]
fn test_assert_panic_3() {
let b = Foo::B("foo");
assert_matches!(b, Foo::B(s) => assert_eq!(s, "bar"));
}
#[test]
#[should_panic]
fn test_assert_panic_4() {
let b = Foo::B("foo");
assert_matches!(b, Foo::B(s) if s == "bar" => assert_eq!(s, "foo"));
}
#[test]
#[should_panic]
fn test_assert_panic_5() {
let b = Foo::B("foo");
assert_matches!(b, Foo::B(s) if s == "foo" => assert_eq!(s, "bar"));
}
#[test]
#[should_panic]
fn test_assert_panic_6() {
let b = Foo::B("foo");
assert_matches!(b, Foo::B(s) if s == "foo" => { assert_eq!(s, "foo"); assert!(false) });
}
#[test]
fn test_assert_no_move() {
let b = &mut Foo::A(0);
assert_matches!(*b, Foo::A(0));
}
#[test]
fn assert_with_message() {
let a = Foo::A(0);
assert_matches!(a, Foo::A(_), "o noes");
assert_matches!(a, Foo::A(n) if n == 0, "o noes");
assert_matches!(a, Foo::A(n) => assert_eq!(n, 0), "o noes");
assert_matches!(a, Foo::A(n) => { assert_eq!(n, 0); assert!(n < 1) }, "o noes");
assert_matches!(a, Foo::A(n) if n == 0 => assert_eq!(n, 0), "o noes");
assert_matches!(a, Foo::A(n) if n == 0 => { assert_eq!(n, 0); assert!(n < 1) }, "o noes");
assert_matches!(a, Foo::A(_), "o noes {:?}", a);
assert_matches!(a, Foo::A(n) if n == 0, "o noes {:?}", a);
assert_matches!(a, Foo::A(n) => assert_eq!(n, 0), "o noes {:?}", a);
assert_matches!(a, Foo::A(n) => { assert_eq!(n, 0); assert!(n < 1) }, "o noes {:?}", a);
assert_matches!(a, Foo::A(_), "o noes {value:?}", value=a);
assert_matches!(a, Foo::A(n) if n == 0, "o noes {value:?}", value=a);
assert_matches!(a, Foo::A(n) => assert_eq!(n, 0), "o noes {value:?}", value=a);
assert_matches!(a, Foo::A(n) => { assert_eq!(n, 0); assert!(n < 1) }, "o noes {value:?}", value=a);
assert_matches!(a, Foo::A(n) if n == 0 => assert_eq!(n, 0), "o noes {value:?}", value=a);
}
fn panic_message<F>(f: F) -> String
where F: FnOnce() + UnwindSafe {
let err = catch_unwind(f)
.expect_err("function did not panic");
*err.downcast::<String>()
.expect("function panicked with non-String value")
}
#[test]
fn test_panic_message() {
let a = Foo::A(1);
// expr, pat
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(_));
}), r#"assertion failed: `A(1)` does not match `Foo::B(_)`"#);
// expr, pat if cond
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(s) if s == "foo");
}), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`"#);
// expr, pat => arm
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(_) => {});
}), r#"assertion failed: `A(1)` does not match `Foo::B(_)`"#);
// expr, pat if cond => arm
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(s) if s == "foo" => {});
}), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`"#);
// expr, pat, args
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(_), "msg");
}), r#"assertion failed: `A(1)` does not match `Foo::B(_)`: msg"#);
// expr, pat if cond, args
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(s) if s == "foo", "msg");
}), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`: msg"#);
// expr, pat => arm, args
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(_) => {}, "msg");
}), r#"assertion failed: `A(1)` does not match `Foo::B(_)`: msg"#);
// expr, pat if cond => arm, args
assert_eq!(panic_message(|| {
assert_matches!(a, Foo::B(s) if s == "foo" => {}, "msg");
}), r#"assertion failed: `A(1)` does not match `Foo::B(s) if s == "foo"`: msg"#);
}
}

71
src/debug.rs Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2022 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
use std::thread;
use zbus::{Connection, ObjectServer, dbus_interface, fdo};
use crate::event_loop;
use crate::state;
use std::convert::TryInto;
/// Accepts commands controlling the debug mode
struct Manager {
sender: event_loop::driver::Threaded,
enabled: bool,
}
#[dbus_interface(name = "sm.puri.SqueekDebug")]
impl Manager {
#[dbus_interface(property, name = "Enabled")]
fn get_enabled(&self) -> bool {
self.enabled
}
#[dbus_interface(property, name = "Enabled")]
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
self.sender
.send(state::Event::Debug(
if enabled { Event::Enable }
else { Event::Disable }
))
.unwrap();
}
}
fn start(mgr: Manager) -> Result<(), Box<dyn std::error::Error>> {
let connection = Connection::new_session()?;
fdo::DBusProxy::new(&connection)?.request_name(
"sm.puri.SqueekDebug",
fdo::RequestNameFlags::ReplaceExisting.into(),
)?;
let mut object_server = ObjectServer::new(&connection);
object_server.at(&"/sm/puri/SqueekDebug".try_into()?, mgr)?;
loop {
if let Err(err) = object_server.try_handle_next() {
eprintln!("{}", err);
}
}
}
pub fn init(sender: event_loop::driver::Threaded) {
let mgr = Manager {
sender,
enabled: false,
};
thread::spawn(move || {
start(mgr).unwrap();
});
}
#[derive(Debug, Clone, Copy)]
pub enum Event {
Enable,
Disable,
}

View File

@ -10,7 +10,7 @@ use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
use ::submission::c::Submission as CSubmission;
use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt;
use gtk::prelude::WidgetExt;
use std::collections::HashSet;
use std::ffi::CStr;

View File

@ -29,7 +29,9 @@ use std::time::Instant;
use crate::logging::Warn;
/// Type of the sender that waits for external events
type Sender = mpsc::Sender<Event>;
/// Type of the sender that waits for internal state changes
type UISender = glib::Sender<Commands>;
/// This loop driver spawns a new thread which updates the state in a loop,

View File

@ -151,8 +151,9 @@ mod test {
use super::*;
use crate::animation;
use crate::imservice::{ ContentHint, ContentPurpose };
use crate::main::PanelCommand;
use crate::panel;
use crate::state::{ Application, InputMethod, InputMethodDetails, Presence, visibility };
use crate::state::test::application_with_fake_output;
fn imdetails_new() -> InputMethodDetails {
InputMethodDetails {
@ -170,17 +171,18 @@ mod test {
im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
};
let l = State::new(state, now);
let (l, commands) = handle_event(l, InputMethod::InactiveSince(now).into(), now);
assert_eq!(commands.panel_visibility, Some(PanelCommand::Show));
assert_matches!(commands.panel_visibility, Some(panel::Command::Show{..}));
assert_eq!(l.scheduled_wakeup, Some(now + animation::HIDING_TIMEOUT));
now += animation::HIDING_TIMEOUT;
let (l, commands) = handle_event(l, Event::TimeoutReached(now), now);
assert_eq!(commands.panel_visibility, Some(PanelCommand::Hide));
assert_eq!(commands.panel_visibility, Some(panel::Command::Hide));
assert_eq!(l.scheduled_wakeup, None);
}
}

View File

@ -1,6 +1,7 @@
#include "submission.h"
#include "input-method-unstable-v2-client-protocol.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include <glib.h>
#include "submission.h"
struct imservice;

View File

@ -26,21 +26,32 @@ pub mod c {
use super::*;
use std::os::raw::{c_char, c_void};
use std::ptr;
// The following defined in C
/// struct zwp_input_method_v2*
#[repr(transparent)]
#[derive(PartialEq, Clone, Copy)]
pub struct InputMethod(*const c_void);
impl InputMethod {
pub fn is_null(&self) -> bool {
self.0.is_null()
}
pub fn null() -> Self {
Self(ptr::null())
}
}
extern "C" {
fn imservice_destroy_im(im: *mut c::InputMethod);
fn imservice_destroy_im(im: InputMethod);
#[allow(improper_ctypes)] // IMService will never be dereferenced in C
pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
pub fn eek_input_method_delete_surrounding_text(im: *mut InputMethod, before: u32, after: u32);
pub fn eek_input_method_commit(im: *mut InputMethod, serial: u32);
pub fn imservice_connect_listeners(im: InputMethod, imservice: *const IMService);
pub fn eek_input_method_commit_string(im: InputMethod, text: *const c_char);
pub fn eek_input_method_delete_surrounding_text(im: InputMethod, before: u32, after: u32);
pub fn eek_input_method_commit(im: InputMethod, serial: u32);
}
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
@ -49,7 +60,7 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn imservice_handle_input_method_activate(imservice: *mut IMService,
im: *const InputMethod)
im: InputMethod)
{
let imservice = check_imservice(imservice, im).unwrap();
imservice.preedit_string = String::new();
@ -62,7 +73,7 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn imservice_handle_input_method_deactivate(imservice: *mut IMService,
im: *const InputMethod)
im: InputMethod)
{
let imservice = check_imservice(imservice, im).unwrap();
imservice.pending = IMProtocolState {
@ -74,7 +85,7 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn imservice_handle_surrounding_text(imservice: *mut IMService,
im: *const InputMethod,
im: InputMethod,
text: *const c_char, cursor: u32, _anchor: u32)
{
let imservice = check_imservice(imservice, im).unwrap();
@ -90,7 +101,7 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn imservice_handle_content_type(imservice: *mut IMService,
im: *const InputMethod,
im: InputMethod,
hint: u32, purpose: u32)
{
let imservice = check_imservice(imservice, im).unwrap();
@ -118,7 +129,7 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn imservice_handle_text_change_cause(imservice: *mut IMService,
im: *const InputMethod,
im: InputMethod,
cause: u32)
{
let imservice = check_imservice(imservice, im).unwrap();
@ -138,16 +149,11 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn imservice_handle_done(imservice: *mut IMService,
im: *const InputMethod)
im: InputMethod)
{
let imservice = check_imservice(imservice, im).unwrap();
imservice.current = imservice.pending.clone();
imservice.pending = IMProtocolState {
active: imservice.current.active,
..IMProtocolState::default()
};
imservice.serial += Wrapping(1u32);
imservice.send_event();
}
@ -156,7 +162,7 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn imservice_handle_unavailable(imservice: *mut IMService,
im: *mut InputMethod)
im: InputMethod)
{
let imservice = check_imservice(imservice, im).unwrap();
unsafe { imservice_destroy_im(im); }
@ -181,7 +187,7 @@ pub mod c {
/// Care must be take
/// not to exceed the lifetime of the pointer with the reference,
/// especially not to store it.
fn check_imservice(imservice: *mut IMService, im: *const InputMethod)
fn check_imservice(imservice: *mut IMService, im: InputMethod)
-> Result<&'static mut IMService, &'static str>
{
if imservice.is_null() {
@ -315,7 +321,7 @@ impl Default for IMProtocolState {
pub struct IMService {
/// Owned reference (still created and destroyed in C)
pub im: *mut c::InputMethod,
pub im: c::InputMethod,
sender: driver::Threaded,
pending: IMProtocolState,
@ -331,7 +337,7 @@ pub enum SubmitError {
impl IMService {
pub fn new(
im: *mut c::InputMethod,
im: c::InputMethod,
sender: driver::Threaded,
) -> Box<IMService> {
// IMService will be referenced to by C,

View File

@ -18,6 +18,7 @@
*/
use std::cell::RefCell;
use std::cmp;
use std::collections::{ HashMap, HashSet };
use std::ffi::CString;
use std::fmt;
@ -26,6 +27,7 @@ use std::vec::Vec;
use ::action::Action;
use ::drawing;
use ::float_ord::FloatOrd;
use ::keyboard::KeyState;
use ::logging;
use ::manager;
@ -117,28 +119,30 @@ pub mod c {
pub struct Transformation {
pub origin_x: f64,
pub origin_y: f64,
pub scale: f64,
pub scale_x: f64,
pub scale_y: f64,
}
impl Transformation {
/// Applies the new transformation after this one
pub fn chain(self, next: Transformation) -> Transformation {
Transformation {
origin_x: self.origin_x + self.scale * next.origin_x,
origin_y: self.origin_y + self.scale * next.origin_y,
scale: self.scale * next.scale,
origin_x: self.origin_x + self.scale_x * next.origin_x,
origin_y: self.origin_y + self.scale_y * next.origin_y,
scale_x: self.scale_x * next.scale_x,
scale_y: self.scale_y * next.scale_y,
}
}
fn forward(&self, p: Point) -> Point {
Point {
x: (p.x - self.origin_x) / self.scale,
y: (p.y - self.origin_y) / self.scale,
x: (p.x - self.origin_x) / self.scale_x,
y: (p.y - self.origin_y) / self.scale_y,
}
}
fn reverse(&self, p: Point) -> Point {
Point {
x: p.x * self.scale + self.origin_x,
y: p.y * self.scale + self.origin_y,
x: p.x * self.scale_x + self.origin_x,
y: p.y * self.scale_y + self.origin_y,
}
}
pub fn reverse_bounds(&self, b: Bounds) -> Bounds {
@ -394,7 +398,8 @@ pub mod c {
let transform = Transformation {
origin_x: 10f64,
origin_y: 11f64,
scale: 12f64,
scale_x: 12f64,
scale_y: 13f64,
};
let point = Point { x: 1f64, y: 1f64 };
let transformed = transform.reverse(transform.forward(point.clone()));
@ -755,16 +760,20 @@ impl Layout {
let size = self.calculate_size();
let h_scale = available.width / size.width;
let v_scale = available.height / size.height;
let scale = if h_scale < v_scale { h_scale } else { v_scale };
// Allow up to 5% (and a bit more) horizontal stretching for filling up available space
let scale_x = if (h_scale / v_scale) < 1.055 { h_scale } else { v_scale };
let scale_y = cmp::min(FloatOrd(h_scale), FloatOrd(v_scale)).0;
let outside_margins = c::Transformation {
origin_x: (available.width - (scale * size.width)) / 2.0,
origin_y: (available.height - (scale * size.height)) / 2.0,
scale: scale,
origin_x: (available.width - (scale_x * size.width)) / 2.0,
origin_y: (available.height - (scale_y * size.height)) / 2.0,
scale_x: scale_x,
scale_y: scale_y,
};
outside_margins.chain(c::Transformation {
origin_x: self.margins.left,
origin_y: self.margins.top,
scale: 1.0,
scale_x: 1.0,
scale_y: 1.0,
})
}
@ -1471,8 +1480,63 @@ mod test {
let transformation = layout.calculate_transformation(
Size { width: 2.0, height: 2.0 }
);
assert_eq!(transformation.scale, 1.0);
assert_eq!(transformation.scale_x, 1.0);
assert_eq!(transformation.scale_y, 1.0);
assert_eq!(transformation.origin_x, 0.5);
assert_eq!(transformation.origin_y, 0.0);
}
#[test]
fn check_stretching() {
// just one button
let view = View::new(vec![
(
0.0,
Row::new(vec![(
0.0,
Box::new(Button {
size: Size { width: 1.0, height: 1.0 },
..*make_button_with_state("foo".into(), make_state())
}),
)]),
),
]);
let layout = Layout {
current_view: String::new(),
view_latched: LatchedState::Not,
keymaps: Vec::new(),
kind: ArrangementKind::Base,
pressed_keys: HashSet::new(),
margins: Margins {
top: 0.0,
left: 0.0,
right: 0.0,
bottom: 0.0,
},
views: hashmap! {
String::new() => (c::Point { x: 0.0, y: 0.0 }, view),
},
purpose: ContentPurpose::Normal,
};
let transformation = layout.calculate_transformation(
Size { width: 100.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 100.0);
assert_eq!(transformation.scale_y, 100.0);
let transformation = layout.calculate_transformation(
Size { width: 95.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 95.0);
assert_eq!(transformation.scale_y, 95.0);
let transformation = layout.calculate_transformation(
Size { width: 105.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 105.0);
assert_eq!(transformation.scale_y, 100.0);
let transformation = layout.calculate_transformation(
Size { width: 106.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 100.0);
assert_eq!(transformation.scale_y, 100.0);
}
}

View File

@ -11,16 +11,21 @@ extern crate gtk_sys;
#[allow(unused_imports)]
#[macro_use] // only for tests
extern crate maplit;
extern crate regex;
extern crate serde;
extern crate xkbcommon;
extern crate zbus;
extern crate zvariant;
#[cfg(test)]
#[macro_use]
mod assert_matches;
#[macro_use]
mod logging;
mod action;
mod animation;
pub mod data;
mod debug;
mod drawing;
mod event_loop;
pub mod float_ord;
@ -31,6 +36,7 @@ mod locale;
mod main;
mod manager;
mod outputs;
mod panel;
mod popover;
mod resources;
mod state;
@ -38,6 +44,5 @@ mod style;
mod submission;
pub mod tests;
pub mod util;
mod ui_manager;
mod vkeyboard;
mod xdg;

View File

@ -8,6 +8,7 @@
#include "eek/eek-types.h"
#include "dbus.h"
#include "panel.h"
struct receiver;
@ -21,11 +22,12 @@ struct rsobjects {
struct receiver *receiver;
struct squeek_state_manager *state_manager;
struct submission *submission;
struct squeek_wayland *wayland;
};
void register_ui_loop_handler(struct receiver *receiver, ServerContextService *ui, DBusHandler *dbus_handler);
void register_ui_loop_handler(struct receiver *receiver, struct panel_manager *panel, EekboardContextService *hint_manager, DBusHandler *dbus_handler);
struct rsobjects squeek_rsobjects_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk);
struct rsobjects squeek_init(void);
void squeek_state_send_force_visible(struct squeek_state_manager *state);
void squeek_state_send_force_hidden(struct squeek_state_manager *state);

View File

@ -1,9 +1,10 @@
/* Copyright (C) 2020 Purism SPC
/* Copyright (C) 2020,2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Glue for the main loop. */
use crate::panel;
use crate::debug;
use crate::state;
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
@ -11,37 +12,73 @@ use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
mod c {
use super::*;
use std::os::raw::c_void;
use std::ptr;
use std::rc::Rc;
use std::time::Instant;
use crate::event_loop::driver;
use crate::imservice::IMService;
use crate::imservice::c::InputMethod;
use crate::outputs::Outputs;
use crate::state;
use crate::submission::Submission;
use crate::util::c::Wrapped;
use crate::vkeyboard::c::ZwpVirtualKeyboardV1;
/// ServerContextService*
#[repr(transparent)]
pub struct UIManager(*const c_void);
/// DbusHandler*
#[repr(transparent)]
pub struct DBusHandler(*const c_void);
/// EekboardContextService* in the role of a hint receiver
// The clone/copy is a concession to C style of programming.
// It would be hard to get rid of it.
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct HintManager(*const c_void);
/// Holds the Rust structures that are interesting from C.
#[repr(C)]
pub struct RsObjects {
/// The handle to which Commands should be sent
/// for processing in the main loop.
receiver: Wrapped<Receiver<Commands>>,
state_manager: Wrapped<driver::Threaded>,
submission: Wrapped<Submission>,
/// Not wrapped, because C needs to access this.
wayland: *mut Wayland,
}
/// Corresponds to wayland.h::squeek_wayland.
/// Fields unused by Rust are marked as generic data types.
#[repr(C)]
pub struct Wayland {
layer_shell: *const c_void,
virtual_keyboard_manager: *const c_void,
input_method_manager: *const c_void,
outputs: Wrapped<Outputs>,
seat: *const c_void,
input_method: InputMethod,
virtual_keyboard: ZwpVirtualKeyboardV1,
}
impl Wayland {
fn new(outputs_manager: Outputs) -> Self {
Wayland {
layer_shell: ptr::null(),
virtual_keyboard_manager: ptr::null(),
input_method_manager: ptr::null(),
outputs: Wrapped::new(outputs_manager),
seat: ptr::null(),
input_method: InputMethod::null(),
virtual_keyboard: ZwpVirtualKeyboardV1::null(),
}
}
}
extern "C" {
fn server_context_service_real_show_keyboard(service: *const UIManager);
fn server_context_service_real_hide_keyboard(service: *const UIManager);
fn server_context_service_set_hint_purpose(service: *const UIManager, hint: u32, purpose: u32);
#[allow(improper_ctypes)]
fn init_wayland(wayland: *mut Wayland);
fn eekboard_context_service_set_hint_purpose(service: HintManager, hint: u32, purpose: u32);
// This should probably only get called from the gtk main loop,
// given that dbus handler is using glib.
fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8);
@ -52,19 +89,25 @@ mod c {
/// and that leads to suffering.
#[no_mangle]
pub extern "C"
fn squeek_rsobjects_new(
im: *mut InputMethod,
vk: ZwpVirtualKeyboardV1,
) -> RsObjects {
fn squeek_init() -> RsObjects {
// Set up channels
let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT);
let now = Instant::now();
let state_manager = driver::Threaded::new(sender, state::Application::new(now));
let imservice = if im.is_null() {
debug::init(state_manager.clone());
let outputs = Outputs::new(state_manager.clone());
let mut wayland = Box::new(Wayland::new(outputs));
let wayland_raw = &mut *wayland as *mut _;
unsafe { init_wayland(wayland_raw); }
let vk = wayland.virtual_keyboard;
let imservice = if wayland.input_method.is_null() {
None
} else {
Some(IMService::new(im, state_manager.clone()))
Some(IMService::new(wayland.input_method, state_manager.clone()))
};
let submission = Submission::new(vk, imservice);
@ -72,6 +115,7 @@ mod c {
submission: Wrapped::new(submission),
state_manager: Wrapped::new(state_manager),
receiver: Wrapped::new(receiver),
wayland: Box::into_raw(wayland),
}
}
@ -80,21 +124,24 @@ mod c {
pub extern "C"
fn register_ui_loop_handler(
receiver: Wrapped<Receiver<Commands>>,
ui_manager: *const UIManager,
panel_manager: panel::c::PanelManager,
hint_manager: HintManager,
dbus_handler: *const DBusHandler,
) {
let receiver = unsafe { receiver.unwrap() };
let receiver = Rc::try_unwrap(receiver).expect("References still present");
let receiver = receiver.into_inner();
let panel_manager = Wrapped::new(panel::Manager::new(panel_manager));
let ctx = MainContext::default();
ctx.acquire();
let _acqu = ctx.acquire();
receiver.attach(
Some(&ctx),
move |msg| {
main_loop_handle_message(msg, ui_manager, dbus_handler);
main_loop_handle_message(msg, panel_manager.clone(), hint_manager, dbus_handler);
Continue(true)
},
);
#[cfg(not(feature = "glib_v0_14"))]
ctx.release();
}
@ -104,27 +151,24 @@ mod c {
/// and doesn't lend itself to testing other than integration.
fn main_loop_handle_message(
msg: Commands,
ui_manager: *const UIManager,
panel_manager: Wrapped<panel::Manager>,
hint_manager: HintManager,
dbus_handler: *const DBusHandler,
) {
match msg.panel_visibility {
Some(PanelCommand::Show) => unsafe {
server_context_service_real_show_keyboard(ui_manager);
},
Some(PanelCommand::Hide) => unsafe {
server_context_service_real_hide_keyboard(ui_manager);
},
None => {},
};
if let Some(visibility) = msg.panel_visibility {
panel::Manager::update(panel_manager, visibility);
}
if let Some(visible) = msg.dbus_visible_set {
unsafe { dbus_handler_set_visible(dbus_handler, visible as u8) };
if dbus_handler != std::ptr::null() {
unsafe { dbus_handler_set_visible(dbus_handler, visible as u8) };
}
}
if let Some(hints) = msg.layout_hint_set {
unsafe {
server_context_service_set_hint_purpose(
ui_manager,
eekboard_context_service_set_hint_purpose(
hint_manager,
hints.hint.bits(),
hints.purpose.clone() as u32,
)
@ -133,17 +177,11 @@ mod c {
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum PanelCommand {
Show,
Hide,
}
/// The commands consumed by the main loop,
/// to be sent out to external components.
#[derive(Clone)]
pub struct Commands {
pub panel_visibility: Option<PanelCommand>,
pub panel_visibility: Option<panel::Command>,
pub layout_hint_set: Option<state::InputMethodDetails>,
pub dbus_visible_set: Option<bool>,
}

View File

@ -14,6 +14,7 @@ sources = [
config_h,
'dbus.c',
'imservice.c',
'panel.c',
'popover.c',
'server-context-service.c',
'wayland.c',

View File

@ -11,7 +11,8 @@ struct squeek_output_handle {
struct squeek_outputs *squeek_outputs_new(void);
void squeek_outputs_free(struct squeek_outputs*);
void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output);
void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output, uint32_t id);
struct wl_output *squeek_outputs_try_unregister(struct squeek_outputs*, uint32_t id);
struct squeek_output_handle squeek_outputs_get_current(struct squeek_outputs*);
int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output);
#endif

View File

@ -1,7 +1,14 @@
/* Copyright (C) 2019-2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Managing Wayland outputs */
use std::ops;
use std::vec::Vec;
use crate::event_loop;
use ::logging;
use crate::util::DivCeil;
// traits
use ::logging::Warn;
@ -11,15 +18,22 @@ pub mod c {
use super::*;
use std::os::raw::{ c_char, c_void };
use std::ptr;
use ::util::c::COpaquePtr;
use ::util::c::{COpaquePtr, Wrapped};
// Defined in C
#[repr(transparent)]
#[derive(Clone, PartialEq, Copy)]
#[derive(Clone, PartialEq, Copy, Debug, Eq, Hash)]
pub struct WlOutput(*const c_void);
impl WlOutput {
fn null() -> Self {
Self(ptr::null())
}
}
#[repr(C)]
struct WlOutputListener<T: COpaquePtr> {
geometry: extern fn(
@ -63,7 +77,7 @@ pub mod c {
}
/// Map to `wl_output.transform` values
#[derive(Clone)]
#[derive(Clone, Copy, Debug)]
pub enum Transform {
Normal = 0,
Rotated90 = 1,
@ -103,33 +117,18 @@ pub mod c {
) -> i32;
}
type COutputs = ::util::c::Wrapped<Outputs>;
/// A stable reference to an output.
#[derive(Clone)]
#[repr(C)]
pub struct OutputHandle {
wl_output: WlOutput,
outputs: COutputs,
}
impl OutputHandle {
// Cannot return an Output reference
// because COutputs is too deeply wrapped
pub fn get_state(&self) -> Option<OutputState> {
let outputs = self.outputs.clone_ref();
let outputs = outputs.borrow();
find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone())
}
}
/// Wrapping Outputs is required for calling its methods from C
type COutputs = Wrapped<Outputs>;
// Defined in Rust
// Callbacks from the output listener follow
extern fn outputs_handle_geometry(
outputs: COutputs,
wl_output: WlOutput,
_x: i32, _y: i32,
_phys_width: i32, _phys_height: i32,
phys_width: i32, phys_height: i32,
_subpixel: i32,
_make: *const c_char, _model: *const c_char,
transform: i32,
@ -143,10 +142,23 @@ pub mod c {
let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut();
let output_state: Option<&mut OutputState>
= find_output_mut(&mut collection, wl_output)
= collection
.find_output_mut(wl_output)
.map(|o| &mut o.pending);
match output_state {
Some(state) => { state.transform = Some(transform) },
Some(state) => {
fn maybe_mm(value: i32) -> Option<Millimeter> {
if value == 0 { None }
else { Some(Millimeter(value)) }
}
state.geometry = Some(Geometry {
phys_size: Size {
width: maybe_mm(phys_width),
height: maybe_mm(phys_height),
},
transform,
});
},
None => log_print!(
logging::Level::Warning,
"Got geometry on unknown output",
@ -171,7 +183,8 @@ pub mod c {
let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut();
let output_state: Option<&mut OutputState>
= find_output_mut(&mut collection, wl_output)
= collection
.find_output_mut(wl_output)
.map(|o| &mut o.pending);
match output_state {
Some(state) => {
@ -192,14 +205,27 @@ pub mod c {
) {
let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut();
let output = find_output_mut(&mut collection, wl_output);
match output {
Some(output) => { output.current = output.pending.clone(); }
None => log_print!(
logging::Level::Warning,
"Got done on unknown output",
),
let output = collection
.find_output_mut(wl_output);
let event = match output {
Some(output) => {
output.current = output.pending.clone();
Some(Event {
output: OutputId(wl_output),
change: ChangeType::Altered(output.current),
})
},
None => {
log_print!(
logging::Level::Warning,
"Got done on unknown output",
);
None
}
};
if let Some(event) = event {
collection.send_event(event);
}
}
extern fn outputs_handle_scale(
@ -210,7 +236,8 @@ pub mod c {
let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut();
let output_state: Option<&mut OutputState>
= find_output_mut(&mut collection, wl_output)
= collection
.find_output_mut(wl_output)
.map(|o| &mut o.pending);
match output_state {
Some(state) => { state.scale = factor; }
@ -221,11 +248,7 @@ pub mod c {
};
}
#[no_mangle]
pub extern "C"
fn squeek_outputs_new() -> COutputs {
COutputs::new(Outputs { outputs: Vec::new() })
}
// End callbacks
#[no_mangle]
pub extern "C"
@ -235,14 +258,17 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn squeek_outputs_register(raw_collection: COutputs, output: WlOutput) {
fn squeek_outputs_register(raw_collection: COutputs, output: WlOutput, id: u32) {
let collection = raw_collection.clone_ref();
let mut collection = collection.borrow_mut();
collection.outputs.push(Output {
output: output.clone(),
pending: OutputState::uninitialized(),
current: OutputState::uninitialized(),
});
collection.outputs.push((
Output {
output: output.clone(),
pending: OutputState::uninitialized(),
current: OutputState::uninitialized(),
},
id,
));
unsafe { squeek_output_add_listener(
output,
@ -255,61 +281,70 @@ pub mod c {
raw_collection,
)};
}
/// This will try to unregister the output, if the id matches a registered one.
#[no_mangle]
pub extern "C"
fn squeek_outputs_get_current(raw_collection: COutputs) -> OutputHandle {
fn squeek_outputs_try_unregister(raw_collection: COutputs, id: u32) -> WlOutput {
let collection = raw_collection.clone_ref();
let collection = collection.borrow();
OutputHandle {
wl_output: collection.outputs[0].output.clone(),
outputs: raw_collection.clone(),
}
let mut collection = collection.borrow_mut();
collection.remove_output_by_global(id)
.map_err(|e| log_print!(
logging::Level::Debug,
"Tried to remove global {:x} but it is not registered as an output: {:?}",
id, e,
))
.unwrap_or(WlOutput::null())
}
// TODO: handle unregistration
fn find_output(
collection: &Outputs,
wl_output: WlOutput,
) -> Option<&Output> {
collection.outputs
.iter()
.find_map(|o|
if o.output == wl_output { Some(o) } else { None }
)
}
fn find_output_mut(
collection: &mut Outputs,
wl_output: WlOutput,
) -> Option<&mut Output> {
collection.outputs
.iter_mut()
.find_map(|o|
if o.output == wl_output { Some(o) } else { None }
)
}
}
/// Generic size
#[derive(Clone)]
pub struct Size {
pub width: u32,
pub height: u32,
#[derive(Clone, Copy, Debug)]
pub struct Size<Unit> {
pub width: Unit,
pub height: Unit,
}
pub type PixelSize = Size<u32>;
/// wl_output mode
#[derive(Clone)]
struct Mode {
width: i32,
height: i32,
#[derive(Clone, Copy, Debug)]
pub struct Mode {
pub width: i32,
pub height: i32,
}
#[derive(Clone)]
#[derive(Clone, Copy, Debug)]
pub struct Millimeter(pub i32);
impl DivCeil<i32> for Millimeter {
type Output = Millimeter;
fn div_ceil(self, other: i32) -> Self {
Self(self.0.div_ceil(other))
}
}
impl ops::Mul<i32> for Millimeter {
type Output = Self;
fn mul(self, m: i32) -> Self {
Self(self.0 * m as i32)
}
}
/// All geometry parameters
#[derive(Clone, Copy, Debug)]
pub struct Geometry {
pub transform: c::Transform,
pub phys_size: Size<Option<Millimeter>>,
}
#[derive(Clone, Copy, Debug)]
pub struct OutputState {
current_mode: Option<Mode>,
transform: Option<c::Transform>,
pub current_mode: Option<Mode>,
pub geometry: Option<Geometry>,
pub scale: i32,
}
@ -323,44 +358,138 @@ impl OutputState {
fn uninitialized() -> OutputState {
OutputState {
current_mode: None,
transform: None,
geometry: None,
scale: 1,
}
}
pub fn get_pixel_size(&self) -> Option<Size> {
fn transform_size<T>(
width: T,
height: T,
transform: self::c::Transform,
) -> Size<T> {
use self::c::Transform;
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => Size {
width,
height,
},
_ => Size {
width: height,
height: width,
},
}
}
/// Return resolution adjusted for current transform
pub fn get_pixel_size(&self) -> Option<PixelSize> {
match self {
OutputState {
current_mode: Some(Mode { width, height } ),
transform: Some(transform),
geometry: Some(Geometry { transform, .. } ),
scale: _,
} => Some(
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => Size {
width: *width as u32,
height: *height as u32,
},
_ => Size {
width: *height as u32,
height: *width as u32,
},
}
),
} => Some(Self::transform_size(*width as u32, *height as u32, *transform)),
OutputState {
current_mode: Some(Mode { width, height } ),
..
} => Some(PixelSize { width: *width as u32, height: *height as u32 } ),
_ => None,
}
}
/// Return physical dimensions adjusted for current transform
pub fn get_physical_size(&self) -> Option<Size<Option<Millimeter>>> {
match self {
OutputState {
geometry: Some(Geometry { transform, phys_size } ),
..
} => Some(Self::transform_size(phys_size.width, phys_size.height, *transform)),
_ => None,
}
}
}
pub struct Output {
/// Not guaranteed to exist,
/// but can be used to look up state.
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)]
pub struct OutputId(pub c::WlOutput);
// WlOutput is a pointer,
// but in the public interface,
// we're only using it as a lookup key.
unsafe impl Send for OutputId {}
struct Output {
output: c::WlOutput,
pending: OutputState,
current: OutputState,
}
#[derive(Debug)]
struct NotFound;
/// Wayland global ID type
type GlobalId = u32;
/// The outputs manager
pub struct Outputs {
outputs: Vec<Output>,
outputs: Vec<(Output, GlobalId)>,
sender: event_loop::driver::Threaded,
}
impl Outputs {
pub fn new(sender: event_loop::driver::Threaded) -> Outputs {
Outputs {
outputs: Vec::new(),
sender,
}
}
fn send_event(&self, event: Event) {
self.sender.send(event.into()).unwrap()
}
fn remove_output_by_global(&mut self, id: GlobalId)
-> Result<c::WlOutput, NotFound>
{
let index = self.outputs.iter()
.position(|(_o, global_id)| *global_id == id);
if let Some(index) = index {
let (output, _id) = self.outputs.remove(index);
self.send_event(Event {
change: ChangeType::Removed,
output: OutputId(output.output),
});
Ok(output.output)
} else {
Err(NotFound)
}
}
fn find_output_mut(&mut self, wl_output: c::WlOutput)
-> Option<&mut Output>
{
self.outputs
.iter_mut()
.find_map(|(o, _global)|
if o.output == wl_output { Some(o) } else { None }
)
}
}
#[derive(Clone, Copy, Debug)]
pub enum ChangeType {
/// Added or changed
Altered(OutputState),
Removed,
}
#[derive(Clone, Copy, Debug)]
pub struct Event {
pub output: OutputId,
pub change: ChangeType,
}

130
src/panel.c Normal file
View File

@ -0,0 +1,130 @@
#include "eekboard/eekboard-context-service.h"
#include "wayland.h"
#include "panel.h"
// Called from rust
/// Destroys the widget
void
panel_manager_hide(struct panel_manager *self)
{
if (self->window) {
gtk_widget_destroy (GTK_WIDGET (self->window));
}
if (self->widget) {
gtk_widget_destroy (GTK_WIDGET (self->widget));
}
self->window = NULL;
self->widget = NULL;
}
static void
on_destroy (struct panel_manager *self, GtkWidget *widget)
{
g_assert (widget == GTK_WIDGET(self->window));
panel_manager_hide(self);
}
/// panel::Manager. Only needed for this callback
struct squeek_panel_manager;
/// Calls back into Rust
void squeek_panel_manager_configured(struct squeek_panel_manager *mgr, uint32_t width, uint32_t height);
static void
on_surface_configure(struct squeek_panel_manager *self, PhoshLayerSurface *surface)
{
gint width;
gint height;
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (surface));
g_object_get(G_OBJECT(surface),
"configured-width", &width,
"configured-height", &height,
NULL);
squeek_panel_manager_configured(self, width, height);
}
static void
make_widget (struct panel_manager *self)
{
if (self->widget) {
g_error("Widget already present");
}
self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->layout);
gtk_widget_set_has_tooltip (self->widget, TRUE);
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
gtk_widget_show_all(self->widget);
}
// Called from rust
/// Creates a new panel widget
void
panel_manager_request_widget (struct panel_manager *self, struct wl_output *output, uint32_t height, struct squeek_panel_manager *mgr)
{
if (self->window) {
g_error("Window already present");
}
self->window = g_object_new (
PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell,
"wl-output", output,
"height", height,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
"layer", ZWLR_LAYER_SHELL_V1_LAYER_TOP,
"kbd-interactivity", FALSE,
"exclusive-zone", height,
"namespace", "osk",
NULL
);
g_object_connect (self->window,
"swapped-signal::destroy", G_CALLBACK(on_destroy), self,
"swapped-signal::configured", G_CALLBACK(on_surface_configure), mgr,
NULL);
// The properties below are just to make hacking easier.
// The way we use layer-shell overrides some,
// and there's no space in the protocol for others.
// Those may still be useful in the future,
// or for hacks with regular windows.
gtk_widget_set_can_focus (GTK_WIDGET(self->window), FALSE);
g_object_set (G_OBJECT(self->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(self->window), "Squeekboard");
gtk_window_set_icon_name (GTK_WINDOW(self->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(self->window), TRUE);
make_widget(self);
gtk_widget_show (GTK_WIDGET(self->window));
}
// Called from rust
/// Updates the size
void
panel_manager_resize (struct panel_manager *self, uint32_t height)
{
phosh_layer_surface_set_size(self->window, 0, height);
phosh_layer_surface_set_exclusive_zone(self->window, height);
phosh_layer_surface_wl_surface_commit(self->window);
}
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout)
{
struct panel_manager mgr = {
.state = state,
.submission = submission,
.layout = layout,
.window = NULL,
.widget = NULL,
.current_output = NULL,
};
return mgr;
}

21
src/panel.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "eek/layersurface.h"
#include "src/layout.h"
#include "src/submission.h"
// Stores the objects that the panel and its widget will refer to
struct panel_manager {
EekboardContextService *state; // unowned
/// Needed for instantiating the widget
struct submission *submission; // unowned
struct squeek_layout_state *layout;
PhoshLayerSurface *window;
GtkWidget *widget; // nullable
// Those should be held in Rust
struct wl_output *current_output;
};
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout);

248
src/panel.rs Normal file
View File

@ -0,0 +1,248 @@
/* Copyright (C) 2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Panel state management.
*
* This is effectively a mirror of the previous C code,
* with an explicit state machine managing the panel size.
*
* It still relies on a callback from Wayland to accept the panel size,
* which makes this code somewhat prone to mistakes.
*
* An alternative to the callback would be
* to send a message all the way to `state::State`
* every time the allocated size changes.
* That would allow for a more holistic view
* of interactions of different pieces of state.
*
* However, `state::State` already has the potential to become a ball of mud,
* tightly coupling different functionality and making it difficult to see independent units.
*
* For this reason, I'm taking a light touch approach with the panel manager,
* and moving it just a bit closer to `state::State`.
* Hopefully ths still allows us to expose assumptions that were not stated yet
* (e.g. can the output disappear between size request andallocation?).
*
* Tight coupling, e.g. a future one between presented hints and layout size,
* will have to be taken into account later.
*/
use crate::logging;
use crate::outputs::OutputId;
use crate::util::c::Wrapped;
pub mod c {
use super::*;
use glib;
use gtk::Continue;
use std::os::raw::c_void;
use crate::outputs::c::WlOutput;
/// struct panel_manager*
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct PanelManager(*const c_void);
extern "C" {
#[allow(improper_ctypes)]
pub fn panel_manager_request_widget(
service: PanelManager,
output: WlOutput,
height: u32,
// for callbacks
panel: Wrapped<Manager>,
);
pub fn panel_manager_resize(service: PanelManager, height: u32);
pub fn panel_manager_hide(service: PanelManager);
}
#[no_mangle]
pub extern "C"
fn squeek_panel_manager_configured(panel: Wrapped<Manager>, width: u32, height: u32) {
// This is why this needs to be moved into state::State:
// it's getting too coupled to glib.
glib::idle_add_local(move || {
let panel = panel.clone_ref();
panel.borrow_mut().set_configured(Size{width, height});
Continue(false)
});
}
}
/// Size in pixels that is aware of scaling
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct PixelSize {
pub pixels: u32,
pub scale_factor: u32,
}
fn div_ceil(a: u32, b: u32) -> u32 {
// Given that it's for pixels on a screen, an overflow is unlikely.
(a + b - 1) / b
}
impl PixelSize {
pub fn as_scaled_floor(&self) -> u32 {
self.pixels / self.scale_factor
}
pub fn as_scaled_ceiling(&self) -> u32 {
div_ceil(self.pixels, self.scale_factor)
}
}
#[derive(Clone, Debug)]
struct Size {
width: u32,
height: u32,
}
/// This state requests the Wayland layer shell protocol synchronization:
/// the application asks for some size,
/// and then receives a size that the compositor thought appropriate.
/// Stores raw values passed to Wayland, i.e. scaled dimensions.
#[derive(Clone, Debug)]
enum State {
Hidden,
SizeRequested {
output: OutputId,
height: u32,
//width: u32,
},
SizeAllocated {
output: OutputId,
wanted_height: u32,
allocated: Size,
},
}
#[derive(Clone, PartialEq, Debug)]
pub enum Command {
Show {
output: OutputId,
height: PixelSize,
},
Hide,
}
/// Tries to contain all the panel sizing duties.
pub struct Manager {
panel: c::PanelManager,
state: State,
}
impl Manager {
pub fn new(panel: c::PanelManager) -> Self {
Self {
panel,
state: State::Hidden,
}
}
// TODO: mabe send the allocated size back to state::State,
// to perform layout adjustments
fn set_configured(&mut self, size: Size) {
self.state = match self.state.clone() {
State::Hidden => {
// This may happen if a hide is scheduled immediately after a show.
log_print!(
logging::Level::Surprise,
"Panel has been configured, but no request is pending. Ignoring",
);
State::Hidden
},
State::SizeAllocated{output, wanted_height, ..} => {
log_print!(
logging::Level::Surprise,
"Panel received new configuration without asking",
);
State::SizeAllocated{output, wanted_height, allocated: size}
},
State::SizeRequested{output, height} => State::SizeAllocated {
output,
wanted_height: height,
allocated: size,
},
};
}
pub fn update(mgr: Wrapped<Manager>, cmd: Command) {
let copied = mgr.clone();
let mgr = mgr.clone_ref();
let mut mgr = mgr.borrow_mut();
(*mgr).state = match (cmd, mgr.state.clone()) {
(Command::Hide, State::Hidden) => State::Hidden,
(Command::Hide, State::SizeAllocated{..}) => {
unsafe { c::panel_manager_hide(mgr.panel); }
State::Hidden
},
(Command::Hide, State::SizeRequested{..}) => {
unsafe { c::panel_manager_hide(mgr.panel); }
State::Hidden
},
(Command::Show{output, height}, State::Hidden) => {
let height = height.as_scaled_ceiling();
unsafe { c::panel_manager_request_widget(mgr.panel, output.0, height, copied); }
State::SizeRequested{output, height}
},
(
Command::Show{output, height},
State::SizeRequested{output: req_output, height: req_height},
) => {
let height = height.as_scaled_ceiling();
if output == req_output && height == req_height {
State::SizeRequested{output: req_output, height: req_height}
} else if output == req_output {
// I'm not sure about that.
// This could cause a busy loop,
// when two requests are being processed at the same time:
// one message in the compositor to allocate size A,
// causing the state to update to height A'
// the other from the state wanting height B',
// causing the compositor to change size to B.
// So better cut this short here, despite artifacts.
// Out of simplicty, just ignore the new request.
// If that causes problems, the request in flight could be stored
// for the purpose of handling it better somehow.
State::SizeRequested{output: req_output, height: req_height}
} else {
// This looks weird, but should be safe.
// The stack seems to handle
// configure events on a dead surface.
unsafe {
c::panel_manager_hide(mgr.panel);
c::panel_manager_request_widget(mgr.panel, output.0, height, copied);
}
State::SizeRequested{output, height}
}
},
(
Command::Show{output, height},
State::SizeAllocated{output: alloc_output, allocated, wanted_height},
) => {
let height = height.as_scaled_ceiling();
if output == alloc_output && height == wanted_height {
State::SizeAllocated{output: alloc_output, wanted_height, allocated}
} else if output == alloc_output && height == allocated.height {
State::SizeAllocated{output: alloc_output, wanted_height: height, allocated}
} else if output == alloc_output {
// Should *all* other heights cause a resize?
// What about those between wanted and allocated?
unsafe { c::panel_manager_resize(mgr.panel, height); }
State::SizeRequested{output, height}
} else {
unsafe {
c::panel_manager_hide(mgr.panel);
c::panel_manager_request_widget(mgr.panel, output.0, height, copied);
}
State::SizeRequested{output, height}
}
},
}
}
}

View File

@ -11,16 +11,11 @@ use ::manager;
use ::resources;
// Traits
use gio::ActionMapExt;
use gio::SettingsExt;
#[cfg(feature = "gio_v0_5")]
use gio::SimpleActionExt;
use gio::prelude::ActionMapExt;
use gio::prelude::SettingsExt;
use glib::translate::FromGlibPtrNone;
use glib::variant::ToVariant;
#[cfg(not(feature = "gtk_v0_5"))]
use gtk::prelude::*;
use gtk::PopoverExt;
use gtk::WidgetExt;
use ::logging::Warn;
mod c {
@ -110,8 +105,13 @@ mod variants {
fn get_settings(schema_name: &str) -> Option<gio::Settings> {
let mut error_handler = logging::Print{};
gio::SettingsSchemaSource::get_default()
.or_warn(
#[cfg(feature = "glib_v0_14")]
let ss = gio::SettingsSchemaSource::default();
#[cfg(not(feature = "glib_v0_14"))]
let ss = gio::SettingsSchemaSource::get_default();
ss.or_warn(
&mut error_handler,
logging::Problem::Surprise,
"No gsettings schemas installed.",
@ -130,7 +130,11 @@ fn get_settings(schema_name: &str) -> Option<gio::Settings> {
fn set_layout(kind: String, name: String) {
let settings = get_settings("org.gnome.desktop.input-sources");
if let Some(settings) = settings {
#[cfg(feature = "glib_v0_14")]
let inputs = settings.value("sources");
#[cfg(not(feature = "glib_v0_14"))]
let inputs = settings.get_value("sources").unwrap();
let current = (kind.clone(), name.clone());
let inputs = variants::get_tuples(inputs).into_iter()
.filter(|t| t != &current);
@ -254,7 +258,11 @@ pub fn show(
let settings = get_settings("org.gnome.desktop.input-sources");
let inputs = settings
.map(|settings| {
#[cfg(feature = "glib_v0_14")]
let inputs = settings.value("sources");
#[cfg(not(feature = "glib_v0_14"))]
let inputs = settings.get_value("sources").unwrap();
variants::get_tuples(inputs)
})
.unwrap_or_else(|| Vec::new());
@ -285,8 +293,18 @@ pub fn show(
}
});
let builder = gtk::Builder::new_from_resource("/sm/puri/squeekboard/popover.ui");
let model: gio::Menu = builder.get_object("app-menu").unwrap();
let model: gio::Menu = {
#[cfg(feature = "glib_v0_14")]
{
let builder = gtk::Builder::from_resource("/sm/puri/squeekboard/popover.ui");
builder.object("app-menu").unwrap()
}
#[cfg(not(feature = "glib_v0_14"))]
{
let builder = gtk::Builder::new_from_resource("/sm/puri/squeekboard/popover.ui");
builder.get_object("app-menu").unwrap()
}
};
for (tr, l) in human_names.iter().rev() {
let detailed_action = format!("layout::{}", l.get_name());
@ -294,7 +312,11 @@ pub fn show(
model.prepend_item (&item);
}
#[cfg(feature = "glib_v0_14")]
let menu = gtk::Popover::from_model(Some(&window), &model);
#[cfg(not(feature = "glib_v0_14"))]
let menu = gtk::Popover::new_from_model(Some(&window), &model);
menu.set_pointing_to(&gtk::Rectangle {
x: position.x.ceil() as i32,
y: position.y.ceil() as i32,

View File

@ -54,6 +54,8 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
("gr", include_str!("../data/keyboards/gr.yaml")),
("gr_wide", include_str!("../data/keyboards/gr_wide.yaml")),
("gr+polytonic", include_str!("../data/keyboards/gr+polytonic.yaml")),
("il", include_str!("../data/keyboards/il.yaml")),
@ -71,6 +73,9 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
("pl", include_str!("../data/keyboards/pl.yaml")),
("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
("ro", include_str!("../data/keyboards/ro.yaml")),
("ro_wide", include_str!("../data/keyboards/ro_wide.yaml")),
("ru", include_str!("../data/keyboards/ru.yaml")),
("se", include_str!("../data/keyboards/se.yaml")),

View File

@ -20,12 +20,6 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "eek/eek.h"
#include "eek/eek-gtk-keyboard.h"
#include "eek/layersurface.h"
#include "eekboard/eekboard-context-service.h"
#include "submission.h"
#include "wayland.h"
#include "server-context-service.h"
enum {
@ -36,196 +30,13 @@ enum {
struct _ServerContextService {
GObject parent;
EekboardContextService *state; // unowned
/// Needed for instantiating the widget
struct submission *submission; // unowned
struct squeek_layout_state *layout;
struct ui_manager *manager; // unowned
struct squeek_state_manager *state_manager; // shared reference
PhoshLayerSurface *window;
GtkWidget *widget; // nullable
guint last_requested_height;
};
G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT);
static void
on_destroy (ServerContextService *self, GtkWidget *widget)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
g_assert (widget == GTK_WIDGET(self->window));
self->window = NULL;
self->widget = NULL;
//eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context));
}
static uint32_t
calculate_height(int32_t width, GdkRectangle *geometry)
{
uint32_t height;
if (geometry->width > geometry->height) {
// 1:5 ratio works fine on lanscape mode, and makes sure there's
// room left for the app window
height = width / 5;
} else {
if (width < 540 && width > 0) {
height = ((unsigned)width * 7 / 12); // to match 360×210
} else {
// Here we switch to wide layout, less height needed
height = ((unsigned)width * 7 / 22);
}
}
return height;
}
static void
on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
{
GdkDisplay *display = NULL;
GdkWindow *window = NULL;
GdkMonitor *monitor = NULL;
GdkRectangle geometry;
gint width;
gint height;
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (surface));
g_object_get(G_OBJECT(surface),
"configured-width", &width,
"configured-height", &height,
NULL);
// In order to improve height calculation, we need the monitor geometry so
// we can use different algorithms for portrait and landscape mode.
// Note: this is a temporary fix until the size manager is complete.
display = gdk_display_get_default ();
if (display) {
window = gtk_widget_get_window (GTK_WIDGET (surface));
}
if (window) {
monitor = gdk_display_get_monitor_at_window (display, window);
}
if (monitor) {
gdk_monitor_get_geometry (monitor, &geometry);
} else {
geometry.width = geometry.height = 0;
}
// When the geometry event comes after surface.configure,
// this entire height calculation does nothing.
// guint desired_height = squeek_uiman_get_perceptual_height(context->manager);
// Temporarily use old method, until the size manager is complete.
guint desired_height = calculate_height(width, &geometry);
guint configured_height = (guint)height;
// if height was already requested once but a different one was given
// (for the same set of surrounding properties),
// then it's probably not reasonable to ask for it again,
// as it's likely to create pointless loops
// of request->reject->request_again->...
if (desired_height != configured_height
&& self->last_requested_height != desired_height) {
self->last_requested_height = desired_height;
phosh_layer_surface_set_size(surface, 0,
(gint)desired_height);
phosh_layer_surface_set_exclusive_zone(surface, (gint)desired_height);
phosh_layer_surface_wl_surface_commit (surface);
}
}
static void
make_window (ServerContextService *self)
{
if (self->window) {
g_error("Window already present");
}
struct squeek_output_handle output = squeek_outputs_get_current(squeek_wayland->outputs);
squeek_uiman_set_output(self->manager, output);
uint32_t height = squeek_uiman_get_perceptual_height(self->manager);
self->window = g_object_new (
PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell,
"wl-output", output.output,
"height", height,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
"layer", ZWLR_LAYER_SHELL_V1_LAYER_TOP,
"kbd-interactivity", FALSE,
"exclusive-zone", height,
"namespace", "osk",
NULL
);
g_object_connect (self->window,
"swapped-signal::destroy", G_CALLBACK(on_destroy), self,
"swapped-signal::configured", G_CALLBACK(on_surface_configure), self,
NULL);
// The properties below are just to make hacking easier.
// The way we use layer-shell overrides some,
// and there's no space in the protocol for others.
// Those may still be useful in the future,
// or for hacks with regular windows.
gtk_widget_set_can_focus (GTK_WIDGET(self->window), FALSE);
g_object_set (G_OBJECT(self->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(self->window), "Squeekboard");
gtk_window_set_icon_name (GTK_WINDOW(self->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(self->window), TRUE);
}
static void
destroy_window (ServerContextService *self)
{
gtk_widget_destroy (GTK_WIDGET (self->window));
self->window = NULL;
}
static void
make_widget (ServerContextService *self)
{
if (self->widget) {
gtk_widget_destroy(self->widget);
self->widget = NULL;
}
self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->layout);
gtk_widget_set_has_tooltip (self->widget, TRUE);
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
gtk_widget_show_all(self->widget);
}
// Called from rust
void
server_context_service_real_show_keyboard (ServerContextService *self)
{
if (!self->window) {
make_window (self);
}
if (!self->widget) {
make_widget (self);
}
gtk_widget_show (GTK_WIDGET(self->window));
}
// Called from rust
void
server_context_service_real_hide_keyboard (ServerContextService *self)
{
if (self->window) {
gtk_widget_hide (GTK_WIDGET(self->window));
}
}
static void
/// Height is in scaled units.
server_context_service_set_property (GObject *object,
guint prop_id,
const GValue *value,
@ -256,17 +67,6 @@ server_context_service_get_property (GObject *object,
}
}
static void
server_context_service_dispose (GObject *object)
{
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
destroy_window (self);
self->widget = NULL;
G_OBJECT_CLASS (server_context_service_parent_class)->dispose (object);
}
static void
server_context_service_class_init (ServerContextServiceClass *klass)
{
@ -275,7 +75,6 @@ server_context_service_class_init (ServerContextServiceClass *klass)
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;
/**
* ServerContextServie:keyboard:
@ -296,42 +95,29 @@ server_context_service_class_init (ServerContextServiceClass *klass)
static void
server_context_service_init (ServerContextService *self) {}
static void
init (ServerContextService *self) {
ServerContextService *
server_context_service_new (struct squeek_state_manager *state_manager)
{
ServerContextService *holder = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
holder->state_manager = state_manager;
const char *schema_name = "org.gnome.desktop.a11y.applications";
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
g_autoptr(GSettingsSchema) schema = NULL;
if (!ssrc) {
g_warning("No gsettings schemas installed.");
return;
return NULL;
}
schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE);
if (schema) {
g_autoptr(GSettings) settings = g_settings_new (schema_name);
g_settings_bind (settings, "screen-keyboard-enabled",
self, "enabled", G_SETTINGS_BIND_GET);
holder, "enabled", G_SETTINGS_BIND_GET);
} else {
g_warning("Gsettings schema %s is not installed on the system. "
"Enabling by default.", schema_name);
}
}
ServerContextService *
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct squeek_state_manager *state_manager)
{
ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
ui->submission = submission;
ui->state = self;
ui->layout = layout;
ui->manager = uiman;
ui->state_manager = state_manager;
init(ui);
return ui;
}
// Used from Rust
void server_context_service_set_hint_purpose(ServerContextService *self, uint32_t hint,
uint32_t purpose) {
eekboard_context_service_set_hint_purpose(self->state, hint, purpose);
return holder;
}

View File

@ -17,10 +17,9 @@
*/
#ifndef SERVER_CONTEXT_SERVICE_H
#define SERVER_CONTEXT_SERVICE_H 1
#include <gtk/gtk.h>
#include "src/layout.h"
#include "src/submission.h"
#include "ui_manager.h"
#include "main.h"
G_BEGIN_DECLS
@ -29,10 +28,8 @@ G_BEGIN_DECLS
/** Manages the lifecycle of the window displaying layouts. */
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject)
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct squeek_state_manager *state_manager);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
void server_context_service_force_show_keyboard (ServerContextService *self);
void server_context_service_hide_keyboard (ServerContextService *self);
ServerContextService *server_context_service_new(struct squeek_state_manager *state_manager);
G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -31,9 +31,9 @@
#include "layout.h"
#include "main.h"
#include "outputs.h"
#include "panel.h"
#include "submission.h"
#include "server-context-service.h"
#include "ui_manager.h"
#include "wayland.h"
#include <gdk/gdkwayland.h>
@ -52,12 +52,12 @@ typedef enum _SqueekboardDebugFlags {
struct squeekboard {
struct squeek_wayland wayland; // Just hooks.
DBusHandler *dbus_handler; // Controls visibility of the OSK.
EekboardContextService *settings_context; // Gsettings hooks.
ServerContextService *ui_context; // mess, includes the entire UI
EekboardContextService *settings_context; // Gsettings hooks for layouts.
/// Gsettings hook for visibility. TODO: this does not belong in gsettings.
ServerContextService *settings_handler;
struct panel_manager panel_manager; // Controls the shape of the panel.
/// Currently wanted layout. TODO: merge into state::Application
struct squeek_layout_state layout_choice;
/// UI shape tracker/chooser. TODO: merge into state::Application
struct ui_manager *ui_manager;
};
@ -112,34 +112,38 @@ registry_handle_global (void *data,
// Even when lower version would be served, it would not be supported,
// causing a hard exit
(void)version;
struct squeekboard *instance = data;
struct squeek_wayland *wayland = data;
if (!strcmp (interface, zwlr_layer_shell_v1_interface.name)) {
instance->wayland.layer_shell = wl_registry_bind (registry, name,
wayland->layer_shell = wl_registry_bind (registry, name,
&zwlr_layer_shell_v1_interface, 1);
} else if (!strcmp (interface, zwp_virtual_keyboard_manager_v1_interface.name)) {
instance->wayland.virtual_keyboard_manager = wl_registry_bind(registry, name,
wayland->virtual_keyboard_manager = wl_registry_bind(registry, name,
&zwp_virtual_keyboard_manager_v1_interface, 1);
} else if (!strcmp (interface, zwp_input_method_manager_v2_interface.name)) {
instance->wayland.input_method_manager = wl_registry_bind(registry, name,
wayland->input_method_manager = wl_registry_bind(registry, name,
&zwp_input_method_manager_v2_interface, 1);
} else if (!strcmp (interface, "wl_output")) {
struct wl_output *output = wl_registry_bind (registry, name,
&wl_output_interface, 2);
squeek_outputs_register(instance->wayland.outputs, output);
squeek_outputs_register(wayland->outputs, output, name);
} else if (!strcmp(interface, "wl_seat")) {
instance->wayland.seat = wl_registry_bind(registry, name,
wayland->seat = wl_registry_bind(registry, name,
&wl_seat_interface, 1);
}
}
static void
registry_handle_global_remove (void *data,
struct wl_registry *registry,
uint32_t name)
{
// TODO
(void)registry;
struct squeek_wayland *wayland = data;
struct wl_output *output = squeek_outputs_try_unregister(wayland->outputs, name);
if (output) {
wl_output_destroy(output);
}
}
static const struct wl_registry_listener registry_listener = {
@ -147,6 +151,54 @@ static const struct wl_registry_listener registry_listener = {
registry_handle_global_remove
};
void init_wayland(struct squeek_wayland *wayland) {
// Set up Wayland
gdk_set_allowed_backends ("wayland");
GdkDisplay *gdk_display = gdk_display_get_default ();
struct wl_display *display = gdk_wayland_display_get_wl_display (gdk_display);
if (display == NULL) {
g_error ("Failed to get display: %m\n");
exit(1);
}
struct wl_registry *registry = wl_display_get_registry (display);
wl_registry_add_listener (registry, &registry_listener, wayland);
wl_display_roundtrip(display); // wait until the registry is actually populated
if (!wayland->seat) {
g_error("No seat Wayland global available.");
exit(1);
}
if (!wayland->virtual_keyboard_manager) {
g_error("No virtual keyboard manager Wayland global available.");
exit(1);
}
if (!wayland->layer_shell) {
g_error("No layer shell global available.");
exit(1);
}
if (!wayland->input_method_manager) {
g_warning("Wayland input method interface not available");
}
if (wayland->input_method_manager) {
wayland->input_method = zwp_input_method_manager_v2_get_input_method(
wayland->input_method_manager,
wayland->seat);
}
if (wayland->virtual_keyboard_manager) {
wayland->virtual_keyboard = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
wayland->virtual_keyboard_manager,
wayland->seat);
}
// initialize global
squeek_wayland = wayland;
}
#define SESSION_NAME "sm.puri.OSK0"
GDBusProxy *_proxy = NULL;
@ -284,22 +336,6 @@ phosh_theme_init (void)
g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", TRUE, NULL);
}
/// Create Rust objects in one go,
/// to avoid crossing the language barrier and losing type information
static struct rsobjects create_rsobjects(struct zwp_input_method_manager_v2 *immanager,
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
struct wl_seat *seat) {
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 squeek_rsobjects_new(im, vk);
}
static GDebugKey debug_keys[] =
{
{ .key = "force-show",
@ -359,46 +395,10 @@ main (int argc, char **argv)
phosh_theme_init ();
// Set up Wayland
gdk_set_allowed_backends ("wayland");
GdkDisplay *gdk_display = gdk_display_get_default ();
struct wl_display *display = gdk_wayland_display_get_wl_display (gdk_display);
if (display == NULL) {
g_error ("Failed to get display: %m\n");
exit(1);
}
struct squeekboard instance = {0};
squeek_wayland_init (&instance.wayland);
struct wl_registry *registry = wl_display_get_registry (display);
wl_registry_add_listener (registry, &registry_listener, &instance);
wl_display_roundtrip(display); // wait until the registry is actually populated
squeek_wayland_set_global(&instance.wayland);
if (!instance.wayland.seat) {
g_error("No seat Wayland global available.");
exit(1);
}
if (!instance.wayland.virtual_keyboard_manager) {
g_error("No virtual keyboard manager Wayland global available.");
exit(1);
}
if (!instance.wayland.layer_shell) {
g_error("No layer shell global available.");
exit(1);
}
if (!instance.wayland.input_method_manager) {
g_warning("Wayland input method interface not available");
}
struct rsobjects rsobjects = create_rsobjects(instance.wayland.input_method_manager,
instance.wayland.virtual_keyboard_manager,
instance.wayland.seat);
instance.ui_manager = squeek_uiman_new();
// Also initializes wayland
struct rsobjects rsobjects = squeek_init();
instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
@ -438,21 +438,21 @@ main (int argc, char **argv)
}
}
eekboard_context_service_set_submission(instance.settings_context, rsobjects.submission);
ServerContextService *ui_context = server_context_service_new(
instance.settings_context,
rsobjects.submission,
&instance.layout_choice,
instance.ui_manager,
ServerContextService *setting_listener = server_context_service_new(
rsobjects.state_manager);
if (!ui_context) {
g_error("Could not initialize GUI");
exit(1);
if (!setting_listener) {
g_warning ("could not connect to gsettings");
}
instance.ui_context = ui_context;
register_ui_loop_handler(rsobjects.receiver, instance.ui_context, instance.dbus_handler);
instance.settings_handler = setting_listener;
eekboard_context_service_set_submission(instance.settings_context, rsobjects.submission);
instance.panel_manager = panel_manager_new(instance.settings_context,
rsobjects.submission,
&instance.layout_choice);
register_ui_loop_handler(rsobjects.receiver, &instance.panel_manager, instance.settings_context, instance.dbus_handler);
session_register();
@ -477,6 +477,5 @@ main (int argc, char **argv)
}
g_main_loop_unref (loop);
squeek_wayland_deinit (&instance.wayland);
return 0;
}

View File

@ -6,35 +6,46 @@
* It's driven by the loop defined in the loop module. */
use crate::animation;
use crate::debug;
use crate::imservice::{ ContentHint, ContentPurpose };
use crate::main::{ Commands, PanelCommand };
use crate::main::Commands;
use crate::outputs;
use crate::outputs::{Millimeter, OutputId, OutputState};
use crate::panel;
use crate::panel::PixelSize;
use crate::util::Rational;
use std::cmp;
use std::collections::HashMap;
use std::time::Instant;
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub enum Presence {
Present,
Missing,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct InputMethodDetails {
pub hint: ContentHint,
pub purpose: ContentPurpose,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum InputMethod {
Active(InputMethodDetails),
InactiveSince(Instant),
}
/// Incoming events
#[derive(Clone)]
/// Incoming events.
/// This contains events that cause a change to the internal state.
#[derive(Clone, Debug)]
pub enum Event {
InputMethod(InputMethod),
Visibility(visibility::Event),
PhysicalKeyboard(Presence),
Output(outputs::Event),
Debug(debug::Event),
/// Event triggered because a moment in time passed.
/// Use to animate state transitions.
/// The value is the ideal arrival time.
@ -47,8 +58,14 @@ impl From<InputMethod> for Event {
}
}
impl From<outputs::Event> for Event {
fn from(ev: outputs::Event) -> Self {
Self::Output(ev)
}
}
pub mod visibility {
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum Event {
/// User requested the panel to show
ForceVisible,
@ -68,7 +85,7 @@ pub mod visibility {
}
/// The outwardly visible state.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Outcome {
pub visibility: animation::Outcome,
pub im: InputMethod,
@ -83,12 +100,12 @@ impl Outcome {
pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
let layout_hint_set = match new_state {
Outcome {
visibility: animation::Outcome::Visible,
visibility: animation::Outcome::Visible{..},
im: InputMethod::Active(hints),
} => Some(hints.clone()),
Outcome {
visibility: animation::Outcome::Visible,
visibility: animation::Outcome::Visible{..},
im: InputMethod::InactiveSince(_),
} => Some(InputMethodDetails {
hint: ContentHint::NONE,
@ -100,10 +117,11 @@ impl Outcome {
..
} => None,
};
// FIXME: handle switching outputs
let (dbus_visible_set, panel_visibility) = match new_state.visibility {
animation::Outcome::Visible => (Some(true), Some(PanelCommand::Show)),
animation::Outcome::Hidden => (Some(false), Some(PanelCommand::Hide)),
animation::Outcome::Visible{output, height}
=> (Some(true), Some(panel::Command::Show{output, height})),
animation::Outcome::Hidden => (Some(false), Some(panel::Command::Hide)),
};
Commands {
@ -125,11 +143,19 @@ impl Outcome {
/// All state changes return the next state and the optimal time for the next check.
///
/// This state tracker can be driven by any event loop.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Application {
pub im: InputMethod,
pub visibility_override: visibility::State,
pub physical_keyboard: Presence,
pub debug_mode_enabled: bool,
/// The output on which the panel should appear.
/// This is stored as part of the state
/// because it's not clear how to derive the output from the rest of the state.
/// It should probably follow the focused input,
/// but not sure about being allowed on non-touch displays.
pub preferred_output: Option<OutputId>,
pub outputs: HashMap<OutputId, OutputState>,
}
impl Application {
@ -144,11 +170,29 @@ impl Application {
im: InputMethod::InactiveSince(now),
visibility_override: visibility::State::NotForced,
physical_keyboard: Presence::Missing,
debug_mode_enabled: false,
preferred_output: None,
outputs: Default::default(),
}
}
pub fn apply_event(self, event: Event, _now: Instant) -> Self {
match event {
pub fn apply_event(self, event: Event, now: Instant) -> Self {
if self.debug_mode_enabled {
println!(
"Received event:
{:#?}",
event,
);
}
let state = match event {
Event::Debug(dbg) => Self {
debug_mode_enabled: match dbg {
debug::Event::Enable => true,
debug::Event::Disable => false,
},
..self
},
Event::TimeoutReached(_) => self,
Event::Visibility(visibility) => Self {
@ -164,6 +208,25 @@ impl Application {
..self
},
Event::Output(outputs::Event { output, change }) => {
let mut app = self;
match change {
outputs::ChangeType::Altered(state) => {
app.outputs.insert(output, state);
app.preferred_output = app.preferred_output.or(Some(output));
},
outputs::ChangeType::Removed => {
app.outputs.remove(&output);
if app.preferred_output == Some(output) {
// There's currently no policy to choose one output over another,
// so just take whichever comes first.
app.preferred_output = app.outputs.keys().next().map(|output| *output);
}
},
};
app
},
Event::InputMethod(new_im) => match (self.im.clone(), new_im) {
(InputMethod::Active(_old), InputMethod::Active(new_im))
=> Self {
@ -195,23 +258,117 @@ impl Application {
..self
},
}
};
if state.debug_mode_enabled {
println!(
"State is now:
{:#?}
Outcome:
{:#?}",
state,
state.get_outcome(now),
);
}
state
}
fn get_preferred_height(output: &OutputState) -> Option<PixelSize> {
output.get_pixel_size()
.map(|px_size| {
// Assume isotropy.
// Pixels/mm.
let density = output.get_physical_size()
.and_then(|size| size.width)
.map(|width| Rational {
numerator: px_size.width as i32,
denominator: width.0 as u32,
})
// Whatever the Librem 5 has,
// as a good default.
.unwrap_or(Rational {
numerator: 720,
denominator: 65,
});
// Based on what works on the L5.
// Exceeding that probably wastes space. Reducing makes typing harder.
const IDEAL_TARGET_SIZE: Rational<Millimeter> = Rational {
numerator: Millimeter(948),
denominator: 100,
};
// TODO: calculate based on selected layout
const ROW_COUNT: u32 = 4;
let height = {
let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32;
let ideal_height_px = (ideal_height * density).ceil().0 as u32;
// Reduce height to match what the layout can fill.
// For this, we need to guess if normal or wide will be picked up.
// This must match `eek_gtk_keyboard.c::get_type`.
// TODO: query layout database and choose one directly
let abstract_width
= PixelSize {
scale_factor: output.scale as u32,
pixels: px_size.width,
}
.as_scaled_ceiling();
let height_as_widths = {
if abstract_width < 540 {
// Normal
Rational {
numerator: 210,
denominator: 360,
}
} else {
// Wide
Rational {
numerator: 172,
denominator: 540,
}
}
};
cmp::min(
ideal_height_px,
(height_as_widths * px_size.width as i32).ceil() as u32,
)
};
PixelSize {
scale_factor: output.scale as u32,
pixels: cmp::min(height, px_size.height / 2),
}
})
}
pub fn get_outcome(&self, now: Instant) -> Outcome {
// FIXME: include physical keyboard presence
Outcome {
visibility: match (self.physical_keyboard, self.visibility_override) {
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
(_, visibility::State::ForcedVisible) => animation::Outcome::Visible,
(Presence::Present, visibility::State::NotForced) => animation::Outcome::Hidden,
(Presence::Missing, visibility::State::NotForced) => match self.im {
InputMethod::Active(_) => animation::Outcome::Visible,
InputMethod::InactiveSince(since) => {
if now < since + animation::HIDING_TIMEOUT { animation::Outcome::Visible }
else { animation::Outcome::Hidden }
},
},
visibility: match self.preferred_output {
None => animation::Outcome::Hidden,
Some(output) => {
// Hoping that this will get optimized out on branches not using `visible`.
let height = Self::get_preferred_height(self.outputs.get(&output).unwrap())
.unwrap_or(PixelSize{pixels: 0, scale_factor: 1});
// TODO: Instead of setting size to 0 when the output is invalid,
// simply go invisible.
let visible = animation::Outcome::Visible{ output, height };
match (self.physical_keyboard, self.visibility_override) {
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
(_, visibility::State::ForcedVisible) => visible,
(Presence::Present, visibility::State::NotForced) => animation::Outcome::Hidden,
(Presence::Missing, visibility::State::NotForced) => match self.im {
InputMethod::Active(_) => visible,
InputMethod::InactiveSince(since) => {
if now < since + animation::HIDING_TIMEOUT { visible }
else { animation::Outcome::Hidden }
},
},
}
}
},
im: self.im.clone(),
}
@ -236,9 +393,9 @@ impl Application {
#[cfg(test)]
mod test {
pub mod test {
use super::*;
use crate::outputs::c::WlOutput;
use std::time::Duration;
fn imdetails_new() -> InputMethodDetails {
@ -248,6 +405,30 @@ mod test {
}
}
fn fake_output_id(id: usize) -> OutputId {
OutputId(unsafe {
std::mem::transmute::<_, WlOutput>(id)
})
}
pub fn application_with_fake_output(start: Instant) -> Application {
let id = fake_output_id(1);
let mut outputs = HashMap::new();
outputs.insert(
id,
OutputState {
current_mode: None,
geometry: None,
scale: 1,
},
);
Application {
preferred_output: Some(id),
outputs,
..Application::new(start)
}
}
/// Test the original delay scenario: no flicker on quick switches.
#[test]
fn avoid_hide() {
@ -257,15 +438,16 @@ mod test {
im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
};
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
// Check 100ms at 1ms intervals. It should remain visible.
for _i in 0..100 {
now += Duration::from_millis(1);
assert_eq!(
assert_matches!(
state.get_outcome(now).visibility,
animation::Outcome::Visible,
animation::Outcome::Visible{..},
"Hidden when it should remain visible: {:?}",
now.saturating_duration_since(start),
)
@ -273,7 +455,7 @@ mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
assert_eq!(state.get_outcome(now).visibility, animation::Outcome::Visible);
assert_matches!(state.get_outcome(now).visibility, animation::Outcome::Visible{..});
}
/// Make sure that hiding works when input method goes away
@ -285,11 +467,12 @@ mod test {
im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
};
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
while let animation::Outcome::Visible = state.get_outcome(now).visibility {
while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility {
now += Duration::from_millis(1);
assert!(
now < start + Duration::from_millis(250),
@ -309,6 +492,7 @@ mod test {
im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
};
// This reflects the sequence from Wayland:
// disable, disable, enable, disable
@ -318,7 +502,7 @@ mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
while let animation::Outcome::Visible = state.get_outcome(now).visibility {
while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility {
now += Duration::from_millis(1);
assert!(
now < start + Duration::from_millis(250),
@ -347,13 +531,14 @@ mod test {
im: InputMethod::InactiveSince(now),
physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
};
now += Duration::from_secs(1);
let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now);
assert_eq!(
assert_matches!(
state.get_outcome(now).visibility,
animation::Outcome::Visible,
animation::Outcome::Visible{..},
"Failed to show: {:?}",
now.saturating_duration_since(start),
);
@ -380,6 +565,7 @@ mod test {
im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
};
now += Duration::from_secs(1);
@ -406,12 +592,37 @@ mod test {
now += Duration::from_secs(1);
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now);
assert_eq!(
assert_matches!(
state.get_outcome(now).visibility,
animation::Outcome::Visible,
animation::Outcome::Visible{..},
"Failed to appear: {:?}",
now.saturating_duration_since(start),
);
}
#[test]
fn size_l5() {
use crate::outputs::{Mode, Geometry, c, Size};
assert_eq!(
Application::get_preferred_height(&OutputState {
current_mode: Some(Mode {
width: 720,
height: 1440,
}),
geometry: Some(Geometry{
transform: c::Transform::Normal,
phys_size: Size {
width: Some(Millimeter(65)),
height: Some(Millimeter(130)),
},
}),
scale: 2,
}),
Some(PixelSize {
scale_factor: 2,
pixels: 420,
}),
);
}
}

View File

@ -21,7 +21,7 @@
use std::env;
use ::logging;
use glib::object::ObjectExt;
use glib::prelude::ObjectExt;
use logging::Warn;
/// Gathers stuff defined in C or called by C
@ -31,7 +31,7 @@ pub mod c {
use gtk;
use gtk_sys;
use gtk::CssProviderExt;
use gtk::prelude::CssProviderExt;
use glib::translate::ToGlibPtr;
/// Loads the layout style based on current theme
@ -40,8 +40,13 @@ pub mod c {
pub extern "C"
fn squeek_load_style() -> *const gtk_sys::GtkCssProvider {
unsafe { gtk::set_initialized() };
let theme = gtk::Settings::get_default()
.map(|settings| get_theme_name(&settings));
#[cfg(feature = "glib_v0_14")]
let theme = gtk::Settings::default();
#[cfg(not(feature = "glib_v0_14"))]
let theme = gtk::Settings::get_default();
let theme = theme.map(|settings| get_theme_name(&settings));
let css_name = path_from_theme(theme);
@ -93,19 +98,31 @@ fn get_theme_name(settings: &gtk::Settings) -> GtkTheme {
e
}).ok();
#[cfg(feature = "glib_v0_14")]
let prop = |s: &gtk::Settings, name| s.property(name);
#[cfg(not(feature = "glib_v0_14"))]
let prop = |s: &gtk::Settings, name| s.get_property(name);
#[cfg(feature = "glib_v0_14")]
fn check<T, E: std::fmt::Display>(v: Result<T, E>) -> Option<T> {
v.or_print(logging::Problem::Surprise, "Key not of expected type")
}
#[cfg(not(feature = "glib_v0_14"))]
fn check<T>(v: Option<T>) -> Option<T> { v }
match env_theme {
Some(theme) => theme,
None => GtkTheme {
name: {
settings.get_property("gtk-theme-name")
prop(settings, "gtk-theme-name")
.or_print(logging::Problem::Surprise, "No theme name")
.and_then(|value| value.get::<String>())
.and_then(|value| check(value.get::<String>()))
.unwrap_or(DEFAULT_THEME_NAME.into())
},
variant: {
settings.get_property("gtk-application-prefer-dark-theme")
prop(settings, "gtk-application-prefer-dark-theme")
.or_print(logging::Problem::Surprise, "No settings key")
.and_then(|value| value.get::<bool>())
.and_then(|value| check(value.get::<bool>()))
.and_then(|dark_preferred| match dark_preferred {
true => Some("dark".into()),
false => None,

View File

@ -1,13 +1,12 @@
#ifndef __SUBMISSION_H
#define __SUBMISSION_H
#include "input-method-unstable-v2-client-protocol.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "inttypes.h"
#include "eek/eek-types.h"
#include "main.h"
#include "src/ui_manager.h"
struct squeek_layout;
struct submission;
// Defined in Rust
uint8_t submission_hint_available(struct submission *self);

View File

@ -1,19 +0,0 @@
#ifndef UI_MANAGER__
#define UI_MANAGER__
#include <inttypes.h>
#include "eek/eek-types.h"
#include "outputs.h"
#include "main.h"
struct ui_manager;
struct ui_manager *squeek_uiman_new(void);
void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output);
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
struct vis_manager;
struct vis_manager *squeek_visman_new(struct squeek_state_manager *state_manager);
#endif

View File

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

View File

@ -7,6 +7,7 @@ use ::float_ord::FloatOrd;
use std::borrow::Borrow;
use std::hash::{ Hash, Hasher };
use std::iter::FromIterator;
use std::ops::Mul;
pub mod c {
use super::*;
@ -157,6 +158,54 @@ pub fn find_max_double<T, I, F>(iterator: I, get: F)
.0
}
pub trait DivCeil<Rhs = Self> {
type Output;
fn div_ceil(self, rhs: Rhs) -> Self::Output;
}
/// Newer Rust introduces this natively,
/// but we don't always have newer Rust.
impl DivCeil for i32 {
type Output = Self;
fn div_ceil(self, other: i32) -> Self::Output {
let d = self / other;
let m = self % other;
if m == 0 { d } else { d + 1}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Rational<T> {
pub numerator: T,
pub denominator: u32,
}
impl<U, T: DivCeil<i32, Output=U>> Rational<T> {
pub fn ceil(self) -> U {
self.numerator.div_ceil(self.denominator as i32)
}
}
impl<T: Mul<i32, Output=T>> Mul<i32> for Rational<T> {
type Output = Self;
fn mul(self, m: i32) -> Self {
Self {
numerator: self.numerator * m,
denominator: self.denominator,
}
}
}
impl<U, T: Mul<U, Output=T>> Mul<Rational<U>> for Rational<T> {
type Output = Self;
fn mul(self, m: Rational<U>) -> Self {
Self {
numerator: self.numerator * m.numerator,
denominator: self.denominator * m.denominator,
}
}
}
/// Compares pointers but not internal values of Rc
pub struct Pointer<T>(pub Rc<T>);

View File

@ -10,11 +10,18 @@ type KeyCode = u32;
pub mod c {
use std::ffi::CStr;
use std::os::raw::{ c_char, c_void };
use std::ptr;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct ZwpVirtualKeyboardV1(*const c_void);
impl ZwpVirtualKeyboardV1 {
pub fn null() -> Self {
Self(ptr::null())
}
}
#[repr(C)]
pub struct KeyMap {
fd: u32,

View File

@ -4,6 +4,12 @@
struct squeek_wayland *squeek_wayland = NULL;
void squeek_wayland_init_global(struct squeek_outputs *outputs) {
struct squeek_wayland *wayland = {0};
wayland->outputs = outputs;
squeek_wayland = wayland;
}
// The following functions only exist
// to create linkable symbols out of inline functions,
// because those are not directly callable from Rust

View File

@ -10,27 +10,18 @@
#include "outputs.h"
struct squeek_wayland {
// globals
struct zwlr_layer_shell_v1 *layer_shell;
struct zwp_virtual_keyboard_manager_v1 *virtual_keyboard_manager;
struct zwp_input_method_manager_v2 *input_method_manager;
struct squeek_outputs *outputs;
struct wl_seat *seat;
// objects
struct zwp_input_method_v2 *input_method;
struct zwp_virtual_keyboard_v1 *virtual_keyboard;
};
extern struct squeek_wayland *squeek_wayland;
static inline void squeek_wayland_init(struct squeek_wayland *wayland) {
wayland->outputs = squeek_outputs_new();
}
static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) {
squeek_wayland = wayland;
}
static inline void squeek_wayland_deinit(struct squeek_wayland *wayland) {
squeek_outputs_free(wayland->outputs);
}
#endif // WAYLAND_H

View File

@ -76,7 +76,8 @@ foreach layout : [
'es+cat',
'fi',
'fr', 'fr_wide',
'gr',
'gr', 'gr_wide',
'gr+polytonic',
'il',
'ir',
'it',
@ -84,6 +85,7 @@ foreach layout : [
'jp+kana','jp+kana_wide',
'no',
'pl', 'pl_wide',
'ro', 'ro_wide',
'ru',
'se',
'th', 'th_wide',
@ -107,15 +109,8 @@ foreach layout : [
extra += ['allow_missing_return']
endif
# Older Cargo seens to be sensitive to something
# about the RUST_FLAGS env var, and rebuilds all tests when it's set,
# increasing test time by 2 orders of magnitude.
# Let it have its way.
if get_option('legacy') == true
timeout = 300
else
timeout = 30
endif
timeout = 30
test(
'test_layout_' + layout,
cargo_script,

11
tools/entry.py Normal file → Executable file
View File

@ -46,9 +46,12 @@ class App(Gtk.Application):
] + terminal
hints = [
("OSK provided", Gtk.InputHints.INHIBIT_OSK)
("OSK provided", Gtk.InputHints.INHIBIT_OSK),
("Uppercase chars", Gtk.InputHints.UPPERCASE_CHARS),
]
purpose_timer = 0;
def on_purpose_toggled(self, btn, entry):
purpose = Gtk.InputPurpose.PIN if btn.get_active() else Gtk.InputPurpose.PASSWORD
entry.set_input_purpose(purpose)
@ -60,13 +63,17 @@ class App(Gtk.Application):
e.set_input_purpose(purpose)
return True
def on_is_focus_changed(self, e, *args):
if not self.purpose_timer and e.props.is_focus:
GLib.timeout_add_seconds (3, self.on_timeout, e)
def add_random (self, grid):
l = Gtk.Label(label="Random")
e = Gtk.Entry(hexpand=True)
e.connect("notify::is-focus", self.on_is_focus_changed)
e.set_input_purpose(Gtk.InputPurpose.FREE_FORM)
grid.attach(l, 0, len(self.purposes), 1, 1)
grid.attach(e, 1, len(self.purposes), 1, 1)
GLib.timeout_add_seconds (3, self.on_timeout, e)
def do_activate(self):
w = Gtk.ApplicationWindow(application=self)

104
tools/entry4.py Executable file
View File

@ -0,0 +1,104 @@
#!/usr/bin/env python3
import gi
import random
import sys
gi.require_version('Gtk', '4.0')
gi.require_version('GLib', '2.0')
from gi.repository import Gtk
from gi.repository import GLib
def new_grid(items, set_type):
grid = Gtk.Grid(orientation='vertical', column_spacing=8, row_spacing=8)
i = 0
for text, value in items:
label = Gtk.Label(label=text)
label.props.margin_top = 6
label.props.margin_start = 6
entry = Gtk.Entry(hexpand=True)
entry.props.margin_top = 6
entry.props.margin_end = 6
set_type(entry, value)
grid.attach(label, 0, i, 1, 1)
grid.attach(entry, 1, i, 1, 1)
i += 1
return grid
class App(Gtk.Application):
purposes = [
("Free form", Gtk.InputPurpose.FREE_FORM),
("Alphabetical", Gtk.InputPurpose.ALPHA),
("Digits", Gtk.InputPurpose.DIGITS),
("Number", Gtk.InputPurpose.NUMBER),
("Phone", Gtk.InputPurpose.PHONE),
("URL", Gtk.InputPurpose.URL),
("E-mail", Gtk.InputPurpose.EMAIL),
("Name", Gtk.InputPurpose.NAME),
("Password", Gtk.InputPurpose.PASSWORD),
("PIN", Gtk.InputPurpose.PIN),
("Terminal", Gtk.InputPurpose.TERMINAL),
]
hints = [
("OSK provided", Gtk.InputHints.INHIBIT_OSK)
]
purpose_tick_id = 0
def on_purpose_toggled(self, btn, entry):
purpose = Gtk.InputPurpose.PIN if btn.get_active() else Gtk.InputPurpose.PASSWORD
entry.set_input_purpose(purpose)
def on_timeout(self, e):
r = random.randint(0, len(self.purposes) - 1)
(_, purpose) = self.purposes[r]
print(f"Setting {purpose}")
e.set_input_purpose(purpose)
return True
def on_random_enter(self, controller, entry):
self.purpose_tick_id = GLib.timeout_add_seconds(3, self.on_timeout, entry)
def on_random_leave(self, controller, entry):
GLib.source_remove(self.purpose_tick_id)
def add_random(self, grid):
label = Gtk.Label(label="Random")
entry = Gtk.Entry(hexpand=True)
entry.set_input_purpose(Gtk.InputPurpose.FREE_FORM)
grid.attach(label, 0, len(self.purposes), 1, 1)
grid.attach(entry, 1, len(self.purposes), 1, 1)
focus_controller = Gtk.EventControllerFocus()
entry.add_controller(focus_controller)
focus_controller.connect("enter", self.on_random_enter, entry)
focus_controller.connect("leave", self.on_random_leave, entry)
def do_activate(self):
w = Gtk.ApplicationWindow(application=self)
w.set_default_size(300, 500)
notebook = Gtk.Notebook()
def add_purpose(entry, purpose):
entry.set_input_purpose(purpose)
def add_hint(entry, hint):
entry.set_input_hints(hint)
purpose_grid = new_grid(self.purposes, add_purpose)
self.add_random(purpose_grid)
hint_grid = new_grid(self.hints, add_hint)
purpose_scroll = Gtk.ScrolledWindow()
purpose_scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
purpose_scroll.set_child(purpose_grid)
notebook.append_page(purpose_scroll, Gtk.Label(label="Purposes"))
notebook.append_page(hint_grid, Gtk.Label(label="Hints"))
w.set_child(notebook)
w.present()
app = App()
app.run(sys.argv)