Compare commits

...

153 Commits

Author SHA1 Message Date
0c99067b4b Release 1.19.0 "Chirality"
The main behind-the-scenes change in this release is the rework which gathers code selecting layout versions into one place. A resource leak causing slowdowns was also avoided.

New translations:
- Chinese (China)
- French
- Russian

Layout changes:
- new FR+Bépo
- new Georgian
- improved Italian

Thanks to all contrbutors.
2022-07-06 06:12:46 +00:00
ec7116053f build: Update Cargo.lock 2022-07-06 06:08:13 +00:00
cc7657e78c Merge branch 'leak' into 'master'
gtk: Persist panel state

See merge request World/Phosh/squeekboard!563
2022-07-06 06:03:27 +00:00
70bf101812 gtk: Persist panel state
Creating and destroying the panel as needed causes a resource leak somewhere in the deeper layers of the stack. This is a workaround.

See https://gitlab.gnome.org/World/Phosh/squeekboard/-/issues/343
2022-07-01 08:07:29 +00:00
dcz
12572b9de2 Merge branch 'par' into 'master'
build: Generate files before compiling sources

See merge request World/Phosh/squeekboard!562
2022-06-30 19:03:45 +00:00
9528339e02 build: Generate files before compiling sources 2022-06-30 18:12:57 +00:00
dcz
50ef4ab433 Merge branch 'fixci' into 'master'
ci: Use bookworm for online builds

See merge request World/Phosh/squeekboard!561
2022-06-29 15:20:20 +00:00
a8fe4a492e ci: Use bookworm for online builds
PureOS carries rather old versions of rustc and cargo. Attempting to build fails at transitive dependencies, and it's easier to upgrade the compiler than to track down and hold offending depencencies as they come.
2022-06-29 14:59:45 +00:00
dcz
0aee348e30 Merge branch 'georgian-layout' into 'master'
Added Georgian layout

See merge request World/Phosh/squeekboard!558
2022-06-24 18:28:27 +00:00
7f9baa8021 Added Georgian layout 2022-06-24 18:28:21 +00:00
dcz
feefe3fec2 Merge branch 'fix' into 'master'
cargo: Halt overzealous upgrader

See merge request World/Phosh/squeekboard!559
2022-06-24 17:58:31 +00:00
94aa0fa68f cargo: Halt overzealous upgrader
We're using a version of Cargo which doesn't support 2021 edition. The dependency Hashbrown 0.12 uses it, so we perform a minimal reshuffling to use 0.11.
2022-06-24 17:30:56 +00:00
c86161d5b5 Add Russian translation 2022-06-18 21:22:20 +00:00
dcz
49fc28f0a9 Merge branch 'dddoc' into 'master'
doc: Describe how to control debugging mode

See merge request World/Phosh/squeekboard!554
2022-06-14 15:23:43 +00:00
dcz
ea73a34eb8 Merge branch 'layouts' into 'master'
state: Select the layout

See merge request World/Phosh/squeekboard!553
2022-06-14 15:23:14 +00:00
dcz
f8ce24fdbf Merge branch 'keyboard-layout-it' into 'master'
Fixed missing characters in eschars view in italian layout.

See merge request World/Phosh/squeekboard!557
2022-06-11 10:09:58 +00:00
d797ee223b Replaced foreign characters. 2022-06-10 21:25:06 +00:00
267b0745a2 Fixed missing characters in eschars view in italian layout. 2022-06-10 18:41:36 +00:00
c1ceec3673 state: Become the source of layout choice
A redesign of popover was needed: it can no longer query the application state directly due to current state being its own actor, so instead the popover gets a dedicated copy of the relevant state.

I'm not entirely happy with the extra complexity of having an extra actor just for 1 string, but at least the duplication between C and Rust and mutual calls have been reduced.
2022-06-06 16:10:53 +00:00
30141db28d main: Dry-load layout in response to the layout command 2022-06-06 16:10:53 +00:00
8d0e1b4548 state: Use IM hint and purpose for layout selection 2022-06-06 16:10:53 +00:00
e6326b9b38 state: Use dummy layout command 2022-06-06 16:10:53 +00:00
b634e2bfa4 state: Decide panel arrangement
Combines arrangement with layout to get panel contents as outcome.

Includes some path syntax changes for 2018 compatibility.
2022-06-06 16:10:53 +00:00
590cd71f49 state: Store layout override
Not used for any externally observable effects
2022-06-06 16:10:53 +00:00
8ff72f312a state: Include layout choice in visible outcome
This is still in preparation and doesn't issue any observable effects.
2022-06-06 16:10:53 +00:00
bb22d9650b doc: Describe how to control debugging mode 2022-06-06 16:10:03 +00:00
82774d2315 state: Record layout choice
This does not get plugged into anything but debug prints yet.
2022-06-04 17:47:09 +00:00
dcz
9d83910696 Merge branch 'add_fr_bepo_layout' into 'master'
Add FR+Bépo layout

See merge request World/Phosh/squeekboard!552
2022-06-03 11:56:13 +00:00
c75723b705 Add FR+Bépo layout 2022-06-01 21:02:21 +02:00
dcz
af716f72da Merge branch '118' into 'master'
Release 1.18.0 "Dunbar's number"

See merge request World/Phosh/squeekboard!550
2022-06-01 13:40:58 +00:00
dcz
67e9316fe5 Merge branch 'cifix' into 'master'
glib: Fix import

See merge request World/Phosh/squeekboard!551
2022-06-01 13:17:46 +00:00
b54922b021 Add French translation 2022-05-25 15:21:05 +00:00
c0f57e7355 glib: Fix import 2022-04-28 15:28:36 +00:00
dcz
a413146888 Merge branch 'dooc' into 'master'
docs: Describe layouts

See merge request World/Phosh/squeekboard!549
2022-04-28 15:05:38 +00:00
ccc90e1677 Add Chinese (China) translation 2022-04-25 16:44:09 +00:00
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
111c0d157f docs: Describe layouts 2022-04-20 14:52:25 +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
109 changed files with 5904 additions and 1217 deletions

View File

@ -1,15 +1,13 @@
image: debian:bullseye image: pureos/byzantium
stages: stages:
- build - build
- test - test
- deploy
before_script: before_script:
- apt-get -y update - apt-get -y update
- apt-get -y install wget ca-certificates gnupg - apt-get -y install ca-certificates
- 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
build_docs: build_docs:
stage: build stage: build
@ -25,12 +23,14 @@ build_docs:
- $PKG_ONLY == "1" - $PKG_ONLY == "1"
build_meson: build_meson:
image: debian:bookworm
stage: build stage: build
artifacts: artifacts:
paths: paths:
- _build - _build
expire_in: 3h expire_in: 3h
script: script:
- mv debian/control-newer debian/control
- apt-get -y build-dep . - apt-get -y build-dep .
- meson . _build/ -Ddepdatadir=/usr/share --werror - meson . _build/ -Ddepdatadir=/usr/share --werror
- ninja -C _build install - ninja -C _build install
@ -56,6 +56,7 @@ build_deb:
- cp ../*.deb . - cp ../*.deb .
build_deb:arm64: build_deb:arm64:
image: pureos/byzantium
tags: tags:
- aarch64 - aarch64
stage: build stage: build
@ -74,6 +75,45 @@ build_deb:arm64:
- debuild -i -us -uc -b - debuild -i -us -uc -b
- cp ../*.deb . - 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:
image: debian:bookworm
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: test_lintian:
stage: test stage: test
needs: needs:
@ -87,11 +127,13 @@ test_lintian:
- $PKG_ONLY == "1" - $PKG_ONLY == "1"
test: test:
image: debian:bookworm
stage: test stage: test
needs: needs:
- job: build_meson - job: build_meson
artifacts: true artifacts: true
script: script:
- mv debian/control-newer debian/control
- apt-get -y build-dep . - apt-get -y build-dep .
- apt-get -y install clang-tidy - apt-get -y install clang-tidy
- ninja -C _build test - ninja -C _build test
@ -122,3 +164,17 @@ check_release:
except: except:
variables: variables:
- $PKG_ONLY == "1" - $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 # Dependencies which change based on build flags
bitflags = "1.2.*" bitflags = "1.2.*"
clap = { version = "2.33.*", default-features = false } 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] [dependencies.cairo-rs]
version = "0.7.*" version = "0.7.*"
[dependencies.cairo-sys-rs]
version = "0.9"
[dependencies.gdk] [dependencies.gdk]
version = "0.11.*" version = "0.11.*"
@ -17,6 +23,14 @@ features = ["v2_44"]
version = "0.8.*" version = "0.8.*"
features = ["v2_44"] features = ["v2_44"]
[dependencies.glib-sys]
version = "*"
features = ["v2_44"]
[dependencies.gtk] [dependencies.gtk]
version = "0.7.*" version = "0.7.*"
features = ["v3_22"] 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" }

270
Cargo.lock generated
View File

@ -30,9 +30,9 @@ dependencies = [
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@ -40,6 +40,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
version = "0.7.1" version = "0.7.1"
@ -67,15 +73,27 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.72" version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "clap" name = "clap"
version = "2.33.3" version = "2.33.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" checksum = "826bf7bc84f9435630275cb8e802a4a0ec792b615969934bd16d42ffed10f207"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"textwrap", "textwrap",
@ -83,16 +101,50 @@ dependencies = [
] ]
[[package]] [[package]]
name = "dtoa" name = "derivative"
version = "0.4.8" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "fragile" name = "fragile"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://source.puri.sm/dorota.czaplejewicz/fragile.git?tag=0.3.0#51048ca11824279c2114c77fef5bcb950838fc09"
checksum = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
[[package]] [[package]]
name = "gdk" name = "gdk"
@ -273,14 +325,23 @@ checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.7.0" version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "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]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -289,15 +350,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.108" version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.4" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]] [[package]]
name = "maplit" name = "maplit"
@ -315,6 +376,19 @@ dependencies = [
"winapi", "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]] [[package]]
name = "pango" name = "pango"
version = "0.7.0" version = "0.7.0"
@ -344,43 +418,37 @@ dependencies = [
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.22" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.32" version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [ dependencies = [
"unicode-xid", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.10" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [ dependencies = [
"proc-macro2", "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]] [[package]]
name = "rs" name = "rs"
version = "0.1.0" version = "0.1.0"
@ -395,27 +463,53 @@ dependencies = [
"glib-sys", "glib-sys",
"gtk", "gtk",
"gtk-sys", "gtk-sys",
"indexmap",
"maplit", "maplit",
"regex",
"serde", "serde",
"serde_yaml", "serde_yaml",
"xkbcommon", "xkbcommon",
"zbus",
"zvariant",
"zvariant_derive",
] ]
[[package]] [[package]]
name = "serde" name = "ryu"
version = "1.0.130" version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[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.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.130" version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_repr"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -424,25 +518,25 @@ dependencies = [
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.8.21" version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af" checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc"
dependencies = [ dependencies = [
"dtoa",
"indexmap", "indexmap",
"ryu",
"serde", "serde",
"yaml-rust", "yaml-rust",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.82" version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-xid", "unicode-ident",
] ]
[[package]] [[package]]
@ -454,6 +548,21 @@ dependencies = [
"unicode-width", "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-ident"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.9" version = "0.1.9"
@ -461,10 +570,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]] [[package]]
name = "unicode-xid" name = "void"
version = "0.2.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]] [[package]]
name = "winapi" name = "winapi"
@ -506,3 +615,56 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [ dependencies = [
"linked-hash-map", "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" path = "@path@/examples/test_layout.rs"
[features] [features]
gio_v0_5 = [] glib_v0_14 = []
gtk_v0_5 = []
rustc_less_1_36 = []
# Dependencies which don't change based on build flags # 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] [dependencies]
maplit = "1.0.*" maplit = "1.0.*"
serde = { version = "1.0.*", features = ["derive"] } serde = { version = "1.0.*", features = ["derive"] }

View File

@ -86,4 +86,4 @@ It's strongly recommended to support:
Developing Developing
---------- ----------
See [`doc/hacking.md`](doc/hacking.md) for this copy, or the [official documentation](https://developer.puri.sm/projects/squeekboard/) for the current release. See [`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,4 +1,31 @@
/* 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 { sq_view.pin sq_button {
border-radius: 0px; border-radius: 0px;

206
data/keyboards/fr+bepo.yaml Normal file
View File

@ -0,0 +1,206 @@
---
outlines:
small: { width: 50, height: 30 }
default: { width: 35.33, height: 52 }
altline: { width: 35.33, height: 52 }
wide: { width: 50, height: 52 }
spaceline: { width: 150, height: 52 }
special: { width: 35.33, height: 52 }
views:
base:
- "b é p o w v d l j z"
- "a u i e c t s r n m"
- "Shift_L y x k q g h f period BackSpace"
- "show_numbers preferences space show_eschars Return"
upper:
- "B É P O W V D L J Z"
- "A U I E C T S R N M"
- "Shift_L Y X K Q G H F - BackSpace"
- "show_numbers preferences space colon show_eschars Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars Return"
buttons:
F1:
outline: "action"
keysym: "F1"
F2:
outline: "action"
keysym: "F2"
F3:
outline: "action"
keysym: "F3"
F4:
outline: "action"
keysym: "F4"
F5:
outline: "action"
keysym: "F5"
F6:
outline: "action"
keysym: "F6"
F7:
outline: "action"
keysym: "F7"
F8:
outline: "action"
keysym: "F8"
F9:
outline: "action"
keysym: "F9"
F10:
outline: "action"
keysym: "F10"
F11:
outline: "action"
keysym: "F11"
F12:
outline: "action"
keysym: "F12"
Esc:
outline: "action"
keysym: "Escape"
Tab:
outline: "action"
keysym: "Tab"
Del:
outline: "action"
keysym: "Delete"
Insert:
outline: "action"
keysym: "Insert"
Menu:
outline: "action"
keysym: "Menu"
Pause:
outline: "action"
keysym: "Pause"
Break:
outline: "action"
keysym: "Break"
Home:
outline: "small"
keysym: "Home"
End:
outline: "small"
keysym: "End"
PgUp:
outline: "small"
keysym: "Page_Up"
PgDn:
outline: "small"
keysym: "Page_Down"
"↑":
outline: "small"
keysym: "Up"
"↓":
outline: "small"
keysym: "Down"
"←":
outline: "small"
keysym: "Left"
"→":
outline: "small"
keysym: "Right"
Up:
label: "↑"
outline: "action"
keysym: "Up"
Left:
label: "←"
outline: "action"
keysym: "Left"
Down:
label: "↓"
outline: "action"
keysym: "Down"
Right:
label: "→"
outline: "action"
keysym: "Right"
Ctrl:
modifier: "Control"
outline: "small"
label: "Ctrl"
Alt:
modifier: "Alt"
outline: "small"
label: "Alt"
period:
outline: "special"
text: "."
slash:
outline: "special"
text: "/"
show_actions:
action:
set_view: "actions"
outline: "special"
label: ">_"
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "âÂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

View File

@ -0,0 +1,89 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 100, height: 42 }
spaceline: { width: 205, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "b é p o w v d l j z"
- "a u i e c t s r n m"
- "Shift_L y x k q g h f . BackSpace"
- "show_numbers preferences space show_eschars Return"
upper:
- "B É P O W V D L J Z"
- "A U I E C T S R N M"
- "Shift_L Y X K Q G H F - BackSpace"
- "show_numbers preferences space colon show_eschars Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "âÂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

78
data/keyboards/ge.yaml Normal file
View File

@ -0,0 +1,78 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 62, height: 52 }
spaceline: { width: 142, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "ქ წ ე რ ტ უ ი ო პ"
- "ა ს დ ფ გ ჰ ჯ კ ლ"
- "Shift_L ზ ხ ც ვ ბ ნ მ BackSpace"
- "show_numbers preferences space period Return"
upper:
- "ქ ჭ ე ღ თ უ ი ო პ"
- "ა შ დ ფ გ ჰ ჟ კ ლ"
- "Shift_L ძ ხ ჩ ვ ბ ნ მ BackSpace"
- "show_numbers preferences space period Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "! @ # $ % ^ & * ( )"
- "show_symbols - ' \" colon ; , ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "+ ÷ = / _ € £ ¥ ₾"
- "~ ` | · √ π τ ° { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space period Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: "erase"
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "ABC"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
period:
outline: "special"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

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

View File

@ -30,10 +30,10 @@ views:
- "show_numbers \\ / < > = [ ] BackSpace" - "show_numbers \\ / < > = [ ] BackSpace"
- "show_letters show_eschars preferences space ? . Return" - "show_letters show_eschars preferences space ? . Return"
eschars: eschars:
- "á é í ó ú Á É Í Ó Ú" - "è é È É ù ú Ù Ú ò ó"
- è ì ò « » ù ! { }" - á À Á ì í Ì Í Ò Ó"
- "show_numbers \\ / < > = [ ] BackSpace" - "show_numbers “ ” « » ≈ ≠ ‽ BackSpace"
- "show_letters show_eschars preferences space « » Return" - "show_letters show_eschars preferences space , . Return"
buttons: buttons:
Shift_L: Shift_L:

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

@ -0,0 +1,223 @@
---
outlines:
action: { width: 59, height: 46 }
small: { width: 50, height: 22 }
default: { width: 35.33, height: 46 }
altline: { width: 35.33, height: 46 }
wide: { width: 50, height: 46 }
spaceline: { width: 110, height: 46 }
special: { width: 35.33, height: 46 }
views:
base:
- "Ctrl Alt Tabsmall ↑ ↓ ← →"
- "b é p o w v d l j z"
- "a u i e c t s r n m"
- "Shift_L y x k q g h f period BackSpace"
- "show_numbers preferences slash space show_eschars show_actions Return"
upper:
- "Ctrl Alt Tabsmall PgUp PgDn Home End"
- "B É P O W V D L J Z"
- "A U I E C T S R N M"
- "Shift_L Y X K Q G H F - BackSpace"
- "show_numbers preferences space colon show_eschars show_actions Return"
numbers:
- "Ctrl Alt Tabsmall ↑ ↓ ← →"
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars show_actions Return"
symbols:
- "Ctrl Alt Tabsmall ↑ ↓ ← →"
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars show_actions Return"
eschars:
- "Ctrl Alt Tabsmall ↑ ↓ ← →"
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars show_actions Return"
actions:
- "Ctrl Alt PgUp PgDn Home End"
- "F1 F2 F3 F4 F5 F6"
- "F7 F8 F9 F10 F11 F12"
- "Esc Tab Pause Insert Up Del"
- "show_letters Menu Break Left Down Right"
buttons:
F1:
outline: "action"
keysym: "F1"
F2:
outline: "action"
keysym: "F2"
F3:
outline: "action"
keysym: "F3"
F4:
outline: "action"
keysym: "F4"
F5:
outline: "action"
keysym: "F5"
F6:
outline: "action"
keysym: "F6"
F7:
outline: "action"
keysym: "F7"
F8:
outline: "action"
keysym: "F8"
F9:
outline: "action"
keysym: "F9"
F10:
outline: "action"
keysym: "F10"
F11:
outline: "action"
keysym: "F11"
F12:
outline: "action"
keysym: "F12"
Esc:
outline: "action"
keysym: "Escape"
Tab:
outline: "action"
keysym: "Tab"
Tabsmall:
outline: "small"
keysym: "Tab"
label: "Tab"
Del:
outline: "action"
keysym: "Delete"
Insert:
outline: "action"
keysym: "Insert"
Menu:
outline: "action"
keysym: "Menu"
Pause:
outline: "action"
keysym: "Pause"
Break:
outline: "action"
keysym: "Break"
Home:
outline: "small"
keysym: "Home"
End:
outline: "small"
keysym: "End"
PgUp:
outline: "small"
keysym: "Page_Up"
PgDn:
outline: "small"
keysym: "Page_Down"
"↑":
outline: "small"
keysym: "Up"
"↓":
outline: "small"
keysym: "Down"
"←":
outline: "small"
keysym: "Left"
"→":
outline: "small"
keysym: "Right"
Up:
label: "↑"
outline: "action"
keysym: "Up"
Left:
label: "←"
outline: "action"
keysym: "Left"
Down:
label: "↓"
outline: "action"
keysym: "Down"
Right:
label: "→"
outline: "action"
keysym: "Right"
Ctrl:
modifier: "Control"
outline: "small"
label: "Ctrl"
Alt:
modifier: "Alt"
outline: "small"
label: "Alt"
period:
outline: "special"
text: "."
slash:
outline: "special"
text: "/"
show_actions:
action:
set_view: "actions"
outline: "special"
label: ">_"
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "wide"
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,227 @@
---
outlines:
action: { width: 90, height: 37 }
small: { width: 67.4, height: 22 }
default: { width: 54, height: 37 }
altline: { width: 81, height: 37 }
wide: { width: 100, height: 37 }
spaceline: { width: 110, height: 37 }
special: { width: 54, height: 37 }
views:
base:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "b é p o w v d l j z"
- "a u i e c t s r n m"
- "Shift_L y x k q g h f period BackSpace"
- "show_numbers preferences space slash show_eschars show_actions Return"
upper:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "B É P O W V D L J Z"
- "A U I E C T S R N M"
- "Shift_L Y X K Q G H F - BackSpace"
- "show_numbers preferences space colon show_eschars show_actions Return"
numbers:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars show_actions Return"
symbols:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars show_actions Return"
eschars:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars show_actions Return"
actions:
- "EscSmall TabSmall Ctrl Alt PgUp PgDn Home End"
- "F1 F2 F3 F4 F5 F6"
- "F7 F8 F9 F10 F11 F12"
- "Esc Tab Pause Insert Up Del"
- "show_letters Menu Break Left Down Right"
buttons:
F1:
outline: "action"
keysym: "F1"
F2:
outline: "action"
keysym: "F2"
F3:
outline: "action"
keysym: "F3"
F4:
outline: "action"
keysym: "F4"
F5:
outline: "action"
keysym: "F5"
F6:
outline: "action"
keysym: "F6"
F7:
outline: "action"
keysym: "F7"
F8:
outline: "action"
keysym: "F8"
F9:
outline: "action"
keysym: "F9"
F10:
outline: "action"
keysym: "F10"
F11:
outline: "action"
keysym: "F11"
F12:
outline: "action"
keysym: "F12"
Esc:
outline: "action"
keysym: "Escape"
EscSmall:
outline: "small"
keysym: "Escape"
label: "Esc"
Tab:
outline: "action"
keysym: "Tab"
TabSmall:
outline: "small"
keysym: "Tab"
label: "Tab"
Del:
outline: "action"
keysym: "Delete"
Insert:
outline: "action"
keysym: "Insert"
Menu:
outline: "action"
keysym: "Menu"
Pause:
outline: "action"
keysym: "Pause"
Break:
outline: "action"
keysym: "Break"
Home:
outline: "small"
keysym: "Home"
End:
outline: "small"
keysym: "End"
PgUp:
outline: "small"
keysym: "Page_Up"
PgDn:
outline: "small"
keysym: "Page_Down"
"↑":
outline: "small"
keysym: "Up"
"↓":
outline: "small"
keysym: "Down"
"←":
outline: "small"
keysym: "Left"
"→":
outline: "small"
keysym: "Right"
Up:
label: "↑"
outline: "action"
keysym: "Up"
Left:
label: "←"
outline: "action"
keysym: "Left"
Down:
label: "↓"
outline: "action"
keysym: "Down"
Right:
label: "→"
outline: "action"
keysym: "Right"
Ctrl:
modifier: "Control"
outline: "small"
label: "Ctrl"
Alt:
modifier: "Alt"
outline: "small"
label: "Alt"
period:
outline: "special"
text: "."
slash:
outline: "special"
text: "/"
show_actions:
action:
set_view: "actions"
outline: "special"
label: ">_"
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "âÂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

View File

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

View File

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

203
debian/changelog vendored
View File

@ -1,3 +1,206 @@
squeekboard (1.19.0-1) experimental; urgency=medium
[ Dorota Czaplejewicz ]
* docs: Describe layouts
* glib: Fix import
* state: Record layout choice
* doc: Describe how to control debugging mode
* state: Include layout choice in visible outcome
* state: Store layout override
* state: Decide panel arrangement
* state: Use dummy layout command
* state: Use IM hint and purpose for layout selection
* main: Dry-load layout in response to the layout command
* state: Become the source of layout choice
* cargo: Halt overzealous upgrader
* ci: Use bookworm for online builds
* build: Generate files before compiling sources
* gtk: Persist panel state
* build: Update Cargo.lock
[ Luke Luo ]
* Add Chinese (China) translation
[ Éloi Rivard ]
* Add French translation
[ Laurent Laffont ]
* Add FR+Bépo layout
[ Bytez ]
* Fixed missing characters in eschars view in italian layout.
* Replaced foreign characters.
[ Aleksandr Melman ]
* Add Russian translation
[ skysphr ]
* Added Georgian layout
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Wed, 06 Jul 2022 06:10:40 +0000
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 squeekboard (1.15.0-1) experimental; urgency=medium
[ Khaled Eldoheiri ] [ Khaled Eldoheiri ]

2
debian/control vendored
View File

@ -20,10 +20,10 @@ Build-Depends:
librust-gtk+v3-22-dev (>= 0.5), librust-gtk+v3-22-dev (>= 0.5),
librust-gtk-sys-dev, librust-gtk-sys-dev,
librust-maplit-1-dev (>= 1.0), librust-maplit-1-dev (>= 1.0),
librust-regex-1-dev (>= 1.1),
librust-serde-derive-1-dev (>= 1.0), librust-serde-derive-1-dev (>= 1.0),
librust-serde-yaml-0.8-dev (>= 0.8), librust-serde-yaml-0.8-dev (>= 0.8),
librust-xkbcommon-0.4+wayland-dev (>= 0.4), librust-xkbcommon-0.4+wayland-dev (>= 0.4),
librust-zbus-dev (>=1.0),
libwayland-dev (>= 1.16), libwayland-dev (>= 1.16),
lsb-release, lsb-release,
python3, 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) distrel := $(shell lsb_release --codename --short)
ifneq (,$(filter $(distrel),buster amber)) ifneq (,$(filter $(distrel),sid))
legacy = true newer = true
else else
legacy = false newer = false
endif endif
%: %:
@ -38,6 +38,6 @@ endif
# causing Cargo to refuse to build with a crates.io copy # causing Cargo to refuse to build with a crates.io copy
override_dh_auto_configure: override_dh_auto_configure:
[ ! -f Cargo.lock ] || rm Cargo.lock [ ! -f Cargo.lock ] || rm Cargo.lock
dh_auto_configure -- -Dlegacy=$(legacy) dh_auto_configure -- -Dnewer=$(newer) -Donline=false
override_dh_autoreconf: override_dh_autoreconf:

View File

@ -90,6 +90,14 @@ Layouts can be selected using the GNOME Settings application.
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]" $ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]"
``` ```
### Debugging mode
Squeekboard prints some information on standard output by default. To get deep debugging information, it can also print all changes in (some of) its internal state. Those logs are most useful when reporting hard to catch issues, and can be enabled using the following command:
```
busctl set-property --user sm.puri.SqueekDebug /sm/puri/SqueekDebug sm.puri.SqueekDebug Enabled b true
```
### Environment Variables ### Environment Variables
Besides the environment variables supported by GTK and [GLib](https://docs.gtk.org/glib/running.html) applications Besides the environment variables supported by GTK and [GLib](https://docs.gtk.org/glib/running.html) applications
@ -102,6 +110,17 @@ contain a comma separated list of:
Coding 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 ### Project structure
Rust modules should be split into 2 categories: libraries, and user interface. They differ in the way they do error handling. Rust modules should be split into 2 categories: libraries, and user interface. They differ in the way they do error handling.
@ -205,6 +224,7 @@ While the file is not actually used, it's a good idea to save the config in case
``` ```
cd squeekboard-build cd squeekboard-build
.../squeekboard-source/cargo.sh update
ninja test ninja test
cp ./Cargo.lock .../squeekboard-source cp ./Cargo.lock .../squeekboard-source
``` ```

View File

@ -1,13 +1,6 @@
Welcome to squeekboard's documentation! Welcome to squeekboard's documentation!
======================================= =======================================
Contents
--------
* [Tutorial](tutorial.md)
* [Contributing](hacking.md)
* [Switching views](views.md)
Introduction Introduction
------------ ------------
@ -18,9 +11,9 @@ Layouts
Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The [tutorial](tutorial.md) explains the process in detail. Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The [tutorial](tutorial.md) explains the process in detail.
Layouts are created using a text-based format, based on YAML. Layouts are created using a [text-based format, based on YAML](layouts.md).
TODO: Provide a description of the format. ### Views
Squeekboard layouts are separated into *views* and use a *room metaphor* to [switch views](views.md). Squeekboard layouts are separated into *views* and use a *room metaphor* to [switch views](views.md).
@ -28,3 +21,7 @@ Contributions
------------- -------------
Anyone is free to modify *squeekboard*. See the [contributing document](hacking.md). 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).

121
doc/layouts.md Normal file
View File

@ -0,0 +1,121 @@
Layouts
=====
Squeekboard is composed of multiple layouts, several for each language, multiplied by each hint.
Layouts live in the "keyboards" directory.
Hints
-------
The currently supported hints are: default, "email", "emoji", "number', "pin", "terminal", and "url".
Each directory in "keyboards" is named after a hint, with the "keyboards" directory itself taking the role of default.
Languages/scripts
-----------------------
Each hint directory contains multiple layout files. A single language will be composed of multiple files, with names starting with the same text. The language names are taken from iso639-3. An example is "gr".
After the language name optionally comes a "+" and an indication of the variant. For example, "it+fur".
Squeekboard will look for those based on the currently selected layout in Gnome Control Center.
Then, there's an optional part "_wide", which Squeekboard will try to use if the current display is rather wide. Example: "us+colemak_wide" or "us_wide".
Finally, the file name ends with ".yaml", e.g. "jp+kana_wide.yaml".
Together with hint information, this gives a complete path to the layout like this: "keyboards/terminal/fr_wide.yaml" or "keyboards/cz+qwerty.yaml".
Layout syntax
------------------
The layout file follows the YAML syntax, with specific meanings given to sections.
### Outlines
The "outlines" dictionary controls the widths and heights of buttons.
```
outlines:
default: { width: 32, height: 52 }
```
The width and height numbers are not in pixels, but rather they are proportionally scaled to fit the panel size.
There may be any number of outlines, but there are some special names:
- "default" applies to every button unless explicitly changed. It should be used for buttons that emit text
- "altline", "wide" have own color scheme, should be used for buttons which cause view changes
- "special" has own color scheme, to be used for confirmations like enter.
### Views
The "views" dictionary contains the actual views and positions of buttons.
```
views:
base:
- "q w e r t y u i o p å"
```
Squeekboard's layouts consist of multiple views, of which only one is visible at a time. Different views may contain different or the same buttons, more or fewer buttons, but each layout is independent. They are *not* shift levels there is no concept of "shift" in Squeekboard. View selection is also not dependent on modifiers.
There is only one special view "base". Views and view switching are described in detail in the [views](views.md) document.
Views in Squeekboard are based on rows. The first row comes near the top of the panel, the next one below, and so on.
```
- "Q W E R T Y U I O P Å"
- "upper z x c v b n m BackSpace"
```
Each row is a single string, and button names are separated by spaces. In left-to-right languages, the panel will be laid out just like the view code. CAUTION: buttons are placed on the panel left-to-right, starting from the earliest position in the string. That may not display great in your text editor when you use right-to-left characters as button names.
#### Button names in rows
Unicode characters are supported in the row string, so it's easy to use the correct name for most of them. However, the layout code is still YAML, which excludes certain characters: the space " ", the backslash "\", the double quote `"`. Those must use a replacement name.
Similarly, buttons that do not emit characters must have some names.
### Buttons
The buttons section describes what the button looks like and what it does.
```
BackSpace:
outline: altline
icon: "edit-clear-symbolic"
action: erase
```
Each entry in the "buttons" dictionary describes some button already present in one of the "views" rows. In the above example, it's "BackSpace".
The button description can have a number of components, each optional. For details, see
- "outline" selects which entry from the "outlines" section to use to draw this button,
- "label" is what should be displayed on the button, if its name is unsuitable,
- "icon" is the name of the svg icon to use instead of a label (icons are builtin, see the "data/icons" directory),
- "text" is the text to submit when the button is clicked if the name of the button is not suitable,
- "keysym" is the emulated keyboard keysym to send instead of sending text. Its use is discouraged: Squeekboard will automatically send keysyms if it detects that the receiving application does not accept text.
- "modifier" makes the button set an emulated keyboard modifier. The use of this is discouraged, and never needed for entering text.
- "action" sets aside the button for special actions like view switching
#### Action
```
action:
set_view: "numbers"
```
The "action" property has multiple forms.
- "erase" will erase the position behind the cursor,
- "show_preferences" will open the language selection popup,
- "set_view" simply switches to a view,
- "lock_view" switches to a view for a moment.
The two switching modes are better described in the [views](views.md) document.
Sources
----------
The sources, where all this is documented and up to date are in "src/data/parsing.rs". The reference documentation for the `rs::data::parsing::Layout` structure is the main place to look at.

View File

@ -9,14 +9,12 @@ So at least I will try to start writing a short how-to here and edit this post a
## Creating a new layout ## Creating a new layout
Creating a layout is easy. You don't need to recompile things, just edit and test. It's easiest to start with an existing layout. Creating a layout is easy. You don't need to recompile things, just edit and test. It's easiest to start with an existing layout, with the [layouts](layouts.md) documentation in hand.
### Get one of the existing keyboard layouts ### Get one of the existing keyboard layouts
* You can get one of the keyboards from the squeekboard git repository : [https://source.puri.sm/Librem5/squeekboard](https://source.puri.sm/Librem5/squeekboard) * You can get one of the keyboards from the squeekboard git repository : [https://source.puri.sm/Librem5/squeekboard](https://source.puri.sm/Librem5/squeekboard)
* The keyboard layouts are located in the subdirectory [`data/keyboards/`](https://source.puri.sm/Librem5/squeekboard/-/tree/master/data/keyboards) in the `.yaml` files * The keyboard layouts are located in the subdirectory [`data/keyboards/`](https://source.puri.sm/Librem5/squeekboard/-/tree/master/data/keyboards) in the `.yaml` files
* Take a look and try to understand them :slight_smile:
### Creating the keyboard layout ### Creating the keyboard layout

View File

@ -35,6 +35,7 @@
#include "eekboard/eekboard-context-service.h" #include "eekboard/eekboard-context-service.h"
#include "src/layout.h" #include "src/layout.h"
#include "src/popover.h"
#include "src/submission.h" #include "src/submission.h"
#define LIBFEEDBACK_USE_UNSTABLE_API #define LIBFEEDBACK_USE_UNSTABLE_API
@ -48,6 +49,8 @@ typedef struct _EekGtkKeyboardPrivate
struct render_geometry render_geometry; // mutable struct render_geometry render_geometry; // mutable
EekboardContextService *eekboard_context; // unowned reference EekboardContextService *eekboard_context; // unowned reference
struct squeek_popover *popover; // shared reference
struct squeek_state_manager *state_manager; // shared reference
struct submission *submission; // unowned reference struct submission *submission; // unowned reference
struct squeek_layout_state *layout; // unowned struct squeek_layout_state *layout; // unowned
@ -55,6 +58,8 @@ typedef struct _EekGtkKeyboardPrivate
GdkEventSequence *sequence; // unowned reference GdkEventSequence *sequence; // unowned reference
LfbEvent *event; LfbEvent *event;
gulong kb_signal;
} EekGtkKeyboardPrivate; } EekGtkKeyboardPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA) G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA)
@ -116,15 +121,6 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
return FALSE; return FALSE;
} }
// Units of virtual pixels size
static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 540) {
return ARRANGEMENT_KIND_BASE;
}
return ARRANGEMENT_KIND_WIDE;
}
static void static void
eek_gtk_keyboard_real_size_allocate (GtkWidget *self, eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
GtkAllocation *allocation) GtkAllocation *allocation)
@ -132,15 +128,6 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self); EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
EekGtkKeyboardPrivate *priv = EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (keyboard); eek_gtk_keyboard_get_instance_private (keyboard);
// check if the change would switch types
enum squeek_arrangement_kind new_type = get_type(
(uint32_t)(allocation->width - allocation->x),
(uint32_t)(allocation->height - allocation->y));
if (priv->layout->arrangement != new_type) {
priv->layout->arrangement = new_type;
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(priv->eekboard_context, priv->layout, time);
}
if (priv->renderer) { if (priv->renderer) {
set_allocation_size (keyboard, priv->keyboard->layout, set_allocation_size (keyboard, priv->keyboard->layout,
@ -156,6 +143,7 @@ on_event_triggered (LfbEvent *event,
GAsyncResult *res, GAsyncResult *res,
gpointer unused) gpointer unused)
{ {
(void)unused;
g_autoptr (GError) err = NULL; g_autoptr (GError) err = NULL;
if (!lfb_event_trigger_feedback_finish (event, res, &err)) { if (!lfb_event_trigger_feedback_finish (event, res, &err)) {
@ -186,7 +174,7 @@ static void drag(EekGtkKeyboard *self,
squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout, squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission, priv->submission,
x, y, priv->render_geometry.widget_to_layout, time, x, y, priv->render_geometry.widget_to_layout, time,
priv->eekboard_context, self); priv->popover, priv->state_manager, self);
} }
static void release(EekGtkKeyboard *self, guint32 time) static void release(EekGtkKeyboard *self, guint32 time)
@ -197,7 +185,7 @@ static void release(EekGtkKeyboard *self, guint32 time)
} }
squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout, squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission, priv->render_geometry.widget_to_layout, time, priv->submission, priv->render_geometry.widget_to_layout, time,
priv->eekboard_context, self); priv->popover, priv->state_manager, self);
} }
static gboolean static gboolean
@ -307,12 +295,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 static void
eek_gtk_keyboard_dispose (GObject *object) eek_gtk_keyboard_dispose (GObject *object)
{ {
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (object); EekGtkKeyboard *self = EEK_GTK_KEYBOARD (object);
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); 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) { if (priv->renderer) {
eek_renderer_free(priv->renderer); eek_renderer_free(priv->renderer);
priv->renderer = NULL; priv->renderer = NULL;
@ -397,13 +392,15 @@ on_notify_keyboard (GObject *object,
GtkWidget * GtkWidget *
eek_gtk_keyboard_new (EekboardContextService *eekservice, eek_gtk_keyboard_new (EekboardContextService *eekservice,
struct submission *submission, struct submission *submission,
struct squeek_layout_state *layout) struct squeek_state_manager *state_manager,
struct squeek_popover *popover)
{ {
EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL)); EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL));
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret); EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret);
priv->popover = popover;
priv->eekboard_context = eekservice; priv->eekboard_context = eekservice;
priv->submission = submission; priv->submission = submission;
priv->layout = layout; priv->state_manager = state_manager;
priv->renderer = NULL; priv->renderer = NULL;
// This should really be done on initialization. // This should really be done on initialization.
// Before the widget is allocated, // Before the widget is allocated,
@ -418,12 +415,13 @@ eek_gtk_keyboard_new (EekboardContextService *eekservice,
.widget_to_layout = { .widget_to_layout = {
.origin_x = 0, .origin_x = 0,
.origin_y = 0, .origin_y = 0,
.scale = 1, .scale_x = 1,
.scale_y = 1,
}, },
}; };
priv->render_geometry = initial_geometry; priv->render_geometry = initial_geometry;
g_signal_connect (eekservice, priv->kb_signal = g_signal_connect (eekservice,
"notify::keyboard", "notify::keyboard",
G_CALLBACK(on_notify_keyboard), G_CALLBACK(on_notify_keyboard),
ret); ret);

View File

@ -30,6 +30,8 @@
#include "eek/eek-renderer.h" #include "eek/eek-renderer.h"
#include "eek/eek-types.h" #include "eek/eek-types.h"
#include "src/main.h"
#include "src/popover.h"
struct submission; struct submission;
struct squeek_layout_state; struct squeek_layout_state;
@ -48,7 +50,7 @@ struct _EekGtkKeyboardClass
gpointer pdummy[24]; gpointer pdummy[24];
}; };
GtkWidget *eek_gtk_keyboard_new (EekboardContextService *eekservice, struct submission *submission, struct squeek_layout_state *layout); GtkWidget *eek_gtk_keyboard_new (EekboardContextService *eekservice, struct submission *submission, struct squeek_state_manager *state_manager, struct squeek_popover *popover);
void eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self); void eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self);
G_END_DECLS G_END_DECLS

View File

@ -219,7 +219,7 @@ eek_renderer_render_keyboard (EekRenderer *self,
cairo_save(cr); cairo_save(cr);
cairo_translate (cr, geometry.widget_to_layout.origin_x, geometry.widget_to_layout.origin_y); 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_draw_layout_base_view(keyboard->layout, self, cr);
squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission); squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);

View File

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

View File

@ -55,15 +55,15 @@ static guint signals[LAST_SIGNAL] = { 0, };
*/ */
struct _EekboardContextService { struct _EekboardContextService {
GObject parent; GObject parent;
struct squeek_layout_state *layout; // Unowned struct squeek_state_manager *state_manager; // shared reference
LevelKeyboard *keyboard; // currently used keyboard LevelKeyboard *keyboard; // currently used keyboard
GSettings *settings; // Owned reference GSettings *settings; // Owned reference
// Maybe TODO: it's used only for fetching layout type. /// Needed for keymap changes after keyboard updates.
// Maybe let UI push the type to this structure? // TODO: can the main loop access submission to change the key maps instead?
ServerContextService *ui; // unowned reference // This should probably land together with passing buttons through state,
/// Needed for keymap changes after keyboard updates // to avoid race conditions between setting buttons and key maps.
struct submission *submission; // unowned struct submission *submission; // unowned
}; };
@ -126,24 +126,7 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
g_variant_unref(inputs); g_variant_unref(inputs);
} }
void void eekboard_context_service_set_layout(EekboardContextService *context, struct squeek_layout *layout, uint32_t timestamp) {
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state, uint32_t timestamp) {
gchar *layout_name = state->layout_name;
gchar *overlay_name = state->overlay_name;
// try to get the best keyboard layout
if (layout_name == NULL) {
layout_name = "us";
}
// overlay is "Normal" for most layouts, we will only look for "terminal" in rust code.
// for now just avoid passing a null pointer
if (overlay_name == NULL) {
overlay_name = ""; // fallback to Normal
}
// generic part follows
struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement, state->purpose, overlay_name);
LevelKeyboard *keyboard = level_keyboard_new(layout); LevelKeyboard *keyboard = level_keyboard_new(layout);
// set as current // set as current
LevelKeyboard *previous_keyboard = context->keyboard; LevelKeyboard *previous_keyboard = context->keyboard;
@ -169,17 +152,7 @@ static void eekboard_context_service_update_settings_layout(EekboardContextServi
settings_get_layout(context->settings, settings_get_layout(context->settings,
&keyboard_type, &keyboard_layout); &keyboard_type, &keyboard_layout);
if (g_strcmp0(context->layout->layout_name, keyboard_layout) != 0 || context->layout->overlay_name) { squeek_state_send_layout_set(context->state_manager, keyboard_layout, keyboard_type, gdk_event_get_time(NULL));
g_free(context->layout->overlay_name);
context->layout->overlay_name = NULL;
if (keyboard_layout) {
g_free(context->layout->layout_name);
context->layout->layout_name = g_strdup(keyboard_layout);
}
// This must actually update the UI.
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
}
} }
static gboolean static gboolean
@ -297,50 +270,18 @@ eekboard_context_service_get_keyboard (EekboardContextService *context)
return context->keyboard; return context->keyboard;
} }
void eekboard_context_service_set_hint_purpose(EekboardContextService *context, EekboardContextService *eekboard_context_service_new(struct squeek_state_manager *state_manager)
uint32_t hint, uint32_t purpose)
{
if (context->layout->hint != hint || context->layout->purpose != purpose) {
context->layout->hint = hint;
context->layout->purpose = purpose;
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
}
}
void
eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) {
if (g_strcmp0(context->layout->overlay_name, name)) {
g_free(context->layout->overlay_name);
context->layout->overlay_name = g_strdup(name);
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
}
}
const char*
eekboard_context_service_get_overlay(EekboardContextService *context) {
return context->layout->overlay_name;
}
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state)
{ {
EekboardContextService *context = g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL); EekboardContextService *context = g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
context->layout = state; context->state_manager = state_manager;
eekboard_context_service_update_settings_layout(context); eekboard_context_service_update_settings_layout(context);
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
return context; return context;
} }
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) { void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
context->submission = submission; context->submission = submission;
if (context->submission) { if (context->submission && context->keyboard) {
uint32_t time = gdk_event_get_time(NULL); uint32_t time = gdk_event_get_time(NULL);
submission_use_layout(context->submission, context->keyboard->layout, time); submission_use_layout(context->submission, context->keyboard->layout, time);
} }
} }
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
context->ui = ui;
}

View File

@ -24,6 +24,7 @@
#include "src/submission.h" #include "src/submission.h"
#include "src/layout.h" #include "src/layout.h"
#include "src/main.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h"
@ -37,19 +38,13 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(EekboardContextService, eekboard_context_service, EEKBOARD, CONTEXT_SERVICE, GObject) G_DECLARE_FINAL_TYPE(EekboardContextService, eekboard_context_service, EEKBOARD, CONTEXT_SERVICE, GObject)
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state); EekboardContextService *eekboard_context_service_new(struct squeek_state_manager *state_manager);
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission); 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); void eekboard_context_service_destroy (EekboardContextService *context);
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context); LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
void eekboard_context_service_set_keymap(EekboardContextService *context, void eekboard_context_service_set_keymap(EekboardContextService *context,
const LevelKeyboard *keyboard); 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 G_END_DECLS
#endif /* EEKBOARD_CONTEXT_SERVICE_H */ #endif /* EEKBOARD_CONTEXT_SERVICE_H */

View File

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

View File

@ -7,9 +7,13 @@ option('tests',
type: 'boolean', value: true, type: 'boolean', value: true,
description: 'Whether to compile unit tests') description: 'Whether to compile unit tests')
option('legacy', option('newer',
type: 'boolean', value: false, 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', option('strict',
type: 'boolean', value: true, type: 'boolean', value: true,

View File

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

View File

@ -1,2 +1,2 @@
data/popup.ui data/popover.ui
data/sm.puri.Squeekboard.desktop.in.in 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ö"

47
po/fr.po Normal file
View File

@ -0,0 +1,47 @@
# French translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# FULL NAME <EMAIL@ADDRESS>, 2022.
# Éloi Rivard <eloi.rivard@nubla.fr>, 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-28 15:05+0000\n"
"PO-Revision-Date: 2022-05-25 16:34+0200\n"
"Last-Translator: Éloi Rivard <eloi.rivard@nubla.fr>\n"
"Language-Team: French <gnomefr@traduc.org>\n"
"Language: fr\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 "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 du clavier"
#: 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 "Clavier virtuel"
#: data/sm.puri.Squeekboard.desktop.in.in:5
msgid "An on screen virtual keyboard"
msgstr "Un clavier virtuel affiché à lécran"

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"

47
po/ru.po Normal file
View File

@ -0,0 +1,47 @@
# Russian translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Aleksandr Melman <Alexmelman88@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-06-14 15:23+0000\n"
"PO-Revision-Date: 2022-06-16 14:38+0300\n"
"Last-Translator: Aleksandr Melman <Alexmelman88@gmail.com>\n"
"Language-Team: Russian <gnome-cyr@gnome.org>\n"
"Language: ru\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%10==1 && n%100!=11 ? 0 : n%10>=2 "
"&& n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\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 "Виртуальная клавиатура на экране"

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 ""
"Екранна віртуальна клавіатура"

45
po/zh_CN.po Normal file
View File

@ -0,0 +1,45 @@
# Chinese (China) translation for squeekboard.
# Copyright (C) 2022 squeekboard's COPYRIGHT HOLDER
# This file is distributed under the same license as the squeekboard package.
# Luke Luo <2164381336@qq.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-21 07:28+0000\n"
"PO-Revision-Date: 2022-04-23 20:30+0800\n"
"Last-Translator: Luke Luo <njlyf2011@hotmail.com>\n"
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
"Language: zh_CN\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 "终端"
#: 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 "一个虚拟屏幕键盘"

23
src/actors/mod.rs Normal file
View File

@ -0,0 +1,23 @@
/* Copyright (C) 2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Actors are parts of Squeekboard containing state independent from the main application state.
Because main application state is meant to be immutable,
it cannot be referenced directly by pieces of logic
interacting with the environment.
Such impure logic is split away (actor's logic)
and combined with relevant pieces of state (actor state),
thus preserving the purity (and sometimes simplicity) of the main state.
Actors can communicate with the main state by sending it messages,
and by receiving updates from it.
*/
// TODO: move crate::panel into crate::actors::panel.
// Panel contains state and logic to protect the main state from getting flooded
// with low-level wayland and gtk sizing events.
pub mod popover;

40
src/actors/popover.rs Normal file
View File

@ -0,0 +1,40 @@
/* Copyright (C) 2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! The popover is opened directly by the GTK surface,
without bouncing click events off the main state.
Then it must accurately show which layout has been selected.
It can get the system layout directly from gsettings on open,
but it cannot get the user-selected overlay, because it's stored in state.
To solve this, overlay will be cached in the popover actor,
and updated by main state every time it changes.
*/
pub mod c {
use super::*;
use crate::util::c::Wrapped;
/// The mutable instance of state
pub type Actor = Wrapped<State>;
}
#[derive(Clone)]
pub struct State {
pub overlay: Option<String>,
}
impl State {
pub fn new() -> Self {
Self { overlay: None }
}
}
pub fn set_overlay(
actor: &c::Actor,
overlay: Option<String>,
) {
let actor = actor.clone_ref();
let mut actor = actor.borrow_mut();
actor.overlay = overlay;
}

View File

@ -6,12 +6,30 @@
use std::time::Duration; use std::time::Duration;
use crate::imservice::ContentPurpose;
use crate::layout::ArrangementKind;
use crate::outputs::OutputId;
use crate::panel::PixelSize;
/// The keyboard should hide after this has elapsed to prevent flickering. /// The keyboard should hide after this has elapsed to prevent flickering.
pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200); pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200);
/// Description of parameters which influence panel contents
#[derive(PartialEq, Clone, Debug)]
pub struct Contents {
pub name: String,
pub kind: ArrangementKind,
pub overlay_name: Option<String>,
pub purpose: ContentPurpose,
}
/// The outwardly visible state of visibility /// The outwardly visible state of visibility
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub enum Outcome { pub enum Outcome {
Visible, Visible {
output: OutputId,
height: PixelSize,
contents: Contents,
},
Hidden, 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"#);
}
}

View File

@ -7,64 +7,16 @@
use std::env; use std::env;
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::convert::TryFrom;
use super::{ Error, LoadError }; use super::{ Error, LoadError };
use super::parsing; use super::parsing;
use ::layout::ArrangementKind; use crate::layout;
use ::logging; use crate::layout::ArrangementKind;
use ::util::c::as_str; use crate::logging;
use ::xdg; use crate::xdg;
use ::imservice::ContentPurpose; use crate::imservice::ContentPurpose;
// traits, derives
use ::logging::Warn;
/// Gathers stuff defined in C or called by C
pub mod c {
use super::*;
use std::os::raw::c_char;
#[no_mangle]
pub extern "C"
fn squeek_load_layout(
name: *const c_char, // name of the keyboard
type_: u32, // type like Wide
variant: u32, // purpose variant like numeric, terminal...
overlay: *const c_char, // the overlay (looking for "terminal")
) -> *mut ::layout::Layout {
let type_ = match type_ {
0 => ArrangementKind::Base,
1 => ArrangementKind::Wide,
_ => panic!("Bad enum value"),
};
let name = as_str(&name)
.expect("Bad layout name")
.expect("Empty layout name");
let variant = ContentPurpose::try_from(variant)
.or_print(
logging::Problem::Warning,
"Received invalid purpose value",
)
.unwrap_or(ContentPurpose::Normal);
let overlay_str = as_str(&overlay)
.expect("Bad overlay name")
.expect("Empty overlay name");
let overlay_str = match overlay_str {
"" => None,
other => Some(other),
};
let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str);
let layout = ::layout::Layout::new(layout, kind, variant);
Box::into_raw(Box::new(layout))
}
}
const FALLBACK_LAYOUT_NAME: &str = "us"; const FALLBACK_LAYOUT_NAME: &str = "us";
@ -265,7 +217,7 @@ fn load_layout_data_with_fallback(
kind: ArrangementKind, kind: ArrangementKind,
purpose: ContentPurpose, purpose: ContentPurpose,
overlay: Option<&str>, overlay: Option<&str>,
) -> (ArrangementKind, ::layout::LayoutData) { ) -> (ArrangementKind, layout::LayoutData) {
// Build the path to the right keyboard layout subdirectory // Build the path to the right keyboard layout subdirectory
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR") let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
@ -300,6 +252,17 @@ fn load_layout_data_with_fallback(
panic!("No useful layout found!"); panic!("No useful layout found!");
} }
pub fn load_layout(
name: String,
kind: ArrangementKind,
variant: ContentPurpose,
overlay: Option<String>,
) -> layout::Layout {
let overlay = overlay.as_ref().map(String::as_str);
let (found_kind, layout)
= load_layout_data_with_fallback(&name, kind, variant, overlay);
layout::Layout::new(layout, found_kind, variant)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -4,7 +4,7 @@
/*! Combined module for dealing with layout files */ /*! Combined module for dealing with layout files */
mod loading; pub mod loading;
pub mod parsing; pub mod parsing;
use std::io; use std::io;

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 ::submission::c::Submission as CSubmission;
use glib::translate::FromGlibPtrNone; use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt; use gtk::prelude::WidgetExt;
use std::collections::HashSet; use std::collections::HashSet;
use std::ffi::CStr; use std::ffi::CStr;

View File

@ -29,7 +29,9 @@ use std::time::Instant;
use crate::logging::Warn; use crate::logging::Warn;
/// Type of the sender that waits for external events
type Sender = mpsc::Sender<Event>; type Sender = mpsc::Sender<Event>;
/// Type of the sender that waits for internal state changes
type UISender = glib::Sender<Commands>; type UISender = glib::Sender<Commands>;
/// This loop driver spawns a new thread which updates the state in a loop, /// This loop driver spawns a new thread which updates the state in a loop,
@ -37,7 +39,9 @@ type UISender = glib::Sender<Commands>;
/// It sends outcomes to the glib main loop using a channel. /// It sends outcomes to the glib main loop using a channel.
/// The outcomes are applied by the UI end of the channel in the `main` module. /// The outcomes are applied by the UI end of the channel in the `main` module.
// This could still be reasonably tested, // This could still be reasonably tested,
// by creating a glib::Sender and checking what messages it receives. /// by creating a glib::Sender and checking what messages it receives.
// This can/should be abstracted over Event and Commands,
// so that the C call-ins can be thrown away from here and defined near events.
#[derive(Clone)] #[derive(Clone)]
pub struct Threaded { pub struct Threaded {
thread: Sender, thread: Sender,
@ -106,8 +110,11 @@ mod c {
use super::*; use super::*;
use crate::state::Presence; use crate::state::Presence;
use crate::state::LayoutChoice;
use crate::state::visibility; use crate::state::visibility;
use crate::util;
use crate::util::c::Wrapped; use crate::util::c::Wrapped;
use std::os::raw::c_char;
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
@ -138,4 +145,28 @@ mod c {
sender.send(Event::PhysicalKeyboard(state)) sender.send(Event::PhysicalKeyboard(state))
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager"); .or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
} }
#[no_mangle]
pub extern "C"
fn squeek_state_send_layout_set(
sender: Wrapped<Threaded>,
name: *const c_char,
source: *const c_char,
// TODO: use when synthetic events are needed
_timestamp: u32,
) {
let sender = sender.clone_ref();
let sender = sender.borrow();
let string_or_empty = |v| String::from(
util::c::as_str(v)
.unwrap_or(Some(""))
.unwrap_or("")
);
sender
.send(Event::LayoutChoice(LayoutChoice {
name: string_or_empty(&name),
source: string_or_empty(&source).into(),
}))
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
}
} }

View File

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

View File

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

View File

@ -7,6 +7,8 @@
#include "eek/eek-gtk-keyboard.h" #include "eek/eek-gtk-keyboard.h"
#include "eek/eek-renderer.h" #include "eek/eek-renderer.h"
#include "eek/eek-types.h" #include "eek/eek-types.h"
#include "src/main.h"
#include "src/popover.h"
#include "src/submission.h" #include "src/submission.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h"
@ -40,7 +42,8 @@ void squeek_layout_release(struct squeek_layout *layout,
struct submission *submission, struct submission *submission,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, uint32_t timestamp,
EekboardContextService *manager, struct squeek_popover *popover,
struct squeek_state_manager *state,
EekGtkKeyboard *ui_keyboard); EekGtkKeyboard *ui_keyboard);
void squeek_layout_release_all_only(struct squeek_layout *layout, void squeek_layout_release_all_only(struct squeek_layout *layout,
struct submission *submission, struct submission *submission,
@ -54,7 +57,8 @@ void squeek_layout_drag(struct squeek_layout *layout,
struct submission *submission, struct submission *submission,
double x_widget, double y_widget, double x_widget, double y_widget,
struct transformation widget_to_layout, struct transformation widget_to_layout,
uint32_t timestamp, EekboardContextService *manager, uint32_t timestamp, struct squeek_popover *popover,
struct squeek_state_manager *state,
EekGtkKeyboard *ui_keyboard); EekGtkKeyboard *ui_keyboard);
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission); void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission);
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr); void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);

View File

@ -18,35 +18,42 @@
*/ */
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp;
use std::collections::{ HashMap, HashSet }; use std::collections::{ HashMap, HashSet };
use std::ffi::CString; use std::ffi::CString;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::vec::Vec; use std::vec::Vec;
use ::action::Action; use crate::action::Action;
use ::drawing; use crate::actors;
use ::keyboard::KeyState; use crate::drawing;
use ::logging; use crate::float_ord::FloatOrd;
use ::manager; use crate::keyboard::KeyState;
use ::submission::{ Submission, SubmitData, Timestamp }; use crate::logging;
use ::util::find_max_double; use crate::popover;
use crate::receiver;
use crate::submission::{ Submission, SubmitData, Timestamp };
use crate::util::find_max_double;
use ::imservice::ContentPurpose; use crate::imservice::ContentPurpose;
// Traits // Traits
use std::borrow::Borrow; use std::borrow::Borrow;
use ::logging::Warn; use crate::logging::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
use super::*; use super::*;
use gtk_sys; use crate::receiver;
use std::os::raw::c_void;
use crate::submission::c::Submission as CSubmission; use crate::submission::c::Submission as CSubmission;
use gtk_sys;
use std::ops::{ Add, Sub }; use std::ops::{ Add, Sub };
use std::os::raw::c_void;
use crate::util::CloneOwned;
// The following defined in C // The following defined in C
#[repr(transparent)] #[repr(transparent)]
@ -117,28 +124,30 @@ pub mod c {
pub struct Transformation { pub struct Transformation {
pub origin_x: f64, pub origin_x: f64,
pub origin_y: f64, pub origin_y: f64,
pub scale: f64, pub scale_x: f64,
pub scale_y: f64,
} }
impl Transformation { impl Transformation {
/// Applies the new transformation after this one /// Applies the new transformation after this one
pub fn chain(self, next: Transformation) -> Transformation { pub fn chain(self, next: Transformation) -> Transformation {
Transformation { Transformation {
origin_x: self.origin_x + self.scale * next.origin_x, origin_x: self.origin_x + self.scale_x * next.origin_x,
origin_y: self.origin_y + self.scale * next.origin_y, origin_y: self.origin_y + self.scale_y * next.origin_y,
scale: self.scale * next.scale, scale_x: self.scale_x * next.scale_x,
scale_y: self.scale_y * next.scale_y,
} }
} }
fn forward(&self, p: Point) -> Point { fn forward(&self, p: Point) -> Point {
Point { Point {
x: (p.x - self.origin_x) / self.scale, x: (p.x - self.origin_x) / self.scale_x,
y: (p.y - self.origin_y) / self.scale, y: (p.y - self.origin_y) / self.scale_y,
} }
} }
fn reverse(&self, p: Point) -> Point { fn reverse(&self, p: Point) -> Point {
Point { Point {
x: p.x * self.scale + self.origin_x, x: p.x * self.scale_x + self.origin_x,
y: p.y * self.scale + self.origin_y, y: p.y * self.scale_y + self.origin_y,
} }
} }
pub fn reverse_bounds(&self, b: Bounds) -> Bounds { pub fn reverse_bounds(&self, b: Bounds) -> Bounds {
@ -211,13 +220,17 @@ pub mod c {
submission: CSubmission, submission: CSubmission,
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
manager: manager::c::Manager, popover: actors::popover::c::Actor,
app_state: receiver::c::State,
ui_keyboard: EekGtkKeyboard, ui_keyboard: EekGtkKeyboard,
) { ) {
let time = Timestamp(time); let time = Timestamp(time);
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = submission.clone_ref(); let submission = submission.clone_ref();
let mut submission = submission.borrow_mut(); let mut submission = submission.borrow_mut();
let app_state = app_state.clone_owned();
let popover_state = popover.clone_owned();
let ui_backend = UIBackend { let ui_backend = UIBackend {
widget_to_layout, widget_to_layout,
keyboard: ui_keyboard, keyboard: ui_keyboard,
@ -232,7 +245,7 @@ pub mod c {
&mut submission, &mut submission,
Some(&ui_backend), Some(&ui_backend),
time, time,
Some(manager), Some((&popover_state, app_state.clone())),
key, key,
); );
} }
@ -311,13 +324,18 @@ pub mod c {
x_widget: f64, y_widget: f64, x_widget: f64, y_widget: f64,
widget_to_layout: Transformation, widget_to_layout: Transformation,
time: u32, time: u32,
manager: manager::c::Manager, popover: actors::popover::c::Actor,
app_state: receiver::c::State,
ui_keyboard: EekGtkKeyboard, ui_keyboard: EekGtkKeyboard,
) { ) {
let time = Timestamp(time); let time = Timestamp(time);
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = submission.clone_ref(); let submission = submission.clone_ref();
let mut submission = submission.borrow_mut(); let mut submission = submission.borrow_mut();
// We only need to query state here, not update.
// A copy is enough.
let popover_state = popover.clone_owned();
let app_state = app_state.clone_owned();
let ui_backend = UIBackend { let ui_backend = UIBackend {
widget_to_layout, widget_to_layout,
keyboard: ui_keyboard, keyboard: ui_keyboard,
@ -348,7 +366,7 @@ pub mod c {
&mut submission, &mut submission,
Some(&ui_backend), Some(&ui_backend),
time, time,
Some(manager), Some((&popover_state, app_state.clone())),
key, key,
); );
} }
@ -373,7 +391,7 @@ pub mod c {
&mut submission, &mut submission,
Some(&ui_backend), Some(&ui_backend),
time, time,
Some(manager), Some((&popover_state, app_state.clone())),
key, key,
); );
} }
@ -394,7 +412,8 @@ pub mod c {
let transform = Transformation { let transform = Transformation {
origin_x: 10f64, origin_x: 10f64,
origin_y: 11f64, origin_y: 11f64,
scale: 12f64, scale_x: 12f64,
scale_y: 13f64,
}; };
let point = Point { x: 1f64, y: 1f64 }; let point = Point { x: 1f64, y: 1f64 };
let transformed = transform.reverse(transform.forward(point.clone())); let transformed = transform.reverse(transform.forward(point.clone()));
@ -755,16 +774,20 @@ impl Layout {
let size = self.calculate_size(); let size = self.calculate_size();
let h_scale = available.width / size.width; let h_scale = available.width / size.width;
let v_scale = available.height / size.height; 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 { let outside_margins = c::Transformation {
origin_x: (available.width - (scale * size.width)) / 2.0, origin_x: (available.width - (scale_x * size.width)) / 2.0,
origin_y: (available.height - (scale * size.height)) / 2.0, origin_y: (available.height - (scale_y * size.height)) / 2.0,
scale: scale, scale_x: scale_x,
scale_y: scale_y,
}; };
outside_margins.chain(c::Transformation { outside_margins.chain(c::Transformation {
origin_x: self.margins.left, origin_x: self.margins.left,
origin_y: self.margins.top, origin_y: self.margins.top,
scale: 1.0, scale_x: 1.0,
scale_y: 1.0,
}) })
} }
@ -1026,7 +1049,11 @@ mod seat {
submission: &mut Submission, submission: &mut Submission,
ui: Option<&UIBackend>, ui: Option<&UIBackend>,
time: Timestamp, time: Timestamp,
manager: Option<manager::c::Manager>, // TODO: intermediate measure:
// passing state conditionally because it's only used for popover.
// Eventually, it should be used for sumitting button events,
// and passed always.
manager: Option<(&actors::popover::State, receiver::State)>,
rckey: &Rc<RefCell<KeyState>>, rckey: &Rc<RefCell<KeyState>>,
) { ) {
let key: KeyState = { let key: KeyState = {
@ -1061,7 +1088,7 @@ mod seat {
// only show when UI is present // only show when UI is present
Action::ShowPreferences => if let Some(ui) = &ui { Action::ShowPreferences => if let Some(ui) = &ui {
// only show when layout manager is available // only show when layout manager is available
if let Some(manager) = manager { if let Some((manager, app_state)) = manager {
let view = layout.get_current_view(); let view = layout.get_current_view();
let places = ::layout::procedures::find_key_places( let places = ::layout::procedures::find_key_places(
view, &rckey, view, &rckey,
@ -1076,10 +1103,11 @@ mod seat {
width: button.size.width, width: button.size.width,
height: button.size.height, height: button.size.height,
}; };
::popover::show( popover::show(
ui.keyboard, ui.keyboard,
ui.widget_to_layout.reverse_bounds(bounds), ui.widget_to_layout.reverse_bounds(bounds),
manager, manager,
app_state,
); );
} }
} }
@ -1471,8 +1499,63 @@ mod test {
let transformation = layout.calculate_transformation( let transformation = layout.calculate_transformation(
Size { width: 2.0, height: 2.0 } 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_x, 0.5);
assert_eq!(transformation.origin_y, 0.0); 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,22 @@ extern crate gtk_sys;
#[allow(unused_imports)] #[allow(unused_imports)]
#[macro_use] // only for tests #[macro_use] // only for tests
extern crate maplit; extern crate maplit;
extern crate regex;
extern crate serde; extern crate serde;
extern crate xkbcommon; extern crate xkbcommon;
extern crate zbus;
extern crate zvariant;
#[cfg(test)]
#[macro_use]
mod assert_matches;
#[macro_use] #[macro_use]
mod logging; mod logging;
mod action; mod action;
mod actors;
mod animation; mod animation;
pub mod data; pub mod data;
mod debug;
mod drawing; mod drawing;
mod event_loop; mod event_loop;
pub mod float_ord; pub mod float_ord;
@ -29,15 +35,15 @@ mod keyboard;
mod layout; mod layout;
mod locale; mod locale;
mod main; mod main;
mod manager;
mod outputs; mod outputs;
mod panel;
mod popover; mod popover;
mod receiver;
mod resources; mod resources;
mod state; mod state;
mod style; mod style;
mod submission; mod submission;
pub mod tests; pub mod tests;
pub mod util; pub mod util;
mod ui_manager;
mod vkeyboard; mod vkeyboard;
mod xdg; mod xdg;

View File

@ -8,6 +8,8 @@
#include "eek/eek-types.h" #include "eek/eek-types.h"
#include "dbus.h" #include "dbus.h"
#include "panel.h"
#include "src/popover.h"
struct receiver; struct receiver;
@ -21,13 +23,16 @@ struct rsobjects {
struct receiver *receiver; struct receiver *receiver;
struct squeek_state_manager *state_manager; struct squeek_state_manager *state_manager;
struct submission *submission; struct submission *submission;
struct squeek_wayland *wayland;
struct squeek_popover *popover;
}; };
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, struct squeek_popover *popover, 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_visible(struct squeek_state_manager *state);
void squeek_state_send_force_hidden(struct squeek_state_manager *state); void squeek_state_send_force_hidden(struct squeek_state_manager *state);
void squeek_state_send_keyboard_present(struct squeek_state_manager *state, uint32_t keyboard_present); void squeek_state_send_keyboard_present(struct squeek_state_manager *state, uint32_t keyboard_present);
void squeek_state_send_layout_set(struct squeek_state_manager *state, char *name, char *layout, uint32_t timestamp);

View File

@ -1,47 +1,89 @@
/* Copyright (C) 2020 Purism SPC /* Copyright (C) 2020,2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+ * SPDX-License-Identifier: GPL-3.0+
*/ */
/*! Glue for the main loop. */ /*! Glue for the main loop. */
use crate::actors;
use crate::state; use crate::animation;
use crate::debug;
use crate::data::loading;
use crate::panel;
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver}; use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
mod c { mod c {
use super::*; use super::*;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use std::time::Instant; use std::time::Instant;
use crate::event_loop::driver; use crate::event_loop::driver;
use crate::imservice::IMService; use crate::imservice::IMService;
use crate::imservice::c::InputMethod; use crate::imservice::c::InputMethod;
use crate::layout;
use crate::outputs::Outputs;
use crate::state; use crate::state;
use crate::submission::Submission; use crate::submission::Submission;
use crate::util::c::Wrapped; use crate::util::c::Wrapped;
use crate::vkeyboard::c::ZwpVirtualKeyboardV1; use crate::vkeyboard::c::ZwpVirtualKeyboardV1;
/// ServerContextService*
#[repr(transparent)]
pub struct UIManager(*const c_void);
/// DbusHandler* /// DbusHandler*
#[repr(transparent)] #[repr(transparent)]
pub struct DBusHandler(*const c_void); 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. /// Holds the Rust structures that are interesting from C.
#[repr(C)] #[repr(C)]
pub struct RsObjects { pub struct RsObjects {
/// The handle to which Commands should be sent
/// for processing in the main loop.
receiver: Wrapped<Receiver<Commands>>, receiver: Wrapped<Receiver<Commands>>,
state_manager: Wrapped<driver::Threaded>, state_manager: Wrapped<driver::Threaded>,
submission: Wrapped<Submission>, submission: Wrapped<Submission>,
/// Not wrapped, because C needs to access this.
wayland: *mut Wayland,
popover: actors::popover::c::Actor,
}
/// 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" { extern "C" {
fn server_context_service_real_show_keyboard(service: *const UIManager); #[allow(improper_ctypes)]
fn server_context_service_real_hide_keyboard(service: *const UIManager); fn init_wayland(wayland: *mut Wayland);
fn server_context_service_set_hint_purpose(service: *const UIManager, hint: u32, purpose: u32); #[allow(improper_ctypes)]
fn eekboard_context_service_set_layout(service: HintManager, layout: *const layout::Layout, timestamp: u32);
// This should probably only get called from the gtk main loop, // This should probably only get called from the gtk main loop,
// given that dbus handler is using glib. // given that dbus handler is using glib.
fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8); fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8);
@ -52,19 +94,25 @@ mod c {
/// and that leads to suffering. /// and that leads to suffering.
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_rsobjects_new( fn squeek_init() -> RsObjects {
im: *mut InputMethod, // Set up channels
vk: ZwpVirtualKeyboardV1,
) -> RsObjects {
let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT); let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT);
let now = Instant::now(); let now = Instant::now();
let state_manager = driver::Threaded::new(sender, state::Application::new(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 None
} else { } else {
Some(IMService::new(im, state_manager.clone())) Some(IMService::new(wayland.input_method, state_manager.clone()))
}; };
let submission = Submission::new(vk, imservice); let submission = Submission::new(vk, imservice);
@ -72,6 +120,8 @@ mod c {
submission: Wrapped::new(submission), submission: Wrapped::new(submission),
state_manager: Wrapped::new(state_manager), state_manager: Wrapped::new(state_manager),
receiver: Wrapped::new(receiver), receiver: Wrapped::new(receiver),
wayland: Box::into_raw(wayland),
popover: Wrapped::new(actors::popover::State::new()),
} }
} }
@ -80,21 +130,31 @@ mod c {
pub extern "C" pub extern "C"
fn register_ui_loop_handler( fn register_ui_loop_handler(
receiver: Wrapped<Receiver<Commands>>, receiver: Wrapped<Receiver<Commands>>,
ui_manager: *const UIManager, panel_manager: panel::c::PanelManager,
popover: actors::popover::c::Actor,
hint_manager: HintManager,
dbus_handler: *const DBusHandler, dbus_handler: *const DBusHandler,
) { ) {
let receiver = unsafe { receiver.unwrap() }; let receiver = unsafe { receiver.unwrap() };
let receiver = Rc::try_unwrap(receiver).expect("References still present"); let receiver = Rc::try_unwrap(receiver).expect("References still present");
let receiver = receiver.into_inner(); let receiver = receiver.into_inner();
let panel_manager = Wrapped::new(panel::Manager::new(panel_manager));
let ctx = MainContext::default(); let ctx = MainContext::default();
ctx.acquire(); let _acqu = ctx.acquire();
receiver.attach( receiver.attach(
Some(&ctx), Some(&ctx),
move |msg| { move |msg| {
main_loop_handle_message(msg, ui_manager, dbus_handler); main_loop_handle_message(
msg,
panel_manager.clone(),
&popover,
hint_manager,
dbus_handler,
);
Continue(true) Continue(true)
}, },
); );
#[cfg(not(feature = "glib_v0_14"))]
ctx.release(); ctx.release();
} }
@ -104,46 +164,51 @@ mod c {
/// and doesn't lend itself to testing other than integration. /// and doesn't lend itself to testing other than integration.
fn main_loop_handle_message( fn main_loop_handle_message(
msg: Commands, msg: Commands,
ui_manager: *const UIManager, panel_manager: Wrapped<panel::Manager>,
popover: &actors::popover::c::Actor,
hint_manager: HintManager,
dbus_handler: *const DBusHandler, dbus_handler: *const DBusHandler,
) { ) {
match msg.panel_visibility { if let Some(visibility) = msg.panel_visibility {
Some(PanelCommand::Show) => unsafe { panel::Manager::update(panel_manager, visibility);
server_context_service_real_show_keyboard(ui_manager);
},
Some(PanelCommand::Hide) => unsafe {
server_context_service_real_hide_keyboard(ui_manager);
},
None => {},
};
if let Some(visible) = msg.dbus_visible_set {
unsafe { dbus_handler_set_visible(dbus_handler, visible as u8) };
} }
if let Some(hints) = msg.layout_hint_set { if let Some(visible) = msg.dbus_visible_set {
if dbus_handler != std::ptr::null() {
unsafe { dbus_handler_set_visible(dbus_handler, visible as u8) };
}
}
if let Some(commands::SetLayout { description }) = msg.layout_selection {
let animation::Contents {
name,
kind,
overlay_name,
purpose,
} = description;
actors::popover::set_overlay(popover, overlay_name.clone());
let layout = loading::load_layout(name, kind, purpose, overlay_name);
let layout = Box::into_raw(Box::new(layout));
unsafe { unsafe {
server_context_service_set_hint_purpose( eekboard_context_service_set_layout(hint_manager, layout, 0);
ui_manager, }
hints.hint.bits(),
hints.purpose.clone() as u32,
)
};
} }
} }
} }
#[derive(Clone, PartialEq, Debug)] pub mod commands {
pub enum PanelCommand { use crate::animation;
Show, #[derive(Clone, Debug)]
Hide, pub struct SetLayout {
pub description: animation::Contents,
}
} }
/// The commands consumed by the main loop, /// The commands consumed by the main loop,
/// to be sent out to external components. /// to be sent out to external components.
#[derive(Clone)] #[derive(Clone)]
pub struct Commands { 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>, pub dbus_visible_set: Option<bool>,
pub layout_selection: Option<commands::SetLayout>,
} }

View File

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

View File

@ -14,6 +14,7 @@ sources = [
config_h, config_h,
'dbus.c', 'dbus.c',
'imservice.c', 'imservice.c',
'panel.c',
'popover.c', 'popover.c',
'server-context-service.c', 'server-context-service.c',
'wayland.c', 'wayland.c',
@ -24,18 +25,22 @@ sources = [
'../eek/eek-renderer.c', '../eek/eek-renderer.c',
'../eek/eek-types.c', '../eek/eek-types.c',
'../eek/layersurface.c', '../eek/layersurface.c',
dbus_src,
'../eekboard/eekboard-context-service.c', '../eekboard/eekboard-context-service.c',
# '../eekboard/eekboard-xklutil.c', ]
generated_sources = declare_dependency(
sources: [
dbus_src,
squeekboard_resources, squeekboard_resources,
wl_proto_sources, wl_proto_sources,
] ],
)
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
deps = [ deps = [
# dependency('glib-2.0', version: '>=2.26.0'), generated_sources,
dependency('gio-2.0', version: '>=2.26.0'), dependency('gio-2.0', version: '>=2.26.0'),
dependency('gio-unix-2.0'), dependency('gio-unix-2.0'),
dependency('gnome-desktop-3.0', version: '>=3.0'), dependency('gnome-desktop-3.0', version: '>=3.0'),
@ -100,8 +105,6 @@ libsqueekboard = static_library('libsqueekboard',
squeekboard = executable('squeekboard', squeekboard = executable('squeekboard',
'server-main.c', 'server-main.c',
wl_proto_sources,
squeekboard_resources,
link_with: libsqueekboard, link_with: libsqueekboard,
include_directories: [include_directories('..'), include_directories('../eek')], include_directories: [include_directories('..'), include_directories('../eek')],
dependencies: deps, dependencies: deps,

View File

@ -11,7 +11,8 @@ struct squeek_output_handle {
struct squeek_outputs *squeek_outputs_new(void); struct squeek_outputs *squeek_outputs_new(void);
void squeek_outputs_free(struct squeek_outputs*); void squeek_outputs_free(struct squeek_outputs*);
void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output); void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output, 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*); 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); int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output);
#endif #endif

View File

@ -1,7 +1,14 @@
/* Copyright (C) 2019-2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Managing Wayland outputs */ /*! Managing Wayland outputs */
use std::ops;
use std::vec::Vec; use std::vec::Vec;
use crate::event_loop;
use ::logging; use ::logging;
use crate::util::DivCeil;
// traits // traits
use ::logging::Warn; use ::logging::Warn;
@ -11,15 +18,22 @@ pub mod c {
use super::*; use super::*;
use std::os::raw::{ c_char, c_void }; use std::os::raw::{ c_char, c_void };
use std::ptr;
use ::util::c::COpaquePtr; use ::util::c::{COpaquePtr, Wrapped};
// Defined in C // Defined in C
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, PartialEq, Copy)] #[derive(Clone, PartialEq, Copy, Debug, Eq, Hash)]
pub struct WlOutput(*const c_void); pub struct WlOutput(*const c_void);
impl WlOutput {
fn null() -> Self {
Self(ptr::null())
}
}
#[repr(C)] #[repr(C)]
struct WlOutputListener<T: COpaquePtr> { struct WlOutputListener<T: COpaquePtr> {
geometry: extern fn( geometry: extern fn(
@ -63,7 +77,7 @@ pub mod c {
} }
/// Map to `wl_output.transform` values /// Map to `wl_output.transform` values
#[derive(Clone)] #[derive(Clone, Copy, Debug)]
pub enum Transform { pub enum Transform {
Normal = 0, Normal = 0,
Rotated90 = 1, Rotated90 = 1,
@ -103,33 +117,18 @@ pub mod c {
) -> i32; ) -> i32;
} }
type COutputs = ::util::c::Wrapped<Outputs>; /// Wrapping Outputs is required for calling its methods from C
type COutputs = Wrapped<Outputs>;
/// A stable reference to an output.
#[derive(Clone)]
#[repr(C)]
pub struct OutputHandle {
wl_output: WlOutput,
outputs: COutputs,
}
impl OutputHandle {
// Cannot return an Output reference
// because COutputs is too deeply wrapped
pub fn get_state(&self) -> Option<OutputState> {
let outputs = self.outputs.clone_ref();
let outputs = outputs.borrow();
find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone())
}
}
// Defined in Rust // Defined in Rust
// Callbacks from the output listener follow
extern fn outputs_handle_geometry( extern fn outputs_handle_geometry(
outputs: COutputs, outputs: COutputs,
wl_output: WlOutput, wl_output: WlOutput,
_x: i32, _y: i32, _x: i32, _y: i32,
_phys_width: i32, _phys_height: i32, phys_width: i32, phys_height: i32,
_subpixel: i32, _subpixel: i32,
_make: *const c_char, _model: *const c_char, _make: *const c_char, _model: *const c_char,
transform: i32, transform: i32,
@ -143,10 +142,23 @@ pub mod c {
let outputs = outputs.clone_ref(); let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut(); let mut collection = outputs.borrow_mut();
let output_state: Option<&mut OutputState> let output_state: Option<&mut OutputState>
= find_output_mut(&mut collection, wl_output) = collection
.find_output_mut(wl_output)
.map(|o| &mut o.pending); .map(|o| &mut o.pending);
match output_state { 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!( None => log_print!(
logging::Level::Warning, logging::Level::Warning,
"Got geometry on unknown output", "Got geometry on unknown output",
@ -171,7 +183,8 @@ pub mod c {
let outputs = outputs.clone_ref(); let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut(); let mut collection = outputs.borrow_mut();
let output_state: Option<&mut OutputState> let output_state: Option<&mut OutputState>
= find_output_mut(&mut collection, wl_output) = collection
.find_output_mut(wl_output)
.map(|o| &mut o.pending); .map(|o| &mut o.pending);
match output_state { match output_state {
Some(state) => { Some(state) => {
@ -192,14 +205,27 @@ pub mod c {
) { ) {
let outputs = outputs.clone_ref(); let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut(); let mut collection = outputs.borrow_mut();
let output = find_output_mut(&mut collection, wl_output); let output = collection
match output { .find_output_mut(wl_output);
Some(output) => { output.current = output.pending.clone(); } let event = match output {
None => log_print!( Some(output) => {
output.current = output.pending.clone();
Some(Event {
output: OutputId(wl_output),
change: ChangeType::Altered(output.current),
})
},
None => {
log_print!(
logging::Level::Warning, logging::Level::Warning,
"Got done on unknown output", "Got done on unknown output",
), );
None
}
}; };
if let Some(event) = event {
collection.send_event(event);
}
} }
extern fn outputs_handle_scale( extern fn outputs_handle_scale(
@ -210,7 +236,8 @@ pub mod c {
let outputs = outputs.clone_ref(); let outputs = outputs.clone_ref();
let mut collection = outputs.borrow_mut(); let mut collection = outputs.borrow_mut();
let output_state: Option<&mut OutputState> let output_state: Option<&mut OutputState>
= find_output_mut(&mut collection, wl_output) = collection
.find_output_mut(wl_output)
.map(|o| &mut o.pending); .map(|o| &mut o.pending);
match output_state { match output_state {
Some(state) => { state.scale = factor; } Some(state) => { state.scale = factor; }
@ -221,11 +248,7 @@ pub mod c {
}; };
} }
#[no_mangle] // End callbacks
pub extern "C"
fn squeek_outputs_new() -> COutputs {
COutputs::new(Outputs { outputs: Vec::new() })
}
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
@ -235,14 +258,17 @@ pub mod c {
#[no_mangle] #[no_mangle]
pub extern "C" 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 collection = raw_collection.clone_ref();
let mut collection = collection.borrow_mut(); let mut collection = collection.borrow_mut();
collection.outputs.push(Output { collection.outputs.push((
Output {
output: output.clone(), output: output.clone(),
pending: OutputState::uninitialized(), pending: OutputState::uninitialized(),
current: OutputState::uninitialized(), current: OutputState::uninitialized(),
}); },
id,
));
unsafe { squeek_output_add_listener( unsafe { squeek_output_add_listener(
output, output,
@ -256,60 +282,69 @@ pub mod c {
)}; )};
} }
/// This will try to unregister the output, if the id matches a registered one.
#[no_mangle] #[no_mangle]
pub extern "C" 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 = raw_collection.clone_ref();
let collection = collection.borrow(); let mut collection = collection.borrow_mut();
OutputHandle { collection.remove_output_by_global(id)
wl_output: collection.outputs[0].output.clone(), .map_err(|e| log_print!(
outputs: raw_collection.clone(), 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 // 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 /// Generic size
#[derive(Clone)] #[derive(Clone, Copy, Debug)]
pub struct Size { pub struct Size<Unit> {
pub width: u32, pub width: Unit,
pub height: u32, pub height: Unit,
} }
pub type PixelSize = Size<u32>;
/// wl_output mode /// wl_output mode
#[derive(Clone)] #[derive(Clone, Copy, Debug)]
struct Mode { pub struct Mode {
width: i32, pub width: i32,
height: 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 { pub struct OutputState {
current_mode: Option<Mode>, pub current_mode: Option<Mode>,
transform: Option<c::Transform>, pub geometry: Option<Geometry>,
pub scale: i32, pub scale: i32,
} }
@ -323,44 +358,138 @@ impl OutputState {
fn uninitialized() -> OutputState { fn uninitialized() -> OutputState {
OutputState { OutputState {
current_mode: None, current_mode: None,
transform: None, geometry: None,
scale: 1, 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; use self::c::Transform;
match self {
OutputState {
current_mode: Some(Mode { width, height } ),
transform: Some(transform),
scale: _,
} => Some(
match transform { match transform {
Transform::Normal Transform::Normal
| Transform::Rotated180 | Transform::Rotated180
| Transform::Flipped | Transform::Flipped
| Transform::FlippedRotated180 => Size { | Transform::FlippedRotated180 => Size {
width: *width as u32, width,
height: *height as u32, height,
}, },
_ => Size { _ => Size {
width: *height as u32, width: height,
height: *width as u32, 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 } ),
geometry: Some(Geometry { transform, .. } ),
scale: _,
} => 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, _ => 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, output: c::WlOutput,
pending: OutputState, pending: OutputState,
current: OutputState, current: OutputState,
} }
#[derive(Debug)]
struct NotFound;
/// Wayland global ID type
type GlobalId = u32;
/// The outputs manager
pub struct Outputs { 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,
} }

124
src/panel.c Normal file
View File

@ -0,0 +1,124 @@
#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_hide (GTK_WIDGET (self->window));
}
}
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->state_manager, self->popover);
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) {
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);
}
if (!self->widget) {
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_state_manager *state_manager, struct squeek_popover *popover)
{
struct panel_manager mgr = {
.state = state,
.submission = submission,
.window = NULL,
.widget = NULL,
.current_output = NULL,
.state_manager = state_manager,
.popover = popover,
};
return mgr;
}

25
src/panel.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "eek/layersurface.h"
#include "src/layout.h"
#include "src/main.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 squeek_state_manager *state_manager; // shared reference
struct squeek_popover *popover; // shared reference
struct submission *submission; // unowned
// both memoized - doesn't have to be, but bugs happen:
// https://gitlab.gnome.org/World/Phosh/squeekboard/-/issues/343
PhoshLayerSurface *window;
GtkWidget *widget;
// Those should be held in Rust
struct wl_output *current_output;
};
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_state_manager *state_manager, struct squeek_popover *popover);

247
src/panel.rs Normal file
View File

@ -0,0 +1,247 @@
/* 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 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});
glib::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}
}
},
}
}
}

5
src/popover.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
/// Popover state.
/// Wrapped<actors::popover::State>
struct squeek_popover;

View File

@ -4,24 +4,21 @@ use gio;
use gtk; use gtk;
use std::ffi::CString; use std::ffi::CString;
use std::cmp::Ordering; use std::cmp::Ordering;
use ::layout::c::{ Bounds, EekGtkKeyboard }; use crate::actors;
use ::locale::{ OwnedTranslation, compare_current_locale }; use crate::layout::c::{ Bounds, EekGtkKeyboard };
use ::logging; use crate::locale::{ OwnedTranslation, compare_current_locale };
use ::manager; use crate::logging;
use ::resources; use crate::receiver;
use crate::resources;
use crate::state;
// Traits // Traits
use gio::ActionMapExt; use gio::prelude::ActionMapExt;
use gio::SettingsExt; use gio::prelude::SettingsExt;
#[cfg(feature = "gio_v0_5")]
use gio::SimpleActionExt;
use glib::translate::FromGlibPtrNone; use glib::translate::FromGlibPtrNone;
use glib::variant::ToVariant; use glib::variant::ToVariant;
#[cfg(not(feature = "gtk_v0_5"))]
use gtk::prelude::*; use gtk::prelude::*;
use gtk::PopoverExt; use crate::logging::Warn;
use gtk::WidgetExt;
use ::logging::Warn;
mod c { mod c {
use std::os::raw::c_char; use std::os::raw::c_char;
@ -110,8 +107,13 @@ mod variants {
fn get_settings(schema_name: &str) -> Option<gio::Settings> { fn get_settings(schema_name: &str) -> Option<gio::Settings> {
let mut error_handler = logging::Print{}; 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, &mut error_handler,
logging::Problem::Surprise, logging::Problem::Surprise,
"No gsettings schemas installed.", "No gsettings schemas installed.",
@ -127,10 +129,16 @@ fn get_settings(schema_name: &str) -> Option<gio::Settings> {
.map(|_sschema| gio::Settings::new(schema_name)) .map(|_sschema| gio::Settings::new(schema_name))
} }
fn set_layout(kind: String, name: String) { fn set_layout(kind: &str, name: &str) {
let settings = get_settings("org.gnome.desktop.input-sources"); let settings = get_settings("org.gnome.desktop.input-sources");
if let Some(settings) = settings { if let Some(settings) = settings {
let kind = String::from(kind);
let name = String::from(name);
#[cfg(feature = "glib_v0_14")]
let inputs = settings.value("sources");
#[cfg(not(feature = "glib_v0_14"))]
let inputs = settings.get_value("sources").unwrap(); let inputs = settings.get_value("sources").unwrap();
let current = (kind.clone(), name.clone()); let current = (kind.clone(), name.clone());
let inputs = variants::get_tuples(inputs).into_iter() let inputs = variants::get_tuples(inputs).into_iter()
.filter(|t| t != &current); .filter(|t| t != &current);
@ -146,7 +154,7 @@ fn set_layout(kind: String, name: String) {
/// A reference to what the user wants to see /// A reference to what the user wants to see
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
enum LayoutId { pub enum LayoutId {
/// Affects the layout in system settings /// Affects the layout in system settings
System { System {
kind: String, kind: String,
@ -166,40 +174,23 @@ impl LayoutId {
} }
fn set_visible_layout( fn set_visible_layout(
manager: manager::c::Manager, layout_id: &LayoutId,
layout_id: LayoutId,
) { ) {
match layout_id { match layout_id {
LayoutId::System { kind, name } => { LayoutId::System { kind, name } => {
unsafe {
use std::ptr;
manager::c::eekboard_context_service_set_overlay(
manager,
ptr::null(),
);
}
set_layout(kind, name); set_layout(kind, name);
}
LayoutId::Local(name) => {
let name = CString::new(name.as_str()).unwrap();
let name_ptr = name.as_ptr();
unsafe {
manager::c::eekboard_context_service_set_overlay(
manager,
name_ptr,
)
}
}, },
_ => {},
} }
} }
/// Takes into account first any overlays, then system layouts from the list /// Takes into account first any overlays, then system layouts from the list
fn get_current_layout( fn get_current_layout(
manager: manager::c::Manager, popover: &actors::popover::State,
system_layouts: &Vec<LayoutId>, system_layouts: &Vec<LayoutId>,
) -> Option<LayoutId> { ) -> Option<LayoutId> {
match manager::get_overlay(manager) { match &popover.overlay {
Some(name) => Some(LayoutId::Local(name)), Some(name) => Some(LayoutId::Local(name.into())),
None => system_layouts.get(0).map(LayoutId::clone), None => system_layouts.get(0).map(LayoutId::clone),
} }
} }
@ -243,7 +234,8 @@ fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
pub fn show( pub fn show(
window: EekGtkKeyboard, window: EekGtkKeyboard,
position: Bounds, position: Bounds,
manager: manager::c::Manager, popover: &actors::popover::State,
app_state: receiver::State,
) { ) {
unsafe { gtk::set_initialized() }; unsafe { gtk::set_initialized() };
let window = unsafe { gtk::Widget::from_glib_none(window.0) }; let window = unsafe { gtk::Widget::from_glib_none(window.0) };
@ -254,7 +246,11 @@ pub fn show(
let settings = get_settings("org.gnome.desktop.input-sources"); let settings = get_settings("org.gnome.desktop.input-sources");
let inputs = settings let inputs = settings
.map(|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(); let inputs = settings.get_value("sources").unwrap();
variants::get_tuples(inputs) variants::get_tuples(inputs)
}) })
.unwrap_or_else(|| Vec::new()); .unwrap_or_else(|| Vec::new());
@ -285,8 +281,18 @@ pub fn show(
} }
}); });
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"); let builder = gtk::Builder::new_from_resource("/sm/puri/squeekboard/popover.ui");
let model: gio::Menu = builder.get_object("app-menu").unwrap(); builder.get_object("app-menu").unwrap()
}
};
for (tr, l) in human_names.iter().rev() { for (tr, l) in human_names.iter().rev() {
let detailed_action = format!("layout::{}", l.get_name()); let detailed_action = format!("layout::{}", l.get_name());
@ -294,7 +300,11 @@ pub fn show(
model.prepend_item (&item); 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); let menu = gtk::Popover::new_from_model(Some(&window), &model);
menu.set_pointing_to(&gtk::Rectangle { menu.set_pointing_to(&gtk::Rectangle {
x: position.x.ceil() as i32, x: position.x.ceil() as i32,
y: position.y.ceil() as i32, y: position.y.ceil() as i32,
@ -305,7 +315,7 @@ pub fn show(
let action_group = gio::SimpleActionGroup::new(); let action_group = gio::SimpleActionGroup::new();
if let Some(current_layout) = get_current_layout(manager, &system_layouts) { if let Some(current_layout) = get_current_layout(popover, &system_layouts) {
let current_layout_name = all_layouts.iter() let current_layout_name = all_layouts.iter()
.find( .find(
|l| l.get_name() == current_layout.get_name() |l| l.get_name() == current_layout.get_name()
@ -334,10 +344,13 @@ pub fn show(
.find( .find(
|choices| state == choices.get_name() |choices| state == choices.get_name()
).unwrap(); ).unwrap();
set_visible_layout( app_state
manager, .send(state::Event::OverlayChanged(layout.clone()))
layout.clone(), .or_print(
) logging::Problem::Bug,
&format!("Can't send to state"),
);
set_visible_layout(layout)
}); });
}, },
None => log_print!( None => log_print!(

15
src/receiver.rs Normal file
View File

@ -0,0 +1,15 @@
/*! Defines the application-wide message bus for updating state.*/
use crate::event_loop::driver::Threaded;
pub mod c {
use super::*;
use crate::util::c::Wrapped;
pub type State = Wrapped<Threaded>;
}
// The state receiver is an endpoint of a channel, so it's safely cloneable.
// There's no need to keep it in a Rc.
// The C version uses Wrapped with an underlying Rc,
// because Wrapped is well-tested already.
pub type State = Threaded;

View File

@ -52,8 +52,13 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
("fr", include_str!("../data/keyboards/fr.yaml")), ("fr", include_str!("../data/keyboards/fr.yaml")),
("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")), ("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
("fr+bepo", include_str!("../data/keyboards/fr+bepo.yaml")),
("fr+bepo_wide", include_str!("../data/keyboards/fr+bepo_wide.yaml")),
("ge", include_str!("../data/keyboards/ge.yaml")),
("gr", include_str!("../data/keyboards/gr.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")), ("il", include_str!("../data/keyboards/il.yaml")),
@ -71,6 +76,9 @@ static KEYBOARDS: &[(&'static str, &'static str)] = &[
("pl", include_str!("../data/keyboards/pl.yaml")), ("pl", include_str!("../data/keyboards/pl.yaml")),
("pl_wide", include_str!("../data/keyboards/pl_wide.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")), ("ru", include_str!("../data/keyboards/ru.yaml")),
("se", include_str!("../data/keyboards/se.yaml")), ("se", include_str!("../data/keyboards/se.yaml")),

View File

@ -20,12 +20,6 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <glib/gi18n.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" #include "server-context-service.h"
enum { enum {
@ -36,196 +30,13 @@ enum {
struct _ServerContextService { struct _ServerContextService {
GObject parent; 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 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); G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT);
static void static void
on_destroy (ServerContextService *self, GtkWidget *widget) /// Height is in scaled units.
{
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
server_context_service_set_property (GObject *object, server_context_service_set_property (GObject *object,
guint prop_id, guint prop_id,
const GValue *value, 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 static void
server_context_service_class_init (ServerContextServiceClass *klass) 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->set_property = server_context_service_set_property;
gobject_class->get_property = server_context_service_get_property; gobject_class->get_property = server_context_service_get_property;
gobject_class->dispose = server_context_service_dispose;
/** /**
* ServerContextServie:keyboard: * ServerContextServie:keyboard:
@ -296,42 +95,29 @@ server_context_service_class_init (ServerContextServiceClass *klass)
static void static void
server_context_service_init (ServerContextService *self) {} 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"; const char *schema_name = "org.gnome.desktop.a11y.applications";
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default(); GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
g_autoptr(GSettingsSchema) schema = NULL; g_autoptr(GSettingsSchema) schema = NULL;
if (!ssrc) { if (!ssrc) {
g_warning("No gsettings schemas installed."); g_warning("No gsettings schemas installed.");
return; return NULL;
} }
schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE); schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE);
if (schema) { if (schema) {
g_autoptr(GSettings) settings = g_settings_new (schema_name); g_autoptr(GSettings) settings = g_settings_new (schema_name);
g_settings_bind (settings, "screen-keyboard-enabled", g_settings_bind (settings, "screen-keyboard-enabled",
self, "enabled", G_SETTINGS_BIND_GET); holder, "enabled", G_SETTINGS_BIND_GET);
} else { } else {
g_warning("Gsettings schema %s is not installed on the system. " g_warning("Gsettings schema %s is not installed on the system. "
"Enabling by default.", schema_name); "Enabling by default.", schema_name);
} }
} return holder;
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);
} }

View File

@ -17,10 +17,9 @@
*/ */
#ifndef SERVER_CONTEXT_SERVICE_H #ifndef SERVER_CONTEXT_SERVICE_H
#define SERVER_CONTEXT_SERVICE_H 1 #define SERVER_CONTEXT_SERVICE_H 1
#include <gtk/gtk.h>
#include "src/layout.h" #include "main.h"
#include "src/submission.h"
#include "ui_manager.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -29,10 +28,8 @@ G_BEGIN_DECLS
/** Manages the lifecycle of the window displaying layouts. */ /** Manages the lifecycle of the window displaying layouts. */
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject) 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); ServerContextService *server_context_service_new(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);
G_END_DECLS G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */ #endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -31,9 +31,9 @@
#include "layout.h" #include "layout.h"
#include "main.h" #include "main.h"
#include "outputs.h" #include "outputs.h"
#include "panel.h"
#include "submission.h" #include "submission.h"
#include "server-context-service.h" #include "server-context-service.h"
#include "ui_manager.h"
#include "wayland.h" #include "wayland.h"
#include <gdk/gdkwayland.h> #include <gdk/gdkwayland.h>
@ -52,12 +52,10 @@ typedef enum _SqueekboardDebugFlags {
struct squeekboard { struct squeekboard {
struct squeek_wayland wayland; // Just hooks. struct squeek_wayland wayland; // Just hooks.
DBusHandler *dbus_handler; // Controls visibility of the OSK. DBusHandler *dbus_handler; // Controls visibility of the OSK.
EekboardContextService *settings_context; // Gsettings hooks. EekboardContextService *settings_context; // Gsettings hooks for layouts.
ServerContextService *ui_context; // mess, includes the entire UI /// Gsettings hook for visibility. TODO: this does not belong in gsettings.
/// Currently wanted layout. TODO: merge into state::Application ServerContextService *settings_handler;
struct squeek_layout_state layout_choice; struct panel_manager panel_manager; // Controls the shape of the panel.
/// UI shape tracker/chooser. TODO: merge into state::Application
struct ui_manager *ui_manager;
}; };
@ -112,34 +110,38 @@ registry_handle_global (void *data,
// Even when lower version would be served, it would not be supported, // Even when lower version would be served, it would not be supported,
// causing a hard exit // causing a hard exit
(void)version; (void)version;
struct squeekboard *instance = data; struct squeek_wayland *wayland = data;
if (!strcmp (interface, zwlr_layer_shell_v1_interface.name)) { 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); &zwlr_layer_shell_v1_interface, 1);
} else if (!strcmp (interface, zwp_virtual_keyboard_manager_v1_interface.name)) { } 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); &zwp_virtual_keyboard_manager_v1_interface, 1);
} else if (!strcmp (interface, zwp_input_method_manager_v2_interface.name)) { } 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); &zwp_input_method_manager_v2_interface, 1);
} else if (!strcmp (interface, "wl_output")) { } else if (!strcmp (interface, "wl_output")) {
struct wl_output *output = wl_registry_bind (registry, name, struct wl_output *output = wl_registry_bind (registry, name,
&wl_output_interface, 2); &wl_output_interface, 2);
squeek_outputs_register(instance->wayland.outputs, output); squeek_outputs_register(wayland->outputs, output, name);
} else if (!strcmp(interface, "wl_seat")) { } 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); &wl_seat_interface, 1);
} }
} }
static void static void
registry_handle_global_remove (void *data, registry_handle_global_remove (void *data,
struct wl_registry *registry, struct wl_registry *registry,
uint32_t name) 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 = { static const struct wl_registry_listener registry_listener = {
@ -147,6 +149,54 @@ static const struct wl_registry_listener registry_listener = {
registry_handle_global_remove 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" #define SESSION_NAME "sm.puri.OSK0"
GDBusProxy *_proxy = NULL; GDBusProxy *_proxy = NULL;
@ -284,22 +334,6 @@ phosh_theme_init (void)
g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", TRUE, NULL); 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[] = static GDebugKey debug_keys[] =
{ {
{ .key = "force-show", { .key = "force-show",
@ -359,48 +393,12 @@ main (int argc, char **argv)
phosh_theme_init (); 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}; 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) { // Also initializes wayland
g_error("No seat Wayland global available."); struct rsobjects rsobjects = squeek_init();
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) { instance.settings_context = eekboard_context_service_new(rsobjects.state_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();
instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
// set up dbus // set up dbus
@ -438,21 +436,22 @@ main (int argc, char **argv)
} }
} }
eekboard_context_service_set_submission(instance.settings_context, rsobjects.submission); ServerContextService *setting_listener = server_context_service_new(
ServerContextService *ui_context = server_context_service_new(
instance.settings_context,
rsobjects.submission,
&instance.layout_choice,
instance.ui_manager,
rsobjects.state_manager); rsobjects.state_manager);
if (!ui_context) { if (!setting_listener) {
g_error("Could not initialize GUI"); g_warning ("could not connect to gsettings");
exit(1);
} }
instance.ui_context = ui_context; instance.settings_handler = setting_listener;
register_ui_loop_handler(rsobjects.receiver, instance.ui_context, instance.dbus_handler);
eekboard_context_service_set_submission(instance.settings_context, rsobjects.submission);
instance.panel_manager = panel_manager_new(instance.settings_context,
rsobjects.submission,
rsobjects.state_manager,
rsobjects.popover);
register_ui_loop_handler(rsobjects.receiver, &instance.panel_manager, rsobjects.popover, instance.settings_context, instance.dbus_handler);
session_register(); session_register();
@ -477,6 +476,5 @@ main (int argc, char **argv)
} }
g_main_loop_unref (loop); g_main_loop_unref (loop);
squeek_wayland_deinit (&instance.wayland);
return 0; return 0;
} }

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Purism SPC /* Copyright (C) 2021,2022 Purism SPC
* SPDX-License-Identifier: GPL-3.0+ * SPDX-License-Identifier: GPL-3.0+
*/ */
@ -6,35 +6,74 @@
* It's driven by the loop defined in the loop module. */ * It's driven by the loop defined in the loop module. */
use crate::animation; use crate::animation;
use crate::debug;
use crate::imservice::{ ContentHint, ContentPurpose }; use crate::imservice::{ ContentHint, ContentPurpose };
use crate::main::{ Commands, PanelCommand }; use crate::layout::ArrangementKind;
use crate::main;
use crate::main::Commands;
use crate::outputs;
use crate::outputs::{Millimeter, OutputId, OutputState};
use crate::panel;
use crate::panel::PixelSize;
use crate::popover;
use crate::util::Rational;
use std::cmp;
use std::collections::HashMap;
use std::time::Instant; use std::time::Instant;
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub enum Presence { pub enum Presence {
Present, Present,
Missing, Missing,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct InputMethodDetails { pub struct InputMethodDetails {
pub hint: ContentHint, pub hint: ContentHint,
pub purpose: ContentPurpose, pub purpose: ContentPurpose,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub enum InputMethod { pub enum InputMethod {
Active(InputMethodDetails), Active(InputMethodDetails),
InactiveSince(Instant), InactiveSince(Instant),
} }
/// Incoming events #[derive(Clone, Debug)]
#[derive(Clone)] pub enum LayoutSource {
Xkb,
Other(String),
}
impl From<String> for LayoutSource {
fn from(v: String) -> Self {
if v.as_str() == "xkb" {
LayoutSource::Xkb
} else {
LayoutSource::Other(v)
}
}
}
/// The user's preferred system layout
#[derive(Clone, Debug)]
pub struct LayoutChoice {
pub name: String,
pub source: LayoutSource,
}
/// Incoming events.
/// This contains events that cause a change to the internal state.
#[derive(Clone, Debug)]
pub enum Event { pub enum Event {
InputMethod(InputMethod), InputMethod(InputMethod),
Visibility(visibility::Event), Visibility(visibility::Event),
PhysicalKeyboard(Presence), PhysicalKeyboard(Presence),
Output(outputs::Event),
LayoutChoice(LayoutChoice),
OverlayChanged(popover::LayoutId),
Debug(debug::Event),
/// Event triggered because a moment in time passed. /// Event triggered because a moment in time passed.
/// Use to animate state transitions. /// Use to animate state transitions.
/// The value is the ideal arrival time. /// The value is the ideal arrival time.
@ -47,8 +86,14 @@ impl From<InputMethod> for Event {
} }
} }
impl From<outputs::Event> for Event {
fn from(ev: outputs::Event) -> Self {
Self::Output(ev)
}
}
pub mod visibility { pub mod visibility {
#[derive(Clone)] #[derive(Clone, Debug)]
pub enum Event { pub enum Event {
/// User requested the panel to show /// User requested the panel to show
ForceVisible, ForceVisible,
@ -68,9 +113,9 @@ pub mod visibility {
} }
/// The outwardly visible state. /// The outwardly visible state.
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Outcome { pub struct Outcome {
pub visibility: animation::Outcome, pub panel: animation::Outcome,
pub im: InputMethod, pub im: InputMethod,
} }
@ -81,35 +126,40 @@ impl Outcome {
/// The receivers of the commands bear the burden /// The receivers of the commands bear the burden
/// of checking if the commands end up being no-ops. /// of checking if the commands end up being no-ops.
pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands { pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
let layout_hint_set = match new_state { // FIXME: handle switching outputs
Outcome { let (dbus_visible_set, panel_visibility) = match new_state.panel {
visibility: animation::Outcome::Visible, animation::Outcome::Visible{output, height, ..}
im: InputMethod::Active(hints), => (Some(true), Some(panel::Command::Show{output, height})),
} => Some(hints.clone()), animation::Outcome::Hidden => (Some(false), Some(panel::Command::Hide)),
Outcome {
visibility: animation::Outcome::Visible,
im: InputMethod::InactiveSince(_),
} => Some(InputMethodDetails {
hint: ContentHint::NONE,
purpose: ContentPurpose::Normal,
}),
Outcome {
visibility: animation::Outcome::Hidden,
..
} => None,
}; };
let (dbus_visible_set, panel_visibility) = match new_state.visibility { // Compare the old and new states as not to flood with updates,
animation::Outcome::Visible => (Some(true), Some(PanelCommand::Show)), // which may look up in the file system.
animation::Outcome::Hidden => (Some(false), Some(PanelCommand::Hide)), use animation::Outcome::*;
let layout_selection = match &new_state.panel {
Visible{ contents: new_contents, ..} => {
let same
= if let Visible { contents, .. } = &self.panel {
contents == new_contents
} else {
false
};
if !same {
Some(main::commands::SetLayout {
description: new_contents.clone()
})
} else {
None
}
},
animation::Outcome::Hidden => None,
}; };
Commands { Commands {
panel_visibility, panel_visibility,
layout_hint_set,
dbus_visible_set, dbus_visible_set,
layout_selection,
} }
} }
} }
@ -125,11 +175,26 @@ impl Outcome {
/// All state changes return the next state and the optimal time for the next check. /// 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. /// This state tracker can be driven by any event loop.
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Application { pub struct Application {
pub im: InputMethod, pub im: InputMethod,
pub visibility_override: visibility::State, pub visibility_override: visibility::State,
pub physical_keyboard: Presence, 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>,
/// We presume that the system always has some preference,
/// even though we receive the preference after init,
/// and we might not receive one at all (gsettings missing).
/// Then a default is used.
pub layout_choice: LayoutChoice,
/// Manual override of the system layout
pub overlay_layout: Option<popover::LayoutId>,
} }
impl Application { impl Application {
@ -144,11 +209,34 @@ impl Application {
im: InputMethod::InactiveSince(now), im: InputMethod::InactiveSince(now),
visibility_override: visibility::State::NotForced, visibility_override: visibility::State::NotForced,
physical_keyboard: Presence::Missing, physical_keyboard: Presence::Missing,
debug_mode_enabled: false,
preferred_output: None,
outputs: Default::default(),
layout_choice: LayoutChoice {
name: String::from("us"),
source: LayoutSource::Xkb,
},
overlay_layout: None,
} }
} }
pub fn apply_event(self, event: Event, _now: Instant) -> Self { pub fn apply_event(self, event: Event, now: Instant) -> Self {
match event { 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::TimeoutReached(_) => self,
Event::Visibility(visibility) => Self { Event::Visibility(visibility) => Self {
@ -164,6 +252,25 @@ impl Application {
..self ..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) { Event::InputMethod(new_im) => match (self.im.clone(), new_im) {
(InputMethod::Active(_old), InputMethod::Active(new_im)) (InputMethod::Active(_old), InputMethod::Active(new_im))
=> Self { => Self {
@ -194,24 +301,165 @@ impl Application {
im: InputMethod::InactiveSince(old), im: InputMethod::InactiveSince(old),
..self ..self
}, },
},
Event::LayoutChoice(layout_choice) => Self {
layout_choice,
overlay_layout: None,
..self
},
Event::OverlayChanged(overlay_layout) => Self {
overlay_layout: Some(overlay_layout),
..self
},
};
if state.debug_mode_enabled {
println!(
"State is now:
{:#?}
Outcome:
{:#?}",
state,
state.get_outcome(now),
);
} }
state
} }
fn get_preferred_height_and_arrangement(output: &OutputState)
-> Option<(PixelSize, ArrangementKind)>
{
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 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 (arrangement, height_as_widths) = {
if abstract_width < 540 {(
ArrangementKind::Base,
Rational {
numerator: 210,
denominator: 360,
},
)} else {(
ArrangementKind::Wide,
Rational {
numerator: 172,
denominator: 540,
}
)}
};
let height
= 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),
},
arrangement,
)
})
}
/// Returns layout name, overlay name
fn get_layout_names(&self) -> (String, Option<String>) {
(
String::from(match &self.overlay_layout {
Some(popover::LayoutId::System { name, .. }) => name,
_ => &self.layout_choice.name,
}),
match &self.overlay_layout {
Some(popover::LayoutId::Local(name)) => Some(name.clone()),
_ => None,
},
)
} }
pub fn get_outcome(&self, now: Instant) -> Outcome { pub fn get_outcome(&self, now: Instant) -> Outcome {
// FIXME: include physical keyboard presence // FIXME: include physical keyboard presence
Outcome { Outcome {
visibility: match (self.physical_keyboard, self.visibility_override) { panel: match self.preferred_output {
None => animation::Outcome::Hidden,
Some(output) => {
let (height, arrangement) = Self::get_preferred_height_and_arrangement(self.outputs.get(&output).unwrap())
.unwrap_or((
PixelSize{pixels: 0, scale_factor: 1},
ArrangementKind::Base,
));
let (layout_name, overlay) = self.get_layout_names();
// TODO: Instead of setting size to 0 when the output is invalid,
// simply go invisible.
let visible = animation::Outcome::Visible{
output,
height,
contents: animation::Contents {
kind: arrangement,
name: layout_name,
overlay_name: overlay,
purpose: match self.im {
InputMethod::Active(InputMethodDetails { purpose, .. }) => purpose,
InputMethod::InactiveSince(_) => ContentPurpose::Normal,
},
}
};
match (self.physical_keyboard, self.visibility_override) {
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden, (_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
(_, visibility::State::ForcedVisible) => animation::Outcome::Visible, (_, visibility::State::ForcedVisible) => visible,
(Presence::Present, visibility::State::NotForced) => animation::Outcome::Hidden, (Presence::Present, visibility::State::NotForced) => animation::Outcome::Hidden,
(Presence::Missing, visibility::State::NotForced) => match self.im { (Presence::Missing, visibility::State::NotForced) => match self.im {
InputMethod::Active(_) => animation::Outcome::Visible, InputMethod::Active(_) => visible,
InputMethod::InactiveSince(since) => { InputMethod::InactiveSince(since) => {
if now < since + animation::HIDING_TIMEOUT { animation::Outcome::Visible } if now < since + animation::HIDING_TIMEOUT { visible }
else { animation::Outcome::Hidden } else { animation::Outcome::Hidden }
}, },
}, },
}
}
}, },
im: self.im.clone(), im: self.im.clone(),
} }
@ -236,9 +484,9 @@ impl Application {
#[cfg(test)] #[cfg(test)]
mod test { pub mod test {
use super::*; use super::*;
use crate::outputs::c::WlOutput;
use std::time::Duration; use std::time::Duration;
fn imdetails_new() -> InputMethodDetails { fn imdetails_new() -> InputMethodDetails {
@ -248,6 +496,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 the original delay scenario: no flicker on quick switches.
#[test] #[test]
fn avoid_hide() { fn avoid_hide() {
@ -257,15 +529,16 @@ mod test {
im: InputMethod::Active(imdetails_new()), im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing, physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced, visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
}; };
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now); let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
// Check 100ms at 1ms intervals. It should remain visible. // Check 100ms at 1ms intervals. It should remain visible.
for _i in 0..100 { for _i in 0..100 {
now += Duration::from_millis(1); now += Duration::from_millis(1);
assert_eq!( assert_matches!(
state.get_outcome(now).visibility, state.get_outcome(now).panel,
animation::Outcome::Visible, animation::Outcome::Visible{..},
"Hidden when it should remain visible: {:?}", "Hidden when it should remain visible: {:?}",
now.saturating_duration_since(start), now.saturating_duration_since(start),
) )
@ -273,7 +546,10 @@ mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now); 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).panel,
animation::Outcome::Visible{..}
);
} }
/// Make sure that hiding works when input method goes away /// Make sure that hiding works when input method goes away
@ -285,11 +561,12 @@ mod test {
im: InputMethod::Active(imdetails_new()), im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing, physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced, visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
}; };
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), 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).panel {
now += Duration::from_millis(1); now += Duration::from_millis(1);
assert!( assert!(
now < start + Duration::from_millis(250), now < start + Duration::from_millis(250),
@ -309,6 +586,7 @@ mod test {
im: InputMethod::Active(imdetails_new()), im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing, physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced, visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
}; };
// This reflects the sequence from Wayland: // This reflects the sequence from Wayland:
// disable, disable, enable, disable // disable, disable, enable, disable
@ -318,7 +596,7 @@ mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now); let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), 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).panel {
now += Duration::from_millis(1); now += Duration::from_millis(1);
assert!( assert!(
now < start + Duration::from_millis(250), now < start + Duration::from_millis(250),
@ -331,7 +609,7 @@ mod test {
for _i in 0..1000 { for _i in 0..1000 {
now += Duration::from_millis(1); now += Duration::from_millis(1);
assert_eq!( assert_eq!(
state.get_outcome(now).visibility, state.get_outcome(now).panel,
animation::Outcome::Hidden, animation::Outcome::Hidden,
"Appeared unnecessarily: {:?}", "Appeared unnecessarily: {:?}",
now.saturating_duration_since(start), now.saturating_duration_since(start),
@ -347,13 +625,14 @@ mod test {
im: InputMethod::InactiveSince(now), im: InputMethod::InactiveSince(now),
physical_keyboard: Presence::Missing, physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced, visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
}; };
now += Duration::from_secs(1); now += Duration::from_secs(1);
let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now); let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now);
assert_eq!( assert_matches!(
state.get_outcome(now).visibility, state.get_outcome(now).panel,
animation::Outcome::Visible, animation::Outcome::Visible{..},
"Failed to show: {:?}", "Failed to show: {:?}",
now.saturating_duration_since(start), now.saturating_duration_since(start),
); );
@ -365,7 +644,7 @@ mod test {
now += Duration::from_secs(1); now += Duration::from_secs(1);
assert_eq!( assert_eq!(
state.get_outcome(now).visibility, state.get_outcome(now).panel,
animation::Outcome::Hidden, animation::Outcome::Hidden,
"Failed to release forced visibility: {:?}", "Failed to release forced visibility: {:?}",
now.saturating_duration_since(start), now.saturating_duration_since(start),
@ -380,12 +659,13 @@ mod test {
im: InputMethod::Active(imdetails_new()), im: InputMethod::Active(imdetails_new()),
physical_keyboard: Presence::Missing, physical_keyboard: Presence::Missing,
visibility_override: visibility::State::NotForced, visibility_override: visibility::State::NotForced,
..application_with_fake_output(start)
}; };
now += Duration::from_secs(1); now += Duration::from_secs(1);
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Present), now); let state = state.apply_event(Event::PhysicalKeyboard(Presence::Present), now);
assert_eq!( assert_eq!(
state.get_outcome(now).visibility, state.get_outcome(now).panel,
animation::Outcome::Hidden, animation::Outcome::Hidden,
"Failed to hide: {:?}", "Failed to hide: {:?}",
now.saturating_duration_since(start), now.saturating_duration_since(start),
@ -397,7 +677,7 @@ mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now); let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
assert_eq!( assert_eq!(
state.get_outcome(now).visibility, state.get_outcome(now).panel,
animation::Outcome::Hidden, animation::Outcome::Hidden,
"Failed to remain hidden: {:?}", "Failed to remain hidden: {:?}",
now.saturating_duration_since(start), now.saturating_duration_since(start),
@ -406,12 +686,40 @@ mod test {
now += Duration::from_secs(1); now += Duration::from_secs(1);
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now); let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now);
assert_eq!( assert_matches!(
state.get_outcome(now).visibility, state.get_outcome(now).panel,
animation::Outcome::Visible, animation::Outcome::Visible{..},
"Failed to appear: {:?}", "Failed to appear: {:?}",
now.saturating_duration_since(start), now.saturating_duration_since(start),
); );
} }
#[test]
fn size_l5() {
use crate::outputs::{Mode, Geometry, c, Size};
assert_eq!(
Application::get_preferred_height_and_arrangement(&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,
},
ArrangementKind::Base,
)),
);
}
} }

View File

@ -21,7 +21,7 @@
use std::env; use std::env;
use ::logging; use ::logging;
use glib::object::ObjectExt; use glib::prelude::ObjectExt;
use logging::Warn; use logging::Warn;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
@ -31,7 +31,7 @@ pub mod c {
use gtk; use gtk;
use gtk_sys; use gtk_sys;
use gtk::CssProviderExt; use gtk::prelude::CssProviderExt;
use glib::translate::ToGlibPtr; use glib::translate::ToGlibPtr;
/// Loads the layout style based on current theme /// Loads the layout style based on current theme
@ -40,8 +40,13 @@ pub mod c {
pub extern "C" pub extern "C"
fn squeek_load_style() -> *const gtk_sys::GtkCssProvider { fn squeek_load_style() -> *const gtk_sys::GtkCssProvider {
unsafe { gtk::set_initialized() }; 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); let css_name = path_from_theme(theme);
@ -93,19 +98,31 @@ fn get_theme_name(settings: &gtk::Settings) -> GtkTheme {
e e
}).ok(); }).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 { match env_theme {
Some(theme) => theme, Some(theme) => theme,
None => GtkTheme { None => GtkTheme {
name: { name: {
settings.get_property("gtk-theme-name") prop(settings, "gtk-theme-name")
.or_print(logging::Problem::Surprise, "No 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()) .unwrap_or(DEFAULT_THEME_NAME.into())
}, },
variant: { variant: {
settings.get_property("gtk-application-prefer-dark-theme") prop(settings, "gtk-application-prefer-dark-theme")
.or_print(logging::Problem::Surprise, "No settings key") .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 { .and_then(|dark_preferred| match dark_preferred {
true => Some("dark".into()), true => Some("dark".into()),
false => None, false => None,

View File

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

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