Compare commits

..

78 Commits

Author SHA1 Message Date
8aefae9634 Release 1.13.0 "Externality"
Changes:

- A system for latching and locking views
- Some crash fixes
- Locale-flavored terminals
- A way to add locale-flavored emoji and numbers layouts
- Making the code less annoying to work with in some places
- Fixed layout changes on newer GSettings
- Mod4 modifier
- Better session registration
- New layouts: Dvorak, Colemak wide, hebrew, wide Thai
2021-04-12 10:52:17 +00:00
52e2384f72 Cargo: Version bump 2021-04-12 10:36:55 +00:00
0eb9d89de3 Merge branch 'hints' into 'master'
layout: Take into account text purpose again

Closes #277

See merge request Librem5/squeekboard!448
2021-04-12 10:24:19 +00:00
c23b6f5f50 Merge branch 'layout' into 'master'
layouts: Stop assuming that layout name always changes on switch

Closes #276

See merge request Librem5/squeekboard!450
2021-04-12 10:21:35 +00:00
e4f1b121eb Merge branch 'errors' into 'master'
rust: Fix compiler warnings

See merge request Librem5/squeekboard!445
2021-04-09 08:36:06 +00:00
4f3bec3989 Merge branch 'doc' into 'master'
docs: Describe view switching

See merge request Librem5/squeekboard!443
2021-04-09 08:35:44 +00:00
4fdce2802a layouts: Stop assuming that layout name always changes on switch
Layout type switching outside of overlay was always done with gsettings in the middle, assuming that all clicks on languages in the popover result in a gsettings event. That's a bad assumption if there's only one xkb lang present.

This is a simple work around. A better solution would be to turn the entire system of layout switching into a central object that receives messages about changes that need to be applied, and then applies them.
2021-04-07 15:26:18 +00:00
1908769032 layouts: Make selection testable
From now on, all the parameters for loading layout are handled inside a single pure function, which makes them possible to test.

As a side benefit, the old preference order function composed of a mess of nested procedures is gone.
2021-04-05 10:36:41 +00:00
93e4345e82 layout: Take into account text purpose again 2021-04-05 08:17:07 +00:00
acac00bc88 Merge branch 'terminal' into 'master'
Fix emoji and number layouts

See merge request Librem5/squeekboard!444
2021-04-01 18:59:47 +00:00
ac03c26d5d rust: Fix compiler warnings 2021-04-01 16:30:53 +00:00
608be930f7 layout selection: Fix emoji and number 2021-04-01 16:24:06 +00:00
36d4f6726c language-terminal: Place keyboards in a sub-path 2021-04-01 16:05:44 +00:00
d289129404 Merge branch 'master' into 'master'
Rust additions to make terminal keyboard shows a localized layout

See merge request Librem5/squeekboard!437
2021-04-01 15:17:54 +00:00
M33
e49de34d0e Revert "Update tests/meson.build"
This reverts commit 27cd8964d02e957fdecd00bfebc0a03e4b24ffe4
2021-04-01 15:17:53 +00:00
225e53df07 docs: Describe view switching 2021-04-01 12:41:51 +00:00
37820bf169 Merge branch 'latch' into 'master'
Latch views

See merge request Librem5/squeekboard!416
2021-03-31 15:29:48 +00:00
d8e58fd774 Merge branch 'keyboard-layout-hebrew' into 'master'
moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations.

See merge request Librem5/squeekboard!442
2021-03-31 12:04:37 +00:00
fcb57c9093 moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations.
Signed-off-by: Kozova1 <mug66kk@gmail.com>
2021-03-31 13:01:52 +03:00
b578414655 Merge remote-tracking branch 'upstream/master' into latch 2021-03-31 09:48:29 +00:00
89b1f51ed5 appearance: Colour latched/locked according to design 2021-03-31 09:13:51 +00:00
7b1755a489 renderer: Mark latched buttons differently than locked
There are some hacks here in the form of an extra field "appears_locked_from", which can be used to hint that the user should see the button as locked. Without it, there's some confusion on user side regarding buttons that change states unprompted.
2021-03-31 09:11:11 +00:00
676a2b60ac layout: Make it possible to opt out of latching per-key 2021-03-31 09:09:38 +00:00
32dc25dfbf Revert "moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations."
This reverts commit d8ca9f47ca.

This touches way more than advertised.
2021-03-31 08:39:57 +00:00
484d64cfb9 Merge branch 'keyboard-layout-hebrew-fix-layout' into 'master'
Fix Hebrew layout inconsistencies with standard hebrew layout.

Closes #272

See merge request Librem5/squeekboard!440
2021-03-31 08:27:02 +00:00
637da2c177 Merge branch 'gnome-session' into 'master'
Complete session registration

Closes #274

See merge request Librem5/squeekboard!441
2021-03-31 08:17:39 +00:00
3210a363ab Merge branch 'keyboard-layout-hebrew' into 'master'
Add Hebrew translations for most layouts.

See merge request Librem5/squeekboard!439
2021-03-30 17:55:11 +00:00
8da8d55b98 Fixed Hebrew layout.
The Hebrew layout was a non standard one -
this should now be fixed.
The left shift key was removed, since Hebrew
does not have capital letters.

Signed-off-by: Kozova1 <mug66kk@gmail.com>
2021-03-30 20:31:11 +03:00
d8ca9f47ca moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations.
Signed-off-by: Kozova1 <mug66kk@gmail.com>
2021-03-30 20:30:53 +03:00
a3638f4bfb Added Hebrew translations for most layouts.
This commit adds translations in Hebrew for most layouts.
Note: the hebrew file seems to be named incorrectly,
is that intentional? (he_IL.txt instead of he-IL.txt)

Signed-off-by: Kozova1 <mug66kk@gmail.com>
2021-03-30 20:29:21 +03:00
f45f2db948 Merge branch 'crash' into 'master'
Stop requiring a renderer to be present to get a transformation

Closes #270

See merge request Librem5/squeekboard!438
2021-03-30 13:45:32 +00:00
40bf3ca5de server-main: Properly register to gnome-session
So far squeeboard only did half of the registration failing
to respond to the signals sent by the session.

This causes problems e.g. when exiting the session since the it
thinks the client hangs or is busy.

Closes: #274
2021-03-25 18:11:39 +01:00
e800a88893 server-main: Add quit()
This allows to exit the mainloop e.g. when signalled from gnome-session
or by a signal.
2021-03-25 16:03:57 +01:00
24c3fac505 renderer: Split mutable geometry and place it directly in GtkKeyboard
Geometry is now permanently married to the widget rather the renderer. While geometry is not always defined, C doesn't support sum types, so checks won't be enforced by the compiler. It's OK to pretend there's always some geometry to avoid crashes.
2021-03-17 14:37:54 +00:00
46f8790fc0 renderer: Reduce reliance on knowing the transform 2021-03-17 13:29:02 +00:00
3cdced0c0c Merge branch 'fixes' into 'master'
Rust fixes

See merge request Librem5/squeekboard!435
2021-03-17 13:21:46 +00:00
bffd212e10 Merge branch 'it' into 'master'
italian: Fix colon

See merge request Librem5/squeekboard!434
2021-03-09 16:03:38 +00:00
c2c379b870 Rust: Remove unnecessary no_mangle statements to silence warnings 2021-03-09 14:59:23 +00:00
1ae29ff7bc popover: Fix prematurely deallocated CString 2021-03-09 14:36:37 +00:00
d3cd7dc11f italian: Fix colon 2021-03-09 14:09:59 +00:00
d3695d3bc9 Merge branch 'th-enhancement' into 'master'
Add wide Thai keyboard layout

See merge request Librem5/squeekboard!433
2021-02-13 16:01:45 +00:00
11952ed29a Add wide Thai keyboard layout 2021-02-13 15:11:46 +00:00
842e616cd3 Merge branch 'master' into 'master'
Add Mod4 (Windows) key

See merge request Librem5/squeekboard!432
2021-02-11 17:44:02 +00:00
a265427e8e Add Mod4 (Windows) key 2021-02-11 18:06:49 +01:00
e6c45a63fb Merge branch 'master' into 'master'
Added hebrew keyboard layout

See merge request Librem5/squeekboard!430
2021-02-06 10:45:12 +00:00
e82e256581 Added hebrew keyboard layout 2021-02-06 10:45:12 +00:00
bedabb6188 Merge branch 'catalan' into 'master'
Catalan keyboard layout

See merge request Librem5/squeekboard!431
2021-02-06 10:38:18 +00:00
a030f55a7c Catalan keyboard layout
Modified by Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
2021-02-05 14:51:21 +00:00
49aa4256a9 Merge branch 'keyboard-layout-us+dvorak' into 'master'
Add US Dvorak layout (and Colemak wide)

See merge request Librem5/squeekboard!427
2021-01-26 09:07:22 +00:00
eb7d0d5db9 Add US Dvorak layout (and Colemak wide)
Signed-off-by: Dave Laub <purism@laubster.org>
2021-01-24 10:48:12 -07:00
575619e812 Merge branch 'tagfix' into 'master'
CI: fix xheck_tag to be compatible with Amber

See merge request Librem5/squeekboard!428
2021-01-24 06:28:26 +00:00
630cfc8e59 CI: fix xheck_tag to be compatible with Amber 2021-01-24 06:07:06 +00:00
2a11bce945 Merge branch 'serial' into 'master'
imservice: Increment serials on receiving done, not sending commit

See merge request Librem5/squeekboard!426
2021-01-24 05:14:57 +00:00
a332efca45 Add US Dvorak layout (and Colemak wide)
Signed-off-by: Dave Laub <purism@laubster.org>
2021-01-23 16:44:50 -07:00
5b3c185a16 Merge branch '1.12' into 'master'
Release 1.12.0

See merge request Librem5/squeekboard!422
2021-01-19 14:06:11 +00:00
fefebf7f6e Release 1.12.0 "Convolution"
User-visible changes:
- Fixed a crash related to making keyboard visible.
- Better fallback: when selecting a missing layout named "fr+foo", "fr" will be used instead.
- When enabling the keyboard manually, it will never be stuck in the numbers view.
- Thai layout
- US-Colemak layout
- Czech layouts
- Esperanto layout
- Bulgarian layout
- Improved Norwegian layout

That's a lot of new layouts!

Plus a bunch of stricter warnings, MIPS64el support, and reproducible building.
2021-01-11 11:43:39 +00:00
21c3a74019 cargo: Update dependencies for release 2021-01-11 11:43:39 +00:00
47a483da2a Merge branch 'idle' into 'master'
visibility: Stop calling GTK functions from the visibility manager

See merge request Librem5/squeekboard!425
2021-01-10 11:47:51 +00:00
0c179560b3 visibility: Stop calling GTK functions from the visibility manager
The viibility manager state is changed from various handlers, which are not guaranteed to be reentrant, most notably the Wayland handler for preedit done.
As the state is changed, relevant requests to synchronize user-visible UI are fired from the same handler.

In case of imservice_handle_done, GTK widget show function was being called, which triggered another round of handling Wayland, leading to the done handler being called again, and flaking out.

To solve this, the phase of issuing commands needs to be separate from adjusting desired state. It seems that the easiest solution is to delay the show() and hide() calls into the next GTK main loop spin.

A better solution would probably inject itself directly after the change of desired state, so that *all* the side effects are delayed.
2021-01-10 11:04:02 +00:00
38842f9743 input-method: Fix commit/done mixup in protocol text 2021-01-10 10:45:14 +00:00
3cbfd8351c imservice: Increment serials on receiving done, not sending commit
No idea how that managed to stay undetected for so long.
2021-01-10 10:41:17 +00:00
6e7c0e6f67 Merge branch 'keyboard-layout-farsi' into 'master'
Farsi/Persian keyboard layout

See merge request Librem5/squeekboard!424
2020-12-30 13:54:49 +00:00
0e83697b61 Updated the layout to provide more convenient and faster typing experience 2020-12-27 12:38:29 -05:00
66c3926eb2 Added requirements to resources.rs and meson.build 2020-12-27 02:01:54 -05:00
1856e7023d Farsi/Persian keyboard layout 2020-12-27 01:48:33 -05:00
976f0a6e37 Farsi/Persian keyboard layout 2020-12-27 01:08:07 -05:00
4d24af4e1a Merge branch 'test_docs' into 'master'
Make layout lists more ordered

See merge request Librem5/squeekboard!417
2020-12-21 14:40:11 +00:00
422d06d582 Merge branch 'reset_hint_smart' into 'master'
dbus: Reset hints if text input missing

Closes #256

See merge request Librem5/squeekboard!421
2020-12-16 16:00:53 +00:00
4890c86b4e dbus: Reset hints if text input missing 2020-12-15 13:20:34 +00:00
658df98e18 layout_names: Unmess the list of builtin layouts 2020-12-12 07:42:32 +00:00
6f7252ec7c tests: Add some description to the list of tested layouts
Contributors have started to make it messy.
2020-12-12 07:40:51 +00:00
c6cc58fd8e ffi: Eliminate squeek_button and squeek_row 2020-12-04 17:34:53 +00:00
9522d4e302 renderer: Bring button drawing closer to Rust 2020-12-04 15:35:02 +00:00
8f62520648 view: Ąto-unlatching when multiple latching buttons pressed
Best seen in the PL layout, where to get to Ą, two buttons must be latched: Capitals, and then Accents.
2020-12-04 10:02:04 +00:00
e36c4e597f layout: Remove the little abomination of view change promise
It didn't make anything more testable due to being tightly coupled to Layout.
With the last place needing the curent form abolished, it's no longer needed.

No attempt to make it more stateless and unit-testable was made though.
2020-12-03 19:56:48 +00:00
8ab6997b21 layout: Plug in stateless view switching 2020-12-03 19:44:26 +00:00
3b06eadef5 layout: Add stateless view switching
Tested, but not yet plugged in.
2020-12-03 19:35:50 +00:00
287e851770 layout: Latch keys when clicked twice
Third click unlatches. No actual UI indication.
2020-12-03 18:42:24 +00:00
58 changed files with 4184 additions and 475 deletions

48
Cargo.lock generated
View File

@ -59,9 +59,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.65"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
[[package]]
name = "clap"
@ -76,9 +76,9 @@ dependencies = [
[[package]]
name = "dtoa"
version = "0.4.6"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "fragile"
@ -265,15 +265,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.80"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "linked-hash-map"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "maplit"
@ -326,18 +326,18 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
@ -353,9 +353,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.21"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]]
name = "rs"
@ -380,18 +380,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.117"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.117"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote",
@ -400,9 +400,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.8.14"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7"
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
dependencies = [
"dtoa",
"linked-hash-map",
@ -412,9 +412,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.48"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2",
"quote",
@ -476,9 +476,9 @@ dependencies = [
[[package]]
name = "yaml-rust"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -0,0 +1,87 @@
---
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 ? period 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 ¿ period Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! = BackSpace"
- "show_letters show_eschars preferences space ? period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers \\ / < > = [ ] BackSpace"
- "show_letters show_eschars preferences space ? period Return"
eschars:
- "á é í ó ú Á É Í Ó Ú"
- "à è ì ò ù À È Ì Ò Ù"
- "show_numbers ü ç ï Ü Ç Ï ¡ 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: "default"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "altline"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "àÀ"
period:
outline: "default"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "altline"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

71
data/keyboards/il.yaml Normal file
View File

@ -0,0 +1,71 @@
---
outlines:
default: { width: 40, height: 60 }
altline: { width: 56, height: 60 }
wide: { width: 62, height: 60 }
spaceline: { width: 142, height: 60 }
special: { width: 44, height: 60 }
views:
base:
- "' - ק ר א ט ו ן ם פ"
- "ש ד ג כ ע י ח ל ך ף"
- ס ב ה נ מ צ ת ץ BackSpace"
- "show_numbers comma 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:
BackSpace:
outline: "default"
icon: "edit-clear-symbolic"
action: erase
comma:
outline: "special"
text: ","
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: ":"

78
data/keyboards/ir.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:
- "۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰"
- "@ # ﷼ % & - _ + ( )"
- "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,78 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 108, height: 42 }
spaceline: { width: 216, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "ض ص ق ف غ ع ه خ ح ج"
- "ش س ی ب ل ا ت ن م ک"
- "Shift_L ظ ط ز ر ذ د و BackSpace"
- "show_numbers preferences space period Return"
upper:
- "پ { } [ ] ّ َ ِ ُ چ"
- "ؤ‌ ئ ي‌ إ أ آ ة‌ » « گ"
- "Shift_L ك ٓ ژ ء > < ؟ BackSpace"
- "show_numbers preferences space period Return"
numbers:
- "۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰"
- "@ # ﷼ % & - _ + ( )"
- "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: "*/="
".":
outline: "special"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

View File

@ -22,7 +22,7 @@ views:
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! = BackSpace"
- "show_symbols , \" ' : ; ! = BackSpace"
- "show_letters show_eschars preferences space ? . Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
@ -86,7 +86,4 @@ buttons:
outline: "altline"
icon: "key-enter"
keysym: "Return"
colon:
label: ":"
"\"":
keysym: "quotedbl"

View File

@ -22,7 +22,7 @@ views:
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_symbols , \" ' : ; ! ? BackSpace"
- "show_letters show_eschars preferences space ? . Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
@ -84,7 +84,4 @@ buttons:
outline: "altline"
icon: "key-enter"
keysym: "Return"
colon:
label: ":"
"\"":
keysym: "quotedbl"

View File

@ -52,6 +52,8 @@ buttons:
locking:
lock_view: "upper_accents"
unlock_view: "accents"
looks_locked_from:
- "upper"
outline: "altline"
icon: "key-shift"
BackSpace:
@ -94,6 +96,8 @@ buttons:
locking:
lock_view: "upper_accents"
unlock_view: "upper"
looks_locked_from:
- "accents"
outline: "altline"
label: "ĄĘ"
period:

View File

@ -0,0 +1,220 @@
---
outlines:
action: { width: 59, height: 46 }
small: { width: 50, height: 22 }
default: { width: 35.33, height: 46 }
altline: { width: 48, height: 46 }
wide: { width: 50, height: 46 }
spaceline: { width: 110, height: 46 }
special: { width: 44, height: 46 }
views:
base:
- "Ctrl Alt Tabsmall ↑ ↓ ← →"
- "a z e r t y u i o p"
- "q s d f g h j k l m"
- "Shift_L w x c v b n period BackSpace"
- "show_numbers preferences space show_eschars show_actions Return"
upper:
- "Ctrl Alt Tabsmall PgUp PgDn Home End"
- "A Z E R T Y U I O P"
- "Q S D F G H J K L M"
- "Shift_L W X C V B N , BackSpace"
- "show_numbers preferences space 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: "."
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,223 @@
---
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 ↑ ↓ ← →"
- "a z e r t y u i o p"
- "q s d f g h j k l m"
- "Shift_L w x c v b n period BackSpace"
- "show_numbers preferences space show_eschars show_actions Return"
upper:
- "EscSmall TabSmall Ctrl Alt PgUp PgDn Home End"
- "A Z E R T Y U I O P"
- "Q S D F G H J K L M"
- "Shift_L W X C V B N , BackSpace"
- "show_numbers preferences space 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: "."
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

@ -147,9 +147,6 @@ buttons:
Pause:
outline: "action"
keysym: "Pause"
Menu:
outline: "action"
keysym: "Menu"
Break:
outline: "action"
keysym: "Break"
@ -201,4 +198,3 @@ buttons:
modifier: "Alt"
outline: "small"
label: "Alt"

View File

@ -155,9 +155,6 @@ buttons:
Pause:
outline: "action"
keysym: "Pause"
Menu:
outline: "action"
keysym: "Menu"
Break:
outline: "action"
keysym: "Break"

View File

@ -0,0 +1,84 @@
---
outlines:
default: { width: 75, height: 56 }
altline: { width: 75, height: 56 }
wide: { width: 135, height: 56 }
spaceline: { width: 450, height: 56 }
spacelinesymbol: { width: 300, height: 56 }
special: { width: 90, height: 56 }
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 spacesymbol period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences spacesymbol 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: "กขค"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
period:
outline: "special"
text: "."
space:
outline: "spaceline"
text: " "
spacesymbol:
outline: "spacelinesymbol"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

View File

@ -0,0 +1,78 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 108, height: 42 }
spaceline: { width: 216, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "q w f p g j l u y"
- "a r s t d h n e i o"
- "Shift_L z x c v b k m BackSpace"
- "show_numbers preferences space . Return"
upper:
- "Q W F P G J L U Y"
- "A R S T D H N E I O"
- "Shift_L Z X C V B K M BackSpace"
- "show_numbers preferences space . Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space . Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space . Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
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: "*/="
".":
outline: "special"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

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: 142, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "Shift_L p y f g c r l BackSpace"
- "a o e u i d h t n s"
- ", q j k x b m w v z"
- "show_numbers preferences space period Return"
upper:
- "Shift_L P Y F G C R L BackSpace"
- "A O E U I D H T N S"
- ", Q J K X B M W V Z"
- "show_numbers preferences space period Return"
numbers:
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "* # $ / & - _ + ( )"
- "1 2 3 4 5 6 7 8 9 0"
- "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: ":"
# The US QWERTY layout has fewer letters on the third row, and so has
# the shift & backspace keys placed there. In contrast, the US DVORAK
# layout has fewer letters on the first row, which makes it a good
# choice for the shift & backspace keys. That leads to what may be,
# for many people, an unexpected layout in numbers mode: the numerals
# are on the third row (not the first) so that the backspace key
# remains in a consistent location regardless of mode, without
# sacrificing key width. (Once could argue that in numbers mode, the
# numerals should be closer to the enter key.) As with any keyboard
# layout, familiarity comes with repeated use.

View File

@ -0,0 +1,89 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 108, height: 42 }
spaceline: { width: 216, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "Shift_L p y f g c r l BackSpace"
- "a o e u i d h t n s"
- ", q j k x b m w v z"
- "show_numbers preferences space period Return"
upper:
- "Shift_L P Y F G C R L BackSpace"
- "A O E U I D H T N S"
- ", Q J K X B M W V Z"
- "show_numbers preferences space period Return"
numbers:
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "* # $ / & - _ + ( )"
- "1 2 3 4 5 6 7 8 9 0"
- "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: ":"
# The US QWERTY layout has fewer letters on the third row, and so has
# the shift & backspace keys placed there. In contrast, the US DVORAK
# layout has fewer letters on the first row, which makes it a good
# choice for the shift & backspace keys. That leads to what may be,
# for many people, an unexpected layout in numbers mode: the numerals
# are on the third row (not the first) so that the backspace key
# remains in a consistent location regardless of mode, without
# sacrificing key width. (Once could argue that in numbers mode, the
# numerals should be closer to the enter key.) As with any keyboard
# layout, familiarity comes with repeated use.

2
data/langs/fa-IR.txt Normal file
View File

@ -0,0 +1,2 @@
emoji ایموجی
terminal ترمینال

19
data/langs/he-IL.txt Normal file
View File

@ -0,0 +1,19 @@
be בלגית
br פורטוגזית (ברזיל)
cz צ'כית
de גרמנית
dk דנית
es ספרדית
emoji אימוג'י
fi פינית
fr צרפתית
gr יוונית
il עברית
it איטלקית
no נורווגית
pl פולנית
ru רוסית
se שוודית
terminal טרמינל
ua אוקראינית
us אנגלית (ארה"ב)

View File

@ -31,11 +31,16 @@ sq_button.wide {
border-color: #3e3a44;
}
sq_button.locked {
sq_button.latched {
background: #ffffff;
color: #2b292f;
}
sq_button.locked {
background: #ffffff;
color: #1c71d8;
}
sq_button.action {
font-size: 0.75em;
}

View File

@ -34,11 +34,16 @@ sq_button.wide {
border-color: @borders; /* #3e3a44; */
}
sq_button.locked {
sq_button.latched {
background: @theme_fg_color; /*#ffffff;*/
color: @theme_bg_color; /*#2b292f;*/
}
sq_button.locked {
background: @theme_fg_color; /*#ffffff;*/
color: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#2b292f;*/
}
sq_button.action {
font-size: 0.75em;
}

122
debian/changelog vendored
View File

@ -1,3 +1,125 @@
squeekboard (1.13.0pureos0~amber0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* layout: Latch keys when clicked twice
* layout: Add stateless view switching
* layout: Plug in stateless view switching
* layout: Remove the little abomination of view change promise
* view: Ąto-unlatching when multiple latching buttons pressed
* renderer: Bring button drawing closer to Rust
* ffi: Eliminate squeek_button and squeek_row
* imservice: Increment serials on receiving done, not sending commit
* input-method: Fix commit/done mixup in protocol text
* CI: fix xheck_tag to be compatible with Amber
* italian: Fix colon
* popover: Fix prematurely deallocated CString
* Rust: Remove unnecessary no_mangle statements to silence warnings
* renderer: Reduce reliance on knowing the transform
* renderer: Split mutable geometry and place it directly in GtkKeyboard
* Revert "moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations."
* layout: Make it possible to opt out of latching per-key
* renderer: Mark latched buttons differently than locked
* appearance: Colour latched/locked according to design
* docs: Describe view switching
* language-terminal: Place keyboards in a sub-path
* layout selection: Fix emoji and number
* rust: Fix compiler warnings
* layout: Take into account text purpose again
* layouts: Make selection testable
* layouts: Stop assuming that layout name always changes on switch
* Cargo: Version bump
[ J.D. Laub ]
* Add US Dvorak layout (and Colemak wide)
* Add US Dvorak layout (and Colemak wide)
[ Jordi Masip ]
* Catalan keyboard layout
[ Myth ]
* Added hebrew keyboard layout
[ David96 ]
* Add Mod4 (Windows) key
[ Panawat Wong-klaew ]
* Add wide Thai keyboard layout
[ Guido Günther ]
* server-main: Add quit()
* server-main: Properly register to gnome-session (Closes: #274)
[ Kozova1 ]
* Added Hebrew translations for most layouts.
* moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations.
* Fixed Hebrew layout.
* moved data/langs/he_IL.txt -> data/langs/he-IL.txt to better conform with existing translations.
[ M33 ]
* Revert "Update tests/meson.build"
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 12 Apr 2021 10:40:32 +0000
squeekboard (1.12.0pureos0~amber0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* docs: Correct Cargo update instructions
* visibility: Centralize keyboard panel visibility policy and handling
* build: Fix release
* tests: Prefer the env var for finding test layouts
* tests: Explicitly pass source directory to tests
* debian: Build reproducibly
* tests: Allow legacy mode to have much longer tests.
* build: Enable unused warnings in C
* build: Enable wformat to remove warnings about missing wformat
* build: Fail on any C warnings when strict
* data: Made data flow in fallback clearer
* data: Flattened layout fallback function
* layouts: Use base as fallback for alternative layouts
* layouts: Simplify the main flow of source list
* tests: Add some description to the list of tested layouts
* layout_names: Unmess the list of builtin layouts
* dbus: Reset hints if text input missing
* visibility: Stop calling GTK functions from the visibility manager
[ Wannaphong Phatthiyaphaibun ]
* Add thai keyboard
* Update resources.rs
* Update meson.build
* escape " on thai keyboard
[ clonex10100 ]
* Added US Colemak Keyboard Layout
[ Henry-Nicolas Tourneur ]
* d/rules: fix an FTBFS on mips64el with GOT > 64kb
* d/rules: export RUSTFLAGS only on architecture that needs it
* d/rules: export RUSTFLAGS only on architecture that needs it
[ Jiří Stránský ]
* Add Czech keyboard layouts
[ Stefan Grotz ]
* Esperanto keyboard
[ Vladimir ]
* Bulgarian language keyboard layout
[ Vladimir Stoilov ]
* bulgarian add translation and to needed lists
* Fix bulgarian layout size
[ Andreas Rönnquist ]
* no: Use wide button switching between numbers, symbols and base
[ jranaraki ]
* Farsi/Persian keyboard layout
* Farsi/Persian keyboard layout
* Added requirements to resources.rs and meson.build
* Updated the layout to provide more convenient and faster typing experience
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Sun, 10 Jan 2021 09:43:42 +0000
squeekboard (1.11.1) amber-phone; urgency=medium
[ Mark Müller ]

View File

@ -5,6 +5,7 @@ Feed it the first changelog line, and then all available tags.
"""
import re, sys
tag = "v" + re.findall("\\((.*)\\)", input())[0]
version = re.findall("\\((.*)\\)", input())[0]
tag = 'v' + re.findall("([0-9]+\\.[0-9]+\\.[0-9]+).*", version)[0]
if tag not in map(str.strip, sys.stdin.readlines()):
raise Exception("Changelog's current version doesn't have a tag. Push the tag!")

View File

@ -6,6 +6,7 @@ Contents
* [Tutorial](tutorial.md)
* [Contributing](hacking.md)
* [Switching views](views.md)
Introduction
------------
@ -21,6 +22,8 @@ Layouts are created using a text-based format, based on YAML.
TODO: Provide a description of the format.
Squeekboard layouts are separated into *views* and use a *room metaphor* to [switch views](views.md).
Contributions
-------------

594
doc/latching.svg Normal file
View File

@ -0,0 +1,594 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="177.92439mm"
height="88.144363mm"
viewBox="0 0 177.92439 88.144364"
version="1.1"
id="svg8"
sodipodi:docname="latching.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<defs
id="defs2">
<marker
style="overflow:visible"
id="Arrow1Lend"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Mend"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend"
inkscape:isstock="true">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1098" />
</marker>
<marker
style="overflow:visible"
id="Arrow2Sstart"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Sstart"
inkscape:isstock="true">
<path
transform="matrix(0.3,0,0,0.3,-0.69,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path1119" />
</marker>
<marker
style="overflow:visible"
id="Arrow2Mstart"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Mstart"
inkscape:isstock="true">
<path
transform="scale(0.6)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path1113" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-5" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect848" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect848-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-9" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect848-8" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect916" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-4" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect951" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect986" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect1061" />
<marker
style="overflow:visible"
id="Arrow1Lend-5"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-7" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect1549" />
<marker
style="overflow:visible"
id="Arrow1Lend-8"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#101010;fill-opacity:1;fill-rule:evenodd;stroke:#101010;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-8" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-3" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect1845" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-38" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect1960" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-3-9" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect2304" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-3-4" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect2304-7" />
<marker
style="overflow:visible"
id="Arrow1Lend-2"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#6c6c6c;fill-opacity:1;fill-rule:evenodd;stroke:#6c6c6c;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-89" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Lend-2-6"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-89-8" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-21" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect2574" />
<marker
style="overflow:visible"
id="Arrow1Lend-2-6-1"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-89-8-0" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Lend-2-6-6"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#00ad12;fill-opacity:1;fill-rule:evenodd;stroke:#00ad12;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-89-8-2" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10-1-8" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect3021" />
<marker
style="overflow:visible"
id="Arrow1Lend-8-3"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#101010;fill-opacity:1;fill-rule:evenodd;stroke:#101010;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-8-5" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3358025"
inkscape:cx="212.63846"
inkscape:cy="105.21093"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
fit-margin-top="4"
fit-margin-left="4"
fit-margin-right="4"
fit-margin-bottom="4"
lock-margins="true"
inkscape:window-width="1298"
inkscape:window-height="708"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-72.097892,-53.326191)">
<path
style="fill:none;fill-rule:evenodd;stroke:#101010;stroke-width:0.264999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-8-3)"
d="m 134.9616,86.141869 c 17.5759,-11.622767 35.93283,0 35.93283,0"
id="path1087-1-6" />
<g
id="g2948">
<circle
style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1065-6"
cx="129.67093"
cy="92.793152"
r="7.8844509" />
<text
xml:space="preserve"
id="text833-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(46.071199,-1.2662626)"><tspan
x="80.886719"
y="96.225952"><tspan>A</tspan></tspan></text>
</g>
<rect
style="color:#000000;overflow:visible;fill:#f8f8f8;fill-opacity:1;stroke:#000000;stroke-width:0.555679;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2066"
width="1.4562259"
height="10.90563"
x="81.767418"
y="75.519585"
ry="0.9693895" />
<g
id="g905"
transform="translate(0,-0.85044703)">
<circle
style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1065"
cx="83.154755"
cy="93.6436"
r="7.8844509" />
<text
xml:space="preserve"
id="text833"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none">a</tspan></tspan></text>
</g>
<g
id="g905-3-7"
transform="translate(92.82116,-0.85044861)">
<circle
style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1065-6-7"
cx="83.557831"
cy="93.6436"
r="7.8844509" />
<text
xml:space="preserve"
id="text833-0-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan>Ą</tspan></tspan></text>
</g>
<g
id="g905-1"
transform="translate(68.838914,-17.67039)">
<rect
style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-8"
width="10.583333"
height="13.79613"
x="78.878532"
y="85.920006"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-7"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.2686286,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan> ̨̂ ̈</tspan></tspan></text>
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#101010;stroke-width:0.264999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-8)"
d="m 88.446425,85.89509 c 17.575895,-11.622767 35.932835,0 35.932835,0"
id="path1087-1" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="99.186691"
y="66.221436"
id="text1035-89"><tspan
sodipodi:role="line"
id="tspan1033-6"
x="99.186691"
y="66.221436"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">locking</tspan></text>
<g
id="g905-1-4"
transform="translate(22.423434,-17.67039)">
<rect
style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-8-3"
width="10.583333"
height="13.79613"
x="78.878532"
y="85.920006"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-7-3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-3);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan></tspan></tspan></text>
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.5, 0.5;stroke-dashoffset:0;stroke-opacity:1"
d="m 83.251484,76.550079 c -0.31498,0 -0.944941,-0.314981 -0.944941,0 0,0.31498 0.770221,0.262079 0.944941,0 0.212555,-0.318834 0.08197,-0.862973 -0.188988,-1.133929 -0.132651,-0.13265 -1.931144,0.354037 -2.078869,0.944941 -0.108038,0.432149 0.877467,-0.188989 1.322916,-0.188989 0.339244,0 0.626121,0.262042 0.944941,0.377977 0.374434,0.136158 0.777569,0.199796 1.133928,0.377976 0.113894,0.05695 -0.0037,0.439732 0.944941,0.755952 0.544156,0.181385 1.78847,0.275251 2.267857,0.377976 0.448438,0.09609 0.881944,0.251984 1.322917,0.377977 0.063,0.188988 0.388199,0.566964 0.188988,0.566964 -0.195153,0 -0.745291,-0.93961 0,-0.566964 0.281726,0.140863 0.485859,0.404908 0.755952,0.566964 1.460327,0.876196 3.039871,1.714659 4.346726,2.834821 0.966187,0.828161 1.495207,2.006597 2.645833,2.645834 2.876101,1.597834 5.000414,0.287454 7.370534,1.133929 0.4783,0.170821 0.85136,0.567325 1.32292,0.755952 1.8557,0.74228 0.94424,0.230907 2.26785,0.377976 1.47287,0.16365 2.67387,1.06159 4.15774,0.566965 1.22233,-0.407443 3.2674,-1.377519 4.15774,-2.267858 0.39842,-0.398422 0.42845,-1.053404 0.75595,-1.511905 0.50136,-0.70189 1.27041,-1.107171 1.7009,-1.889881 2.28615,-4.156632 0.41931,-1.931216 3.2128,-4.724702 0.18898,-0.188988 0.33491,-0.434362 0.56696,-0.566964 0.864,-0.493714 5.11938,-0.281812 6.04762,-0.188989 0.34449,0.03445 3.71968,0.978261 3.96875,1.133929 0.35339,0.220868 1.41179,1.650838 1.88988,1.889881 0.80324,0.40162 2.22057,0.354332 3.02381,0.755952 1.24832,0.624162 -0.45041,0.669376 -0.18899,1.322917 0.1193,0.298244 0.64418,0.0762 0.94494,0.188988 0.28702,0.107634 1.48279,1.113549 1.5119,1.133929 2.56034,1.792236 -0.96522,-0.855663 2.26786,1.133928 1.61516,0.993946 2.82802,2.547938 4.53572,3.401785 2.03418,1.017093 4.38487,-0.122885 6.42559,-0.377975 1.6791,-0.209886 3.23813,0.06137 4.91369,-0.566965 0.50424,-0.18909 1.82709,-1.701063 2.26786,-2.078869 0.45926,-0.393653 1.0853,-0.707329 1.5119,-1.133928 0.81561,-0.815609 1.44764,-1.826091 2.45685,-2.456845 0.28768,-0.179799 0.65726,-0.198178 0.94494,-0.377977 0.28791,-0.179945 0.60349,-0.774216 0.94494,-0.94494 0.23232,-0.116159 0.51479,-0.09252 0.75595,-0.188988 0.21089,-0.08436 0.33983,-0.377976 0.56697,-0.377976 0.14086,0 0.23711,0.188988 0.37797,0.188988 0.0891,0 0.126,-0.125992 0.18899,-0.188988 0.18899,-0.063 0.36976,-0.217161 0.56696,-0.188988 0.77174,0.110248 1.92711,0.94494 3.02381,0.94494 0.16785,0 0.94189,-0.862638 1.13393,-0.94494 0.19546,-0.08377 1.57981,-0.364523 1.7009,-0.377977 0.72233,-0.08026 2.11645,0.340388 2.64583,-0.188988"
id="path2010" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="73.802078"
y="72.354546"
id="text2091"><tspan
sodipodi:role="line"
id="tspan2089"
x="73.802078"
y="72.354546"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">SAVED</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="164.55956"
y="72.354546"
id="text2091-8"><tspan
sodipodi:role="line"
id="tspan2089-8"
x="164.55956"
y="72.354546"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">CURRENT</tspan><tspan
sodipodi:role="line"
x="164.55956"
y="79.410095"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
id="tspan3432" /></text>
<g
id="g2259"
transform="matrix(1.0856157,0,0,0.94777147,-19.677062,6.4360598)">
<path
id="rect2111-9"
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 179.53502,72.72924 h 5.84717 l -1.37074,1.931346 h -3.04292 z"
sodipodi:nodetypes="ccccc" />
<path
id="rect2111-9-0"
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 179.4049,80.846668 h 5.84717 l -1.23711,-1.931347 h -3.17655 z"
sodipodi:nodetypes="ccccc" />
<g
id="g2247"
transform="translate(-0.04032786,-0.04319387)">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,75.343053 2.96688,-0.19452"
id="path2145"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,76.112065 2.96688,-0.19452"
id="path2145-3"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,76.881078 2.96688,-0.19452"
id="path2145-0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,77.65009 2.96688,-0.19452"
id="path2145-2"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,78.419102 2.96688,-0.19452"
id="path2145-4"
sodipodi:nodetypes="cc" />
</g>
</g>
<g
id="g905-0"
transform="translate(157.32586,-0.83970203)" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="145.70187"
y="66.221436"
id="text1035-89-2"><tspan
sodipodi:role="line"
id="tspan1033-6-9"
x="145.70187"
y="66.221436"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">locking</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB

631
doc/latching_return.svg Normal file
View File

@ -0,0 +1,631 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="169.9455mm"
height="98.072433mm"
viewBox="0 0 169.94549 98.072434"
version="1.1"
id="svg8"
sodipodi:docname="latching_return.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<defs
id="defs2">
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10-1" />
<marker
style="overflow:visible"
id="Arrow1Lend-5"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-7" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-38" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-3-9" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-3-4" />
<marker
style="overflow:visible"
id="Arrow1Lend-2-6"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-89-8" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-21" />
<marker
style="overflow:visible"
id="Arrow1Lend-2-6-1"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-89-8-0" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Lend-2-6-6"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-89-8-2" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10-1-8" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-12" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10-7" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.94455498"
inkscape:cx="314.8465"
inkscape:cy="213.42055"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
fit-margin-top="4"
fit-margin-left="4"
fit-margin-right="4"
fit-margin-bottom="4"
lock-margins="true"
inkscape:window-width="1298"
inkscape:window-height="708"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
showguides="false" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-70.551731,-46.47634)">
<g
id="g3858"
transform="translate(1.354821,1.4005714)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="204.75821"
y="130.07979"
id="text2293-4"><tspan
sodipodi:role="line"
id="tspan2291-1"
x="204.75821"
y="130.07979"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="194.54915"
y="128.70164"
id="text2293-0-5-8"><tspan
sodipodi:role="line"
id="tspan2291-6-7-5"
x="194.54915"
y="128.70164"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">cuts</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
d="m 210.34971,123.9389 c 0.0233,0.14006 0.07,0.27818 0.07,0.42017 0,0.0738 -0.07,0.13627 -0.07,0.21009 0,0.11902 0.0582,0.23171 0.07,0.35014 0.049,0.49035 0.0857,0.98109 0.14006,1.4706 0.018,0.1624 -0.0269,0.32902 0,0.4902 0.0435,0.26077 0.11166,0.3369 0,0.56023 -0.0209,0.0418 0.033,0.10705 0,0.14006 -0.0369,0.0369 -0.10315,0.0331 -0.14006,0.07"
id="path3816-9" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
d="m 209.9819,128.43627 c -0.12445,0.48957 -0.1295,0.98859 -0.0743,1.48553 0.0118,0.10583 0.01,0.21643 0.0248,0.32186 -0.002,0.0627 0.0396,0.11405 0.0495,0.17332 0.0144,0.0866 -0.0193,0.077 0,0.17331 0.0124,0.0621 0.20619,0.18971 0.24759,0.22283"
id="path3820-7" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-2-6-1)"
d="m 180.7405,85.345284 c 21.77006,-11.52723 44.50758,0 44.50758,0"
id="path1087-3-0-8" />
<rect
style="color:#000000;overflow:visible;fill:#f8f8f8;fill-opacity:1;stroke:#000000;stroke-width:0.555679;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2066"
width="1.4562259"
height="10.90563"
x="81.767418"
y="75.519585"
ry="0.9693895" />
<g
id="g905-3-7"
transform="translate(92.629263,-0.8504468)">
<text
xml:space="preserve"
id="text833-0-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan>Ą</tspan></tspan></text>
<rect
style="color:#000000;overflow:visible;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-4-9"
width="10.583333"
height="13.79613"
x="78.266167"
y="86.745537"
ry="1.937705" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.4em;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="174.86499"
y="130.79413"
id="text1035-8"><tspan
sodipodi:role="line"
x="174.86499"
y="130.79413"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
id="tspan2277">locking</tspan><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
sodipodi:role="line"
x="174.86499"
y="135.11462"
id="tspan1606" /></text>
<g
id="g905-1-6"
transform="translate(90.668095,26.90725)">
<rect
style="color:#000000;overflow:visible;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-8-0"
width="10.583333"
height="13.79613"
x="78.878532"
y="85.920006"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-7-4"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-38);fill:#f8f8f8;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.2686286,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan
style="fill:#f8f8f8;fill-opacity:1"> ̨̂ ̈</tspan></tspan></text>
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.5, 0.5;stroke-dashoffset:0;stroke-opacity:1"
d="m 83.251484,76.550079 c -0.31498,0 -0.944941,-0.314981 -0.944941,0 0,0.31498 0.770221,0.262079 0.944941,0 0.212555,-0.318834 0.08197,-0.862973 -0.188988,-1.133929 -0.132651,-0.13265 -1.931144,0.354037 -2.078869,0.944941 -0.108038,0.432149 0.877467,-0.188989 1.322916,-0.188989 0.339244,0 0.626121,0.262042 0.944941,0.377977 0.374434,0.136158 0.777569,0.199796 1.133928,0.377976 0.113894,0.05695 -0.0037,0.439732 0.944941,0.755952 0.544156,0.181385 1.78847,0.275251 2.267857,0.377976 0.448438,0.09609 0.881944,0.251984 1.322917,0.377977 0.063,0.188988 0.388199,0.566964 0.188988,0.566964 -0.195153,0 -0.745291,-0.93961 0,-0.566964 0.281726,0.140863 0.485859,0.404908 0.755952,0.566964 1.460327,0.876196 3.039871,1.714659 4.346726,2.834821 0.966187,0.828161 1.495207,2.006597 2.645833,2.645834 2.876101,1.597834 5.000414,0.287454 7.370534,1.133929 0.4783,0.170821 0.85136,0.567325 1.32292,0.755952 1.8557,0.74228 0.94424,0.230907 2.26785,0.377976 1.47287,0.16365 2.67387,1.06159 4.15774,0.566965 1.22233,-0.407443 3.2674,-1.377519 4.15774,-2.267858 0.39842,-0.398422 0.42845,-1.053404 0.75595,-1.511905 0.50136,-0.70189 1.27041,-1.107171 1.7009,-1.889881 2.28615,-4.156632 0.41931,-1.931216 3.2128,-4.724702 0.18898,-0.188988 0.33491,-0.434362 0.56696,-0.566964 0.864,-0.493714 5.11938,-0.281812 6.04762,-0.188989 0.34449,0.03445 3.71968,0.978261 3.96875,1.133929 0.35339,0.220868 1.41179,1.650838 1.88988,1.889881 0.80324,0.40162 2.22057,0.354332 3.02381,0.755952 1.24832,0.624162 -0.45041,0.669376 -0.18899,1.322917 0.1193,0.298244 0.64418,0.0762 0.94494,0.188988 0.28702,0.107634 1.48279,1.113549 1.5119,1.133929 2.56034,1.792236 -0.96522,-0.855663 2.26786,1.133928 1.61516,0.993946 2.82802,2.547938 4.53572,3.401785 2.03418,1.017093 4.38487,-0.122885 6.42559,-0.377975 1.6791,-0.209886 3.23813,0.06137 4.91369,-0.566965 0.50424,-0.18909 1.82709,-1.701063 2.26786,-2.078869 0.45926,-0.393653 1.0853,-0.707329 1.5119,-1.133928 0.81561,-0.815609 1.44764,-1.826091 2.45685,-2.456845 0.28768,-0.179799 0.65726,-0.198178 0.94494,-0.377977 0.28791,-0.179945 0.60349,-0.774216 0.94494,-0.94494 0.23232,-0.116159 0.51479,-0.09252 0.75595,-0.188988 0.21089,-0.08436 0.33983,-0.377976 0.56697,-0.377976 0.14086,0 0.23711,0.188988 0.37797,0.188988 0.0891,0 0.126,-0.125992 0.18899,-0.188988 0.18899,-0.063 0.36976,-0.217161 0.56696,-0.188988 0.77174,0.110248 1.92711,0.94494 3.02381,0.94494 0.16785,0 0.94189,-0.862638 1.13393,-0.94494 0.19546,-0.08377 1.57981,-0.364523 1.7009,-0.377977 0.72233,-0.08026 2.11645,0.340388 2.64583,-0.188988"
id="path2010" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="74.331245"
y="71.825378"
id="text2091"><tspan
sodipodi:role="line"
id="tspan2089"
x="74.331245"
y="71.825378"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">SAVED</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;line-height:125%;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="164.23172"
y="70.186089"
id="text2091-8"><tspan
sodipodi:role="line"
id="tspan2089-8"
x="164.23172"
y="70.186089"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">CURRENT</tspan><tspan
sodipodi:role="line"
x="164.23172"
y="77.241638"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Abyssinica SIL';-inkscape-font-specification:'Abyssinica SIL';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
id="tspan3432" /></text>
<g
id="g2259"
transform="matrix(1.0856157,0,0,0.94777147,-20.206229,4.319393)">
<path
id="rect2111-9"
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 179.53502,72.72924 h 5.84717 l -1.37074,1.931346 h -3.04292 z"
sodipodi:nodetypes="ccccc" />
<path
id="rect2111-9-0"
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 179.4049,80.846668 h 5.84717 l -1.23711,-1.931347 h -3.17655 z"
sodipodi:nodetypes="ccccc" />
<g
id="g2247"
transform="translate(-0.04032786,-0.04319387)">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,75.343053 2.96688,-0.19452"
id="path2145"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,76.112065 2.96688,-0.19452"
id="path2145-3"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,76.881078 2.96688,-0.19452"
id="path2145-0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,77.65009 2.96688,-0.19452"
id="path2145-2"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,78.419102 2.96688,-0.19452"
id="path2145-4"
sodipodi:nodetypes="cc" />
</g>
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-5)"
d="m 173.01207,99.691217 c 0,0 -8.37295,13.367043 -0.26038,13.813423 9.36786,0.51544 7.89797,-9.16673 6.50059,-13.41252"
id="path2269"
sodipodi:nodetypes="csc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="176.93353"
y="138.21815"
id="text2293"><tspan
sodipodi:role="line"
id="tspan2291"
x="176.93353"
y="138.21815"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"></tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-2-6)"
d="m 181.46685,99.71436 c 21.61425,11.51564 44.18902,0 44.18902,0"
id="path1087-3-0" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="195.7271"
y="122.96945"
id="text1035-89-7"><tspan
sodipodi:role="line"
id="tspan1033-6-5"
x="195.7271"
y="122.96945"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">set_view</tspan></text>
<g
id="g905-1-4-4"
transform="translate(120.23594,18.477932)">
<rect
style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-8-3-8"
width="10.583333"
height="13.79613"
x="78.878532"
y="85.920006"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-7-3-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-3-4);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan></tspan></tspan></text>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.36em;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="196.99921"
y="61.359337"
id="text1035-89-4"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
sodipodi:role="line"
id="tspan3987"
x="196.99921"
y="61.359337">locking</tspan><tspan
sodipodi:role="line"
x="196.99921"
y="65.362335"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
id="tspan3985">pops: false</tspan></text>
<g
id="g905-1-4-9"
transform="translate(120.23594,-18.828317)">
<rect
style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-8-3-2"
width="10.583333"
height="13.79613"
x="78.878532"
y="85.920006"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-7-3-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-3-9);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan></tspan></tspan></text>
</g>
<g
id="g905-0"
transform="translate(147.8008,-0.83970203)">
<rect
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-5"
width="10.583333"
height="13.79613"
x="77.863091"
y="86.745537"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-21);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan>ą</tspan></tspan></text>
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Lend-2-6-6)"
d="m 170.49256,99.691217 c -49.40563,31.667983 -82.046134,0 -82.046134,0"
id="path1087-3-0-5"
sodipodi:nodetypes="cc" />
<g
id="g905-3-7-6"
transform="translate(45.925334,26.134402)">
<rect
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-4-9-8"
width="10.583333"
height="13.79613"
x="78.266167"
y="86.745537"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-0-5-2"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1-8);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan>Ą</tspan></tspan></text>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.4em;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="129.60217"
y="131.30838"
id="text1035-8-4"><tspan
sodipodi:role="line"
x="129.60217"
y="131.30838"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
id="tspan2277-2">text</tspan><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
sodipodi:role="line"
x="129.60217"
y="135.62888"
id="tspan1606-4" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.58611px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="123.67847"
y="136.8163"
id="text2293-0"><tspan
sodipodi:role="line"
id="tspan2291-6"
x="123.67847"
y="136.8163"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.58611px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">returns</tspan></text>
<g
id="g905"
transform="translate(1.1299757e-7,-0.85044747)">
<circle
style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1065"
cx="83.154755"
cy="93.6436"
r="7.8844509" />
<text
xml:space="preserve"
id="text833"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-12);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none">a</tspan></tspan></text>
</g>
<g
id="g2948">
<circle
style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1065-6"
cx="129.67093"
cy="92.793152"
r="7.8844509" />
<text
xml:space="preserve"
id="text833-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-7);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(46.071199,-1.2662626)"><tspan
x="80.886719"
y="96.225952"><tspan>A</tspan></tspan></text>
</g>
<g
id="g2259-3"
transform="matrix(0.61171471,0,0,0.53404326,8.8050741,94.418409)">
<path
id="rect2111-9-1"
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 179.53502,72.72924 h 5.84717 l -1.37074,1.931346 h -3.04292 z"
sodipodi:nodetypes="ccccc" />
<path
id="rect2111-9-0-7"
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 179.4049,80.846668 h 5.84717 l -1.23711,-1.931347 h -3.17655 z"
sodipodi:nodetypes="ccccc" />
<g
id="g2247-5"
transform="translate(-0.04032786,-0.04319387)">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,75.343053 2.96688,-0.19452"
id="path2145-9"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,76.112065 2.96688,-0.19452"
id="path2145-3-6"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,76.881078 2.96688,-0.19452"
id="path2145-0-2"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,77.65009 2.96688,-0.19452"
id="path2145-2-1"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.363778;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 181.02302,78.419102 2.96688,-0.19452"
id="path2145-4-7"
sodipodi:nodetypes="cc" />
</g>
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.06, 0.53;stroke-dashoffset:0;stroke-opacity:1"
d="m 119.51485,135.86385 c -0.50656,0.62998 -2.26796,0.26701 -2.85105,0.85011 -0.52314,0.52314 2.02401,2.4426 3.96141,3.16913 2.94083,1.1028 4.79827,0.22 7.72476,-0.19807 0.3268,-0.0467 0.66473,0.0543 0.99035,0 1.72749,-0.28792 3.39512,-0.79539 5.14984,-0.99036 1.10207,-0.12245 0.49735,0.0622 1.58457,0.19807 0.91825,0.11479 1.8542,0.0832 2.77299,0.19807 0.86919,0.10865 1.70964,-0.19807 2.57492,-0.19807"
id="path3796" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="166.72447"
y="136.84001"
id="text2293-0-5"><tspan
sodipodi:role="line"
id="tspan2291-6-7"
x="166.72447"
y="136.84001"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">cuts</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529166;stroke-dashoffset:0;stroke-opacity:1"
d="m 182.52503,132.07727 c 0.0233,0.14006 0.07,0.27818 0.07,0.42017 0,0.0738 -0.07,0.13627 -0.07,0.21009 0,0.11902 0.0582,0.23171 0.07,0.35014 0.049,0.49035 0.0857,0.98109 0.14006,1.4706 0.018,0.1624 -0.0269,0.32902 0,0.4902 0.0435,0.26077 0.11166,0.3369 0,0.56023 -0.0209,0.0418 0.033,0.10705 0,0.14006 -0.0369,0.0369 -0.10315,0.0331 -0.14006,0.07"
id="path3816" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529166;stroke-dashoffset:0;stroke-opacity:1"
d="m 182.15722,136.57464 c -0.12445,0.48957 -0.1295,0.98859 -0.0743,1.48553 0.0118,0.10583 0.01,0.21643 0.0248,0.32186 -0.002,0.0627 0.0396,0.11405 0.0495,0.17332 0.0144,0.0866 -0.0193,0.077 0,0.17331 0.0124,0.0621 0.20619,0.18971 0.24759,0.22283"
id="path3820" />
<g
id="g3858-9"
transform="translate(1.354821,-73.436839)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="204.75821"
y="130.07979"
id="text2293-4-6"><tspan
sodipodi:role="line"
id="tspan2291-1-4"
x="204.75821"
y="130.07979"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="194.54915"
y="128.70164"
id="text2293-0-5-8-3"><tspan
sodipodi:role="line"
id="tspan2291-6-7-5-3"
x="194.54915"
y="128.70164"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.5861px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">cuts</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
d="m 210.34971,123.9389 c 0.0233,0.14006 0.07,0.27818 0.07,0.42017 0,0.0738 -0.07,0.13627 -0.07,0.21009 0,0.11902 0.0582,0.23171 0.07,0.35014 0.049,0.49035 0.0857,0.98109 0.14006,1.4706 0.018,0.1624 -0.0269,0.32902 0,0.4902 0.0435,0.26077 0.11166,0.3369 0,0.56023 -0.0209,0.0418 0.033,0.10705 0,0.14006 -0.0369,0.0369 -0.10315,0.0331 -0.14006,0.07"
id="path3816-9-3" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:0;stroke-opacity:1"
d="m 209.9819,128.43627 c -0.12445,0.48957 -0.1295,0.98859 -0.0743,1.48553 0.0118,0.10583 0.01,0.21643 0.0248,0.32186 -0.002,0.0627 0.0396,0.11405 0.0495,0.17332 0.0144,0.0866 -0.0193,0.077 0,0.17331 0.0124,0.0621 0.20619,0.18971 0.24759,0.22283"
id="path3820-7-8" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 39 KiB

386
doc/switching.svg Normal file
View File

@ -0,0 +1,386 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="70.905495mm"
height="78.260262mm"
viewBox="0 0 70.905494 78.260262"
version="1.1"
id="svg8"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
sodipodi:docname="switching.svg">
<defs
id="defs2">
<marker
style="overflow:visible"
id="Arrow1Lend"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Mend"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend"
inkscape:isstock="true">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1098" />
</marker>
<marker
style="overflow:visible"
id="Arrow2Sstart"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Sstart"
inkscape:isstock="true">
<path
transform="matrix(0.3,0,0,0.3,-0.69,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path1119" />
</marker>
<marker
style="overflow:visible"
id="Arrow2Mstart"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Mstart"
inkscape:isstock="true">
<path
transform="scale(0.6)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path1113" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-5" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect848" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect848-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-9" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect848-8" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect916" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-4" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect951" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-10-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect986" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect1061" />
<marker
style="overflow:visible"
id="Arrow1Lend-5"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1092-7" />
</marker>
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect835-2-1" />
<rect
x="80.886902"
y="89.202377"
width="18.898809"
height="20.410713"
id="rect1549" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="136.49044"
inkscape:cy="110.83537"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
fit-margin-top="4"
fit-margin-left="4"
fit-margin-right="4"
fit-margin-bottom="4"
lock-margins="true"
inkscape:window-width="1298"
inkscape:window-height="708"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-70.899889,-58.974186)">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
d="m 88.446426,85.895089 c 17.575894,-11.622767 35.932844,0 35.932844,0"
id="path1087" />
<g
id="g905"
transform="translate(0,-0.85044703)">
<circle
style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1065"
cx="83.03434"
cy="93.834679"
r="7.8844509" />
<text
xml:space="preserve"
id="text833"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.16231537,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none">a</tspan></tspan></text>
</g>
<g
id="g905-3"
transform="translate(46.113101,-0.85044703)">
<circle
style="color:#000000;overflow:visible;fill:#ffffff;stroke:#000000;stroke-width:0.499999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1065-3"
cx="83.557831"
cy="93.366959"
r="7.8844509" />
<text
xml:space="preserve"
id="text833-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan>A</tspan></tspan></text>
</g>
<g
id="g905-7"
transform="translate(-0.94494048,73.232887)">
<text
xml:space="preserve"
id="text833-6"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-4);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan>ą</tspan></tspan></text>
<rect
style="color:#000000;overflow:visible;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-3"
width="10.583333"
height="13.79613"
x="77.863091"
y="86.745537"
ry="1.937705" />
</g>
<g
id="g905-3-7"
transform="translate(49.136911,68.319196)">
<text
xml:space="preserve"
id="text833-0-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-10-1);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan>Ą</tspan></tspan></text>
<rect
style="color:#000000;overflow:visible;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-4-9"
width="10.583333"
height="13.79613"
x="78.266167"
y="86.745537"
ry="1.937705" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="90.548096"
y="65.654472"
id="text1035"><tspan
sodipodi:role="line"
id="tspan1033"
x="90.548096"
y="65.654472"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">set_view: upper</tspan></text>
<g
id="g905-1"
transform="translate(22.423437,-17.670388)">
<rect
style="color:#000000;overflow:visible;fill:#e2e2e2;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-8"
width="10.583333"
height="13.79613"
x="78.878532"
y="85.920006"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-7"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2);fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan></tspan></tspan></text>
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-5)"
d="m 124.74085,99.691217 c -17.5759,11.622773 -35.932849,0 -35.932849,0"
id="path1087-4" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0.4em;font-family:monospace;-inkscape-font-specification:monospace;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="91.116165"
y="123.76147"
id="text1035-8"><tspan
sodipodi:role="line"
id="tspan1033-5"
x="91.116165"
y="123.76147"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px">locking</tspan><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
sodipodi:role="line"
id="tspan1604"
x="91.116165"
y="128.08197">lock_view: upper</tspan><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777px;font-family:monospace;-inkscape-font-specification:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px"
sodipodi:role="line"
x="91.116165"
y="132.40247"
id="tspan1606">unlock_view: lower</tspan></text>
<g
id="g905-1-9"
transform="translate(22.423437,17.64464)">
<rect
style="color:#000000;overflow:visible;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect900-8-7"
width="10.583333"
height="13.79613"
x="78.878532"
y="85.920006"
ry="1.937705" />
<text
xml:space="preserve"
id="text833-7-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835-2-1);fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
transform="translate(-0.04189825,-0.41581583)"><tspan
x="80.886719"
y="96.225952"><tspan
style="fill:#ffffff;fill-opacity:1"></tspan></tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

64
doc/views.md Normal file
View File

@ -0,0 +1,64 @@
Switching views
=========
Squeekboard layout files are separated into *views*.
What are views?
-------------------
A view is the button arrangement which you see on Squeekboard's panel. The view always spans the entire panel area, so it's not possible to see two views at the same time, even if the layout contains multiple views.
Views are useful because they allow to have many more buttons than would fit on the panel at the same time. That works because views can be switched.
Views are different from layouts: they can be switched without affecting the active language, and without touching the globe button. Layouts cannot share views, so switching layouts *always* switches views.
Switching views
------------------
The model selected for switching views is less similar to "levels" known from physical keyboards, but closer to "rooms", which may resemble a game map.
Buttons don't have states. It's more of a model where each view is a room, and buttons are doors. Switching means moving to the next room, and buttons highlight according to which view/room they lead to or from.
There are two basic kinds of switching buttons: one way (`set_view`), and two way (`locking`). `locking` is the more sophisticated one. When placed inside `lock_view`, it is drawn highlighted, and goes to `unlock view`. When placed inside any other view, it behaves like `set_view`.
![Diagram showing two transitions: from lowercase via `set_view` to uppercase, and back via `locking`](switching.svg) This diagram shows which buttons can switch between two views. Views are shown as circles, and buttons as rounded rectangles.
The two buttons are separate, and visible only in the view *from which the switch starts*. Note that the `locking` button is shown highlighted. That's because it's in `upper` view, which matches its `lock_view`.
Latching
----------
`locking` buttons provide a second mode of operation: latching. It's useful when the target view is needed only for a single button press, like entering a single accent or a single capital letter in Latin scripts.
When a latching button is pressed, the keyboard remembers to come back to the current (source) view, and then the view is switched. If another `locking` button is pressed, the source view stays in memory. If a text button is pressed, the view from memory is shown again, and forgotten.
![Diagram showing the switches needed to travel across 3 views: a→A→Ą, while latching.](latching.svg) In this diagram, the dashed line connects the view the typist is seeing to the view remembered for unlatching.
There are two ways to erase the memory without going back to the remembered view. Pressing the button again will permanently switch to the current view, and `set_view` will permanently switch to its target.
In the room metaphor, it's as if tying a thread inside the room before going through the door to the next one. And another `locking` door while holding the thread. Once the Minotaur is slain (text button pressed), the hero follows the thread back to the starting room.
The typist hero cuts the thread in two circumstances: when staying longer in the current room (press button again), or when moving through a `set_view` door.
![Diagram showing possible ways to stop latching, by staying in Ą, by unlatching back to a, and by moving on to ą view.](latching_return.svg) This diagram shows the possible ways to stop latching. One is by pressing a text button, which takes back to the original view. Another is pressing a locking button which appears highlighted (note that it can be any button, what matters is its `lock_view`). Finally, switching to another view using a button that doesn't keep the latch on forgets latching.
The layout author should pay attention that `set_view`'s lack of latching does not come as a surprise to typists.
Differences from keyboard levels
---------------------------------------
Views are **not** like keyboard levels.
On a physical keyboard, the number of buttons can not change when switching levels. In Squeekboard, they can have any arrangement of buttons you could imagine.
When switching levels on a keyboard, for example by pressing Shift, the key press not only affects the meaning of other keys, but also tells the application that it's pressed down. In Squeekboard, pressing buttons to change layouts *does not* do anything but switch the layout. Pressing the switching button especially *does not* tell the application that it was pressed. (This is the reason Shift and AltGr modifiers are not implemented in Squeekboard.)
Why not use the "views" model?
-------------------------------------
Squeekboard's goal is to support as many scripts as possible, and the author of the initial design doesn't know a whole lot. There are two problems with using the levels metaphor:
Firstly, the levels model assumes that there is a "base" and an "active" level. This does not work well with scripts that have different but equivalent modes of writing. An example is the Kana layout with Katakana and Hiragana, which are both "base".
Both systems could have been combined, but the view switching designer doesn't have enough experience with different scripts to do that. Some scripts may have different non-hierarchical ways to switch character groups (Balinese?), which could make combining hierarchy with free-form switching even harder.
Secondly, when dealing with languages with a hierarchy, we end up with extra work to eliminate nonsensical combinations. With "symbols" and "uppercase" levels, what does it mean to have both engaged? Eliminating that means extra work. Either validating layouts, so that it's not possible to engage "uppercase" from "symbols", or duplicating, so that "uppercase+symbols" is the same as just "symbols". With "accents" in the mix, this could become a challenge to design well.

View File

@ -45,6 +45,8 @@
typedef struct _EekGtkKeyboardPrivate
{
EekRenderer *renderer; // owned, nullable
struct render_geometry render_geometry; // mutable
EekboardContextService *eekboard_context; // unowned reference
struct submission *submission; // unowned reference
@ -72,12 +74,23 @@ eek_gtk_keyboard_real_realize (GtkWidget *self)
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
}
static void set_allocation_size(EekGtkKeyboard *gtk_keyboard,
struct squeek_layout *layout, gdouble width, gdouble height)
{
// This is where size-dependent surfaces would be released
EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (gtk_keyboard);
priv->render_geometry = eek_render_geometry_from_allocation_size(
layout, width, height);
}
static gboolean
eek_gtk_keyboard_real_draw (GtkWidget *self,
cairo_t *cr)
{
EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
eek_gtk_keyboard_get_instance_private (keyboard);
GtkAllocation allocation;
gtk_widget_get_allocation (self, &allocation);
@ -92,15 +105,14 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
priv->keyboard,
pcontext);
eek_renderer_set_allocation_size (priv->renderer,
priv->keyboard->layout,
allocation.width,
allocation.height);
set_allocation_size (keyboard, priv->keyboard->layout,
allocation.width, allocation.height);
eek_renderer_set_scale_factor (priv->renderer,
gtk_widget_get_scale_factor (self));
}
eek_renderer_render_keyboard (priv->renderer, priv->submission, cr, priv->keyboard);
eek_renderer_render_keyboard (priv->renderer, priv->render_geometry,
priv->submission, cr, priv->keyboard);
return FALSE;
}
@ -117,8 +129,9 @@ static void
eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
GtkAllocation *allocation)
{
EekGtkKeyboard *keyboard = EEK_GTK_KEYBOARD (self);
EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
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),
@ -130,10 +143,8 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
}
if (priv->renderer) {
eek_renderer_set_allocation_size (priv->renderer,
priv->keyboard->layout,
allocation->width,
allocation->height);
set_allocation_size (keyboard, priv->keyboard->layout,
allocation->width, allocation->height);
}
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->
@ -162,7 +173,7 @@ static void depress(EekGtkKeyboard *self,
}
squeek_layout_depress(priv->keyboard->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, self);
x, y, priv->render_geometry.widget_to_layout, time, self);
}
static void drag(EekGtkKeyboard *self,
@ -174,7 +185,7 @@ static void drag(EekGtkKeyboard *self,
}
squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time,
x, y, priv->render_geometry.widget_to_layout, time,
priv->eekboard_context, self);
}
@ -185,8 +196,7 @@ static void release(EekGtkKeyboard *self, guint32 time)
return;
}
squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission,
eek_renderer_get_transformation(priv->renderer), time,
priv->submission, priv->render_geometry.widget_to_layout, time,
priv->eekboard_context, self);
}
@ -396,6 +406,24 @@ eek_gtk_keyboard_new (EekboardContextService *eekservice,
priv->submission = submission;
priv->layout = layout;
priv->renderer = NULL;
// This should really be done on initialization.
// Before the widget is allocated,
// we don't really know what geometry it takes.
// When it's off the screen, we also kinda don't.
struct render_geometry initial_geometry = {
// Set to 100 just to make sure if there's any attempt to use it,
// it actually gives plausible results instead of blowing up,
// e.g. on zero division.
.allocation_width = 100,
.allocation_height = 100,
.widget_to_layout = {
.origin_x = 0,
.origin_y = 0,
.scale = 1,
},
};
priv->render_geometry = initial_geometry;
g_signal_connect (eekservice,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),

View File

@ -28,6 +28,7 @@
#include <glib.h>
#include <gtk/gtk.h>
#include "eek/eek-renderer.h"
#include "eek/eek-types.h"
struct submission;

View File

@ -18,8 +18,6 @@
* 02110-1301 USA
*/
#include "config.h"
#include <math.h>
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
@ -33,10 +31,6 @@
static void render_button_label (cairo_t *cr, GtkStyleContext *ctx,
const gchar *label, EekBounds bounds);
void eek_render_button (EekRenderer *self,
cairo_t *cr, const struct squeek_button *button,
gboolean pressed, gboolean locked);
static void
render_outline (cairo_t *cr,
GtkStyleContext *ctx,
@ -60,21 +54,21 @@ render_outline (cairo_t *cr,
position.x, position.y, position.width, position.height);
}
static void render_button_in_context(gint scale_factor,
/// Rust interface
void eek_render_button_in_context(uint32_t scale_factor,
cairo_t *cr,
GtkStyleContext *ctx,
const struct squeek_button *button) {
EekBounds bounds,
const char *icon_name,
const gchar *label) {
/* blank background */
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
cairo_paint (cr);
EekBounds bounds = squeek_button_get_bounds(button);
render_outline (cr, ctx, bounds);
cairo_paint (cr);
/* render icon (if any) */
const char *icon_name = squeek_button_get_icon_name(button);
if (icon_name) {
cairo_surface_t *icon_surface =
eek_renderer_get_icon_surface (icon_name, 16, scale_factor);
@ -104,25 +98,27 @@ static void render_button_in_context(gint scale_factor,
}
}
const gchar *label = squeek_button_get_label(button);
if (label) {
render_button_label (cr, ctx, label, squeek_button_get_bounds(button));
render_button_label (cr, ctx, label, bounds);
}
}
void
eek_render_button (EekRenderer *self,
cairo_t *cr,
const struct squeek_button *button,
gboolean pressed,
gboolean locked)
/// Prepare context for drawing the button.
/// The context MUST be released using the corresponing "put" procedure
/// before drawing the next button.
/// Interface for Rust.
GtkStyleContext *
eek_get_style_context_for_button (EekRenderer *self,
const char *name,
const char *outline_name,
const char *locked_class,
uint64_t pressed)
{
GtkStyleContext *ctx = self->button_context;
/* Set the name of the button on the widget path, using the name obtained
from the button's symbol. */
g_autoptr (GtkWidgetPath) path = NULL;
path = gtk_widget_path_copy (gtk_style_context_get_path (ctx));
const char *name = squeek_button_get_name(button);
gtk_widget_path_iter_set_name (path, -1, name);
/* Update the style context with the updated widget path. */
@ -131,19 +127,22 @@ eek_render_button (EekRenderer *self,
(pressed) or normal. */
gtk_style_context_set_state(ctx,
pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
const char *outline_name = squeek_button_get_outline_name(button);
if (locked) {
gtk_style_context_add_class(ctx, "locked");
if (locked_class) {
gtk_style_context_add_class(ctx, locked_class);
}
gtk_style_context_add_class(ctx, outline_name);
return ctx;
}
render_button_in_context(self->scale_factor, cr, ctx, button);
/// Interface for Rust.
void eek_put_style_context_for_button(GtkStyleContext *ctx,
const char *outline_name,
const char *locked_class) {
// Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
gtk_style_context_remove_class(ctx, outline_name);
if (locked) {
gtk_style_context_remove_class(ctx, "locked");
if (locked_class) {
gtk_style_context_remove_class(ctx, locked_class);
}
}
@ -194,22 +193,23 @@ render_button_label (cairo_t *cr,
// FIXME: Pass just the active modifiers instead of entire submission
void
eek_renderer_render_keyboard (EekRenderer *self,
struct render_geometry geometry,
struct submission *submission,
cairo_t *cr,
LevelKeyboard *keyboard)
{
g_return_if_fail (self->allocation_width > 0.0);
g_return_if_fail (self->allocation_height > 0.0);
g_return_if_fail (geometry.allocation_width > 0.0);
g_return_if_fail (geometry.allocation_height > 0.0);
/* Paint the background covering the entire widget area */
gtk_render_background (self->view_context,
cr,
0, 0,
self->allocation_width, self->allocation_height);
geometry.allocation_width, geometry.allocation_height);
cairo_save(cr);
cairo_translate (cr, self->widget_to_layout.origin_x, self->widget_to_layout.origin_y);
cairo_scale (cr, self->widget_to_layout.scale, self->widget_to_layout.scale);
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);
squeek_draw_layout_base_view(keyboard->layout, self, cr);
squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);
@ -261,8 +261,6 @@ static void
renderer_init (EekRenderer *self)
{
self->pcontext = NULL;
self->allocation_width = 0.0;
self->allocation_height = 0.0;
self->scale_factor = 1;
self->css_provider = squeek_load_style();
@ -309,22 +307,18 @@ eek_renderer_new (LevelKeyboard *keyboard,
return renderer;
}
void
eek_renderer_set_allocation_size (EekRenderer *renderer,
struct squeek_layout *layout,
struct render_geometry
eek_render_geometry_from_allocation_size (struct squeek_layout *layout,
gdouble width,
gdouble height)
{
g_return_if_fail (width > 0.0 && height > 0.0);
renderer->allocation_width = width;
renderer->allocation_height = height;
renderer->widget_to_layout = squeek_layout_calculate_transformation(
layout,
renderer->allocation_width, renderer->allocation_height);
// This is where size-dependent surfaces would be released
struct render_geometry ret = {
.allocation_width = width,
.allocation_height = height,
.widget_to_layout = squeek_layout_calculate_transformation(
layout, width, height),
};
return ret;
}
void
@ -333,6 +327,11 @@ eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale)
renderer->scale_factor = scale;
}
/// Rust interface.
uint32_t eek_renderer_get_scale_factor(EekRenderer *renderer) {
return renderer->scale_factor;
}
cairo_surface_t *
eek_renderer_get_icon_surface (const gchar *icon_name,
gint size,
@ -356,8 +355,3 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
}
return surface;
}
struct transformation
eek_renderer_get_transformation (EekRenderer *renderer) {
return renderer->widget_to_layout;
}

View File

@ -41,22 +41,23 @@ typedef struct EekRenderer
gchar *extra_style; // owned
// Mutable state
gint scale_factor; /* the outputs scale factor */
} EekRenderer;
/// Mutable part of the renderer state.
/// TODO: Possibly should include scale factor.
struct render_geometry {
/// Background extents
gdouble allocation_width;
gdouble allocation_height;
gint scale_factor; /* the outputs scale factor */
/// Coords transformation
struct transformation widget_to_layout;
} EekRenderer;
};
GType eek_renderer_get_type (void) G_GNUC_CONST;
EekRenderer *eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext);
void eek_renderer_set_allocation_size
(EekRenderer *renderer, struct squeek_layout *layout,
gdouble width,
gdouble height);
void eek_renderer_set_scale_factor (EekRenderer *renderer,
gint scale);
@ -64,13 +65,14 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name,
gint size,
gint scale);
void eek_renderer_render_keyboard (EekRenderer *renderer, struct submission *submission,
void eek_renderer_render_keyboard (EekRenderer *renderer, struct render_geometry geometry, struct submission *submission,
cairo_t *cr, LevelKeyboard *keyboard);
void
eek_renderer_free (EekRenderer *self);
struct transformation
eek_renderer_get_transformation (EekRenderer *renderer);
struct render_geometry
eek_render_geometry_from_allocation_size (struct squeek_layout *layout,
gdouble width, gdouble height);
G_END_DECLS
#endif /* EEK_RENDERER_H */

View File

@ -90,13 +90,5 @@ struct transformation {
gdouble scale;
};
struct squeek_button;
struct squeek_row;
/// Represents the path to the button within a view
struct button_place {
const struct squeek_row *row;
const struct squeek_button *button;
};
G_END_DECLS
#endif /* EEK_TYPES_H */

View File

@ -128,30 +128,22 @@ settings_get_layout(GSettings *settings, char **type, char **layout)
void
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state, uint32_t timestamp) {
gchar *layout_name = state->overlay_name;
if (layout_name == NULL) {
layout_name = state->layout_name;
switch (state->purpose) {
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
layout_name = "number";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
layout_name = "terminal";
break;
default:
;
}
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);
struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement, state->purpose, overlay_name);
LevelKeyboard *keyboard = level_keyboard_new(layout);
// set as current
LevelKeyboard *previous_keyboard = context->keyboard;

View File

@ -1,7 +1,7 @@
project(
'squeekboard',
'c', 'rust',
version: '1.10.0',
version: '1.13.0',
license: 'GPLv3',
meson_version: '>=0.51.0',
default_options: [

View File

@ -294,8 +294,8 @@
The serial number reflects the last state of the zwp_input_method_v2
object known to the client. The value of the serial argument must be
equal to the number of commit requests already issued on that object.
When the compositor receives a done event with a serial different than
equal to the number of done events already issued on that object.
When the compositor receives a commit request with a serial different than
the number of past commit requests, it must proceed as normal, except
it should not change the current state of the zwp_input_method_v2
object.

View File

@ -10,13 +10,14 @@ pub struct KeySym(pub String);
type View = String;
/// Use to send modified keypresses
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Modifier {
/// Control and Alt are the only modifiers
/// which doesn't interfere with levels,
/// so it's simple to implement as levels are deprecated in squeekboard.
Control,
Alt,
Mod4,
}
/// Action to perform on the keypress and, in reverse, on keyrelease
@ -29,6 +30,11 @@ pub enum Action {
lock: View,
/// When unlocked by pressing it or emitting a key
unlock: View,
/// Whether key has a latched state
/// that pops when another key is pressed.
latches: bool,
/// Should take on *locked* appearance whenever latch comes back to those views.
looks_locked_from: Vec<View>,
},
/// Hold this modifier for as long as the button is pressed
ApplyModifier(Modifier),
@ -48,14 +54,24 @@ pub enum Action {
impl Action {
pub fn is_locked(&self, view_name: &str) -> bool {
match self {
Action::LockView { lock, unlock: _ } => lock == view_name,
Action::LockView { lock, unlock: _, latches: _, looks_locked_from: _ } => lock == view_name,
_ => false,
}
}
pub fn has_locked_appearance_from(&self, locked_view_name: &str) -> bool {
match self {
Action::LockView { lock: _, unlock: _, latches: _, looks_locked_from } => {
looks_locked_from.iter()
.find(|view| locked_view_name == view.as_str())
.is_some()
},
_ => false,
}
}
pub fn is_active(&self, view_name: &str) -> bool {
match self {
Action::SetView(view) => view == view_name,
Action::LockView { lock, unlock: _ } => lock == view_name,
Action::LockView { lock, unlock: _, latches: _, looks_locked_from: _ } => lock == view_name,
_ => false,
}
}

View File

@ -12,6 +12,7 @@ use std::io;
use std::path::PathBuf;
use std::rc::Rc;
use std::vec::Vec;
use std::convert::TryFrom;
use xkbcommon::xkb;
@ -27,6 +28,7 @@ use ::resources;
use ::util::c::as_str;
use ::util::hash_map_map;
use ::xdg;
use ::imservice::ContentPurpose;
// traits, derives
use serde::Deserialize;
@ -42,19 +44,37 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn squeek_load_layout(
name: *const c_char,
type_: u32,
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 (kind, layout) = load_layout_data_with_fallback(&name, type_);
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);
Box::into_raw(Box::new(layout))
}
@ -97,85 +117,156 @@ impl fmt::Display for DataSource {
}
}
type LayoutSource = (ArrangementKind, DataSource);
/* All functions in this family carry around ArrangementKind,
* because it's not guaranteed to be preserved,
* and the resulting layout needs to know which version was loaded.
* See `squeek_layout_get_kind`.
* Possible TODO: since this is used only in styling,
* and makes the below code nastier than needed, maybe it should go.
*/
/// Lists possible sources, with 0 as the most preferred one
/// Trying order: native lang of the right kind, native base,
/// fallback lang of the right kind, fallback base
fn list_layout_sources(
name: &str,
kind: ArrangementKind,
keyboards_path: Option<PathBuf>,
) -> Vec<LayoutSource> {
// Just a simplification of often called code.
let add_by_name = |
mut ret: Vec<LayoutSource>,
name: &str,
kind: &ArrangementKind,
| -> Vec<LayoutSource> {
if let Some(path) = keyboards_path.clone() {
ret.push((
kind.clone(),
DataSource::File(
path.join(name.to_owned()).with_extension("yaml")
)
))
/// Returns ordered names treating `name` as the base name,
/// ignoring any `+` inside.
fn _get_arrangement_names(name: &str, arrangement: ArrangementKind)
-> Vec<(ArrangementKind, String)>
{
let name_with_arrangement = match arrangement {
ArrangementKind::Base => name.into(),
ArrangementKind::Wide => format!("{}_wide", name),
};
let mut ret = Vec::new();
if name_with_arrangement != name {
ret.push((arrangement, name_with_arrangement));
}
ret.push((
kind.clone(),
DataSource::Resource(name.into())
));
ret.push((ArrangementKind::Base, name.into()));
ret
};
}
// Another grouping.
let add_by_kind = |ret, name: &str, kind| {
let ret = match kind {
&ArrangementKind::Base => ret,
kind => add_by_name(
ret,
&name_with_arrangement(name.into(), kind),
kind,
),
};
/// Returns names accounting for any `+` in the `name`,
/// including the fallback to the default layout.
fn get_preferred_names(name: &str, kind: ArrangementKind)
-> Vec<(ArrangementKind, String)>
{
let mut ret = _get_arrangement_names(name, kind);
add_by_name(ret, name, &ArrangementKind::Base)
};
fn name_with_arrangement(name: String, kind: &ArrangementKind) -> String {
match kind {
ArrangementKind::Base => name,
ArrangementKind::Wide => name + "_wide",
}
}
let ret = Vec::new();
// Name as given takes priority.
let ret = add_by_kind(ret, name, &kind);
// Then try non-alternative name if applicable (`us` for `us+colemak`).
let ret = {
let base_name_preferences = {
let mut parts = name.splitn(2, '+');
match parts.next() {
Some(base) => {
// The name is already equal to base, so it was already added.
if base == name { ret }
else {
add_by_kind(ret, base, &kind)
// The name is already equal to base, so nothing to add
if base == name {
vec![]
} else {
_get_arrangement_names(base, kind)
}
},
// The layout's base name starts with a "+". Weird but OK.
None => {
log_print!(logging::Level::Surprise, "Base layout name is empty: {}", name);
ret
vec![]
}
}
};
// No other choices left, so give anything.
add_by_kind(ret, FALLBACK_LAYOUT_NAME.into(), &kind)
ret.extend(base_name_preferences.into_iter());
let fallback_names = _get_arrangement_names(FALLBACK_LAYOUT_NAME, kind);
ret.extend(fallback_names.into_iter());
ret
}
/// Includes the subdirectory before the forward slash.
type LayoutPath = String;
// This is only used inside iter_fallbacks_with_meta.
// Placed at the top scope
// because `use LayoutPurpose::*;`
// complains about "not in scope" otherwise.
// This seems to be a Rust 2015 edition problem.
/// Helper for determining where to look up the layout.
enum LayoutPurpose<'a> {
Default,
Special(&'a str),
}
/// Returns the directory string
/// where the layout should be looked up, including the slash.
fn get_directory_string(
content_purpose: ContentPurpose,
overlay: Option<&str>) -> String
{
use self::LayoutPurpose::*;
let layout_purpose = match overlay {
None => match content_purpose {
ContentPurpose::Number => Special("number"),
ContentPurpose::Digits => Special("number"),
ContentPurpose::Phone => Special("number"),
ContentPurpose::Terminal => Special("terminal"),
_ => Default,
},
Some(overlay) => Special(overlay),
};
// For intuitiveness,
// default purpose layouts are stored in the root directory,
// as they correspond to typical text
// and are seen the most often.
match layout_purpose {
Default => "".into(),
Special(purpose) => format!("{}/", purpose),
}
}
/// Returns an iterator over all fallback paths.
fn to_layout_paths(
name_fallbacks: Vec<(ArrangementKind, String)>,
content_purpose: ContentPurpose,
overlay: Option<&str>,
) -> impl Iterator<Item=(ArrangementKind, LayoutPath)> {
let prepend_directory = get_directory_string(content_purpose, overlay);
name_fallbacks.into_iter()
.map(move |(arrangement, name)|
(arrangement, format!("{}{}", prepend_directory, name))
)
}
type LayoutSource = (ArrangementKind, DataSource);
fn to_layout_sources(
layout_paths: impl Iterator<Item=(ArrangementKind, LayoutPath)>,
filesystem_path: Option<PathBuf>,
) -> impl Iterator<Item=LayoutSource> {
layout_paths.flat_map(move |(arrangement, layout_path)| {
let mut sources = Vec::new();
if let Some(path) = &filesystem_path {
sources.push((
arrangement,
DataSource::File(
path.join(&layout_path)
.with_extension("yaml")
)
));
};
sources.push((arrangement, DataSource::Resource(layout_path.clone())));
sources.into_iter()
})
}
/// Returns possible sources, with first as the most preferred one.
/// Trying order: native lang of the right kind, native base,
/// fallback lang of the right kind, fallback base
fn iter_layout_sources(
name: &str,
arrangement: ArrangementKind,
purpose: ContentPurpose,
ui_overlay: Option<&str>,
layout_storage: Option<PathBuf>,
) -> impl Iterator<Item=LayoutSource> {
let names = get_preferred_names(name, arrangement);
let paths = to_layout_paths(names, purpose, ui_overlay);
to_layout_sources(paths, layout_storage)
}
fn load_layout_data(source: DataSource)
@ -202,12 +293,16 @@ fn load_layout_data(source: DataSource)
fn load_layout_data_with_fallback(
name: &str,
kind: ArrangementKind,
purpose: ContentPurpose,
overlay: Option<&str>,
) -> (ArrangementKind, ::layout::LayoutData) {
// Build the path to the right keyboard layout subdirectory
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
.map(PathBuf::from)
.or_else(|| xdg::data_path("squeekboard/keyboards"));
for (kind, source) in list_layout_sources(name, kind, path) {
for (kind, source) in iter_layout_sources(&name, kind, purpose, overlay, path) {
let layout = load_layout_data(source.clone());
match layout {
Err(e) => match (e, source) {
@ -289,7 +384,13 @@ struct ButtonMeta {
#[serde(deny_unknown_fields)]
enum Action {
#[serde(rename="locking")]
Locking { lock_view: String, unlock_view: String },
Locking {
lock_view: String,
unlock_view: String,
pops: Option<bool>,
#[serde(default)]
looks_locked_from: Vec<String>,
},
#[serde(rename="set_view")]
SetView(String),
#[serde(rename="show_prefs")]
@ -543,7 +644,7 @@ fn create_action<H: logging::Handler>(
Text(String),
Keysym(String),
Modifier(Modifier),
};
}
let submission = match (
&symbol_meta.action,
@ -600,7 +701,9 @@ fn create_action<H: logging::Handler>(
)
),
SubmitData::Action(Action::Locking {
lock_view, unlock_view
lock_view, unlock_view,
pops,
looks_locked_from,
}) => ::action::Action::LockView {
lock: filter_view_name(
name,
@ -614,6 +717,8 @@ fn create_action<H: logging::Handler>(
&view_names,
warning_handler,
),
latches: pops.unwrap_or(true),
looks_locked_from,
},
SubmitData::Action(
Action::ShowPrefs
@ -658,6 +763,9 @@ fn create_action<H: logging::Handler>(
Modifier::Alt => action::Action::ApplyModifier(
action::Modifier::Alt,
),
Modifier::Mod4 => action::Action::ApplyModifier(
action::Modifier::Mod4,
),
unsupported_modifier => {
warning_handler.handle(
logging::Level::Bug,
@ -926,11 +1034,11 @@ mod tests {
/// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME
#[test]
fn fallbacks_order() {
let sources = list_layout_sources("nb", ArrangementKind::Base, None);
fn test_fallback_basic_builtin() {
let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Normal, None, None);
assert_eq!(
sources,
sources.collect::<Vec<_>>(),
vec!(
(ArrangementKind::Base, DataSource::Resource("nb".into())),
(
@ -941,13 +1049,35 @@ mod tests {
);
}
/// If layout contains a "+", it should reach for what's in front of it too.
/// Prefer loading from file system before builtin.
#[test]
fn fallbacks_order_base() {
let sources = list_layout_sources("nb+aliens", ArrangementKind::Base, None);
fn test_preferences_order_path() {
let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Normal, None, Some(".".into()));
assert_eq!(
sources,
sources.collect::<Vec<_>>(),
vec!(
(ArrangementKind::Base, DataSource::File("./nb.yaml".into())),
(ArrangementKind::Base, DataSource::Resource("nb".into())),
(
ArrangementKind::Base,
DataSource::File("./us.yaml".into())
),
(
ArrangementKind::Base,
DataSource::Resource("us".into())
),
)
);
}
/// If layout contains a "+", it should reach for what's in front of it too.
#[test]
fn test_preferences_order_base() {
let sources = iter_layout_sources("nb+aliens", ArrangementKind::Base, ContentPurpose::Normal, None, None);
assert_eq!(
sources.collect::<Vec<_>>(),
vec!(
(ArrangementKind::Base, DataSource::Resource("nb+aliens".into())),
(ArrangementKind::Base, DataSource::Resource("nb".into())),
@ -959,6 +1089,58 @@ mod tests {
);
}
#[test]
fn test_preferences_order_arrangement() {
let sources = iter_layout_sources("nb", ArrangementKind::Wide, ContentPurpose::Normal, None, None);
assert_eq!(
sources.collect::<Vec<_>>(),
vec!(
(ArrangementKind::Wide, DataSource::Resource("nb_wide".into())),
(ArrangementKind::Base, DataSource::Resource("nb".into())),
(
ArrangementKind::Wide,
DataSource::Resource("us_wide".into())
),
(
ArrangementKind::Base,
DataSource::Resource("us".into())
),
)
);
}
#[test]
fn test_preferences_order_overlay() {
let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Normal, Some("terminal"), None);
assert_eq!(
sources.collect::<Vec<_>>(),
vec!(
(ArrangementKind::Base, DataSource::Resource("terminal/nb".into())),
(
ArrangementKind::Base,
DataSource::Resource("terminal/us".into())
),
)
);
}
#[test]
fn test_preferences_order_hint() {
let sources = iter_layout_sources("nb", ArrangementKind::Base, ContentPurpose::Terminal, None, None);
assert_eq!(
sources.collect::<Vec<_>>(),
vec!(
(ArrangementKind::Base, DataSource::Resource("terminal/nb".into())),
(
ArrangementKind::Base,
DataSource::Resource("terminal/us".into())
),
)
);
}
#[test]
fn unicode_keysym() {

View File

@ -59,7 +59,7 @@ handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
if (service->context) {
if (arg_visible) {
server_context_service_show_keyboard (service->context);
server_context_service_force_show_keyboard (service->context);
} else {
server_context_service_hide_keyboard (service->context);
}

View File

@ -3,20 +3,24 @@
use cairo;
use std::cell::RefCell;
use ::action::Action;
use ::action::{ Action, Modifier };
use ::keyboard;
use ::layout::{ Button, Layout };
use ::layout::c::{ EekGtkKeyboard, Point };
use ::layout::{ Button, Label, LatchedState, Layout };
use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
use ::submission::Submission;
use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt;
use std::collections::HashSet;
use std::ffi::CStr;
use std::ptr;
mod c {
use super::*;
use cairo_sys;
use std::os::raw::c_void;
use std::os::raw::{ c_char, c_void };
// This is constructed only in C, no need for warnings
#[allow(dead_code)]
@ -24,18 +28,44 @@ mod c {
#[derive(Clone, Copy)]
pub struct EekRenderer(*const c_void);
#[no_mangle]
// This is constructed only in C, no need for warnings
/// Just don't clone this for no reason.
#[allow(dead_code)]
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct GtkStyleContext(*const c_void);
extern "C" {
// Button and View inside CButtonPlace are safe to pass to C
// as long as they don't outlive the call
// and nothing dereferences them
#[allow(improper_ctypes)]
pub fn eek_render_button(
pub fn eek_renderer_get_scale_factor(
renderer: EekRenderer,
) -> u32;
#[allow(improper_ctypes)]
pub fn eek_render_button_in_context(
scale_factor: u32,
cr: *mut cairo_sys::cairo_t,
button: *const Button,
ctx: GtkStyleContext,
bounds: Bounds,
icon_name: *const c_char,
label: *const c_char,
);
#[allow(improper_ctypes)]
pub fn eek_get_style_context_for_button(
renderer: EekRenderer,
name: *const c_char,
outline_name: *const c_char,
locked_class: *const c_char,
pressed: u64,
locked: u64,
) -> GtkStyleContext;
#[allow(improper_ctypes)]
pub fn eek_put_style_context_for_button(
ctx: GtkStyleContext,
outline_name: *const c_char,
locked_class: *const c_char,
);
}
@ -55,13 +85,16 @@ mod c {
layout.foreach_visible_button(|offset, button| {
let state = RefCell::borrow(&button.state).clone();
let active_mod = match &state.action {
Action::ApplyModifier(m) => active_modifiers.contains(m),
_ => false,
};
let locked = state.action.is_active(&layout.current_view)
| active_mod;
if state.pressed == keyboard::PressType::Pressed || locked {
let locked = LockedStyle::from_action(
&state.action,
&active_modifiers,
layout.get_view_latched(),
&layout.current_view,
);
if state.pressed == keyboard::PressType::Pressed
|| locked != LockedStyle::Free
{
render_button_at_position(
renderer, &cr,
offset,
@ -87,20 +120,55 @@ mod c {
renderer, &cr,
offset,
button.as_ref(),
keyboard::PressType::Released, false,
keyboard::PressType::Released,
LockedStyle::Free,
);
})
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
enum LockedStyle {
Free,
Latched,
Locked,
}
impl LockedStyle {
fn from_action(
action: &Action,
mods: &HashSet<Modifier>,
latched_view: &LatchedState,
current_view: &str,
) -> LockedStyle {
let active_mod = match action {
Action::ApplyModifier(m) => mods.contains(m),
_ => false,
};
let active_view = action.is_active(current_view);
let latched_button = match latched_view {
LatchedState::Not => false,
LatchedState::FromView(view) => !action.has_locked_appearance_from(view),
};
match (active_mod, active_view, latched_button) {
// Modifiers don't latch.
(true, _, _) => LockedStyle::Locked,
(false, true, false) => LockedStyle::Locked,
(false, true, true) => LockedStyle::Latched,
_ => LockedStyle::Free,
}
}
}
/// Renders a button at a position (button's own bounds ignored)
pub fn render_button_at_position(
fn render_button_at_position(
renderer: c::EekRenderer,
cr: &cairo::Context,
position: Point,
button: &Button,
pressed: keyboard::PressType,
locked: bool,
locked: LockedStyle,
) {
cr.save();
cr.translate(position.x, position.y);
@ -109,19 +177,110 @@ pub fn render_button_at_position(
button.size.width, button.size.height
);
cr.clip();
unsafe {
c::eek_render_button(
let scale_factor = unsafe {
c::eek_renderer_get_scale_factor(renderer)
};
let bounds = button.get_bounds();
let (label_c, icon_name_c) = match &button.label {
Label::Text(text) => (text.as_ptr(), ptr::null()),
Label::IconName(name) => {
let l = unsafe {
// CStr doesn't allocate anything, so it only points to
// the 'static str, avoiding a memory leak
CStr::from_bytes_with_nul_unchecked(b"icon\0")
};
(l.as_ptr(), name.as_ptr())
},
};
with_button_context(
renderer,
button,
pressed,
locked,
|ctx| unsafe {
// TODO: split into separate procedures:
// draw outline, draw label, draw icon.
c::eek_render_button_in_context(
scale_factor,
cairo::Context::to_raw_none(&cr),
button as *const Button,
*ctx,
bounds,
icon_name_c,
label_c,
)
}
);
cr.restore();
}
fn with_button_context<R, F: FnOnce(&c::GtkStyleContext) -> R>(
renderer: c::EekRenderer,
button: &Button,
pressed: keyboard::PressType,
locked: LockedStyle,
operation: F,
) -> R {
let outline_name_c = button.outline_name.as_ptr();
let locked_class_c = match locked {
LockedStyle::Free => ptr::null(),
LockedStyle::Locked => unsafe {
CStr::from_bytes_with_nul_unchecked(b"locked\0").as_ptr()
},
LockedStyle::Latched => unsafe {
CStr::from_bytes_with_nul_unchecked(b"latched\0").as_ptr()
},
};
let ctx = unsafe {
c::eek_get_style_context_for_button(
renderer,
button.name.as_ptr(),
outline_name_c,
locked_class_c,
pressed as u64,
locked as u64,
)
};
cr.restore();
let r = operation(&ctx);
unsafe {
c::eek_put_style_context_for_button(
ctx,
outline_name_c,
locked_class_c,
)
};
r
}
pub fn queue_redraw(keyboard: EekGtkKeyboard) {
let widget = unsafe { gtk::Widget::from_glib_none(keyboard.0) };
widget.queue_draw();
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_exit_only() {
assert_eq!(
LockedStyle::from_action(
&Action::LockView {
lock: "ab".into(),
unlock: "a".into(),
latches: true,
looks_locked_from: vec!["b".into()],
},
&HashSet::new(),
&LatchedState::FromView("b".into()),
"ab",
),
LockedStyle::Locked,
);
}
}

View File

@ -32,9 +32,9 @@ pub mod c {
#[repr(transparent)]
pub struct InputMethod(*const c_void);
#[no_mangle]
extern "C" {
fn imservice_destroy_im(im: *mut c::InputMethod);
#[allow(improper_ctypes)] // IMService will never be dereferenced in C
pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
@ -149,24 +149,21 @@ pub mod c {
..IMProtocolState::default()
};
imservice.serial += Wrapping(1u32);
if active_changed {
(imservice.active_callback)(imservice.current.active);
let (hint, purpose) = if imservice.current.active {(
imservice.current.content_hint,
imservice.current.content_purpose.clone(),
)} else {(
ContentHint::NONE,
ContentPurpose::Normal,
)};
if imservice.current.active {
unsafe {
eekboard_context_service_set_hint_purpose(
imservice.state_manager,
hint.bits(),
purpose as u32,
imservice.current.content_hint.bits(),
imservice.current.content_purpose.clone() as u32,
);
}
}
}
}
// TODO: this is really untested
#[no_mangle]
@ -409,7 +406,6 @@ impl IMService {
unsafe {
c::eek_input_method_commit(self.im, self.serial.0)
}
self.serial += Wrapping(1u32);
Ok(())
},
false => Err(SubmitError::NotActive),

View File

@ -26,19 +26,12 @@ struct squeek_layout_state {
struct squeek_layout;
EekBounds squeek_button_get_bounds(const struct squeek_button*);
const char *squeek_button_get_label(const struct squeek_button*);
const char *squeek_button_get_icon_name(const struct squeek_button*);
const char *squeek_button_get_name(const struct squeek_button*);
const char *squeek_button_get_outline_name(const struct squeek_button*);
void squeek_button_print(const struct squeek_button* button);
struct transformation squeek_layout_calculate_transformation(
const struct squeek_layout *layout,
double allocation_width, double allocation_size);
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type, uint32_t variant_type, const char *overlay_name);
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
void squeek_layout_free(struct squeek_layout*);

View File

@ -41,9 +41,7 @@ pub mod c {
use super::*;
use gtk_sys;
use std::ffi::CStr;
use std::os::raw::{ c_char, c_void };
use std::ptr;
use std::os::raw::c_void;
use std::ops::{ Add, Sub };
@ -52,7 +50,6 @@ pub mod c {
#[derive(Copy, Clone)]
pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
#[no_mangle]
extern "C" {
#[allow(improper_ctypes)]
pub fn eek_gtk_keyboard_emit_feedback(
@ -163,64 +160,6 @@ pub mod c {
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
#[no_mangle]
pub extern "C"
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
let button = unsafe { &*button };
Bounds {
x: 0.0, y: 0.0,
width: button.size.width, height: button.size.height
}
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_label(
button: *const ::layout::Button
) -> *const c_char {
let button = unsafe { &*button };
match &button.label {
Label::Text(text) => text.as_ptr(),
// returning static strings to C is a bit cumbersome
Label::IconName(_) => unsafe {
// CStr doesn't allocate anything, so it only points to
// the 'static str, avoiding a memory leak
CStr::from_bytes_with_nul_unchecked(b"icon\0")
}.as_ptr(),
}
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_icon_name(button: *const Button) -> *const c_char {
let button = unsafe { &*button };
match &button.label {
Label::Text(_) => ptr::null(),
Label::IconName(name) => name.as_ptr(),
}
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_name(button: *const Button) -> *const c_char {
let button = unsafe { &*button };
button.name.as_ptr()
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_outline_name(button: *const Button) -> *const c_char {
let button = unsafe { &*button };
button.outline_name.as_ptr()
}
#[no_mangle]
pub extern "C"
fn squeek_button_print(button: *const ::layout::Button) {
let button = unsafe { &*button };
println!("{:?}", button);
}
/// Positions the layout contents within the available space.
/// The origin of the transformation is the point inside the margins.
#[no_mangle]
@ -485,6 +424,15 @@ pub struct Button {
pub state: Rc<RefCell<KeyState>>,
}
impl Button {
pub fn get_bounds(&self) -> c::Bounds {
c::Bounds {
x: 0.0, y: 0.0,
width: self.size.width, height: self.size.height,
}
}
}
/// The graphical representation of a row of buttons
#[derive(Clone, Debug)]
pub struct Row {
@ -552,6 +500,7 @@ pub struct Spacing {
pub button: f64,
}
#[derive(Clone)]
pub struct View {
/// Rows together with their offsets from the top left
rows: Vec<(c::Point, Row)>,
@ -651,7 +600,7 @@ impl View {
}
/// The physical characteristic of layout for the purpose of styling
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ArrangementKind {
Base = 0,
Wide = 1,
@ -665,6 +614,19 @@ pub struct Margins {
pub right: f64,
}
#[derive(Clone, Debug, PartialEq)]
pub enum LatchedState {
/// Holds view to return to.
FromView(String),
Not,
}
impl LatchedState {
pub fn is_latched(&self) -> bool {
self != &LatchedState::Not
}
}
// TODO: split into sth like
// Arrangement (views) + details (keymap) + State (keys)
/// State of the UI, contains the backend as well
@ -672,6 +634,12 @@ pub struct Layout {
pub margins: Margins,
pub kind: ArrangementKind,
pub current_view: String,
// If current view is latched,
// clicking any button that emits an action (erase, submit, set modifier)
// will cause lock buttons to unlatch.
view_latched: LatchedState,
// Views own the actual buttons which have state
// Maybe they should own UI only,
// and keys should be owned by a dedicated non-UI-State?
@ -718,6 +686,7 @@ impl Layout {
Layout {
kind,
current_view: "base".to_owned(),
view_latched: LatchedState::Not,
views: data.views,
keymaps: data.keymaps,
pressed_keys: HashSet::new(),
@ -743,6 +712,12 @@ impl Layout {
}
}
// Layout is passed around mutably,
// so better keep the field away from direct access.
pub fn get_view_latched(&self) -> &LatchedState {
&self.view_latched
}
/// Calculates size without margins
fn calculate_inner_size(&self) -> Size {
View::calculate_super_size(
@ -818,8 +793,117 @@ impl Layout {
}
out
}
fn apply_view_transition(
&mut self,
action: &Action,
) {
let (transition, new_latched) = Layout::process_action_for_view(
action,
&self.current_view,
&self.view_latched,
);
match transition {
ViewTransition::UnlatchAll => self.unstick_locks(),
ViewTransition::ChangeTo(view) => try_set_view(self, view.into()),
ViewTransition::NoChange => {},
};
self.view_latched = new_latched;
}
/// Unlatch all latched keys,
/// so that the new view is the one before first press.
fn unstick_locks(&mut self) {
if let LatchedState::FromView(name) = self.view_latched.clone() {
match self.set_view(name.clone()) {
Ok(_) => { self.view_latched = LatchedState::Not; }
Err(e) => log_print!(
logging::Level::Bug,
"Bad view {}, can't unlatch ({:?})",
name,
e,
),
}
}
}
/// Last bool is new latch state.
/// It doesn't make sense when the result carries UnlatchAll,
/// but let's not be picky.
///
/// Although the state is not defined at the keys
/// (it's in the relationship between view and action),
/// keys go through the following stages when clicked repeatedly:
/// unlocked+unlatched -> locked+latched -> locked+unlatched
/// -> unlocked+unlatched
fn process_action_for_view<'a>(
action: &'a Action,
current_view: &str,
latched: &LatchedState,
) -> (ViewTransition<'a>, LatchedState) {
match action {
Action::Submit { text: _, keys: _ }
| Action::Erase
| Action::ApplyModifier(_)
=> {
let t = match latched {
LatchedState::FromView(_) => ViewTransition::UnlatchAll,
LatchedState::Not => ViewTransition::NoChange,
};
(t, LatchedState::Not)
},
Action::SetView(view) => (
ViewTransition::ChangeTo(view),
LatchedState::Not,
),
Action::LockView { lock, unlock, latches, looks_locked_from: _ } => {
use self::ViewTransition as VT;
let locked = action.is_locked(current_view);
match (locked, latched, latches) {
// Was unlocked, now make locked but latched.
(false, LatchedState::Not, true) => (
VT::ChangeTo(lock),
LatchedState::FromView(current_view.into()),
),
// Layout is latched for reason other than this button.
(false, LatchedState::FromView(view), true) => (
VT::ChangeTo(lock),
LatchedState::FromView(view.clone()),
),
// Was latched, now only locked.
(true, LatchedState::FromView(_), true)
=> (VT::NoChange, LatchedState::Not),
// Was unlocked, can't latch so now make fully locked.
(false, _, false)
=> (VT::ChangeTo(lock), LatchedState::Not),
// Was locked, now make unlocked.
(true, _, _)
=> (VT::ChangeTo(unlock), LatchedState::Not),
}
},
_ => (ViewTransition::NoChange, latched.clone()),
}
}
}
#[derive(Debug, PartialEq)]
enum ViewTransition<'a> {
ChangeTo(&'a str),
UnlatchAll,
NoChange,
}
fn try_set_view(layout: &mut Layout, view_name: &str) {
layout.set_view(view_name.into())
.or_print(
logging::Problem::Bug,
&format!("Bad view {}, ignoring", view_name),
);
}
mod procedures {
use super::*;
@ -898,67 +982,6 @@ pub struct UIBackend {
mod seat {
use super::*;
fn try_set_view(layout: &mut Layout, view_name: String) {
layout.set_view(view_name.clone())
.or_print(
logging::Problem::Bug,
&format!("Bad view {}, ignoring", view_name),
);
}
/// A vessel holding an obligation to switch view.
/// Use with #[must_use]
struct ViewChange<'a> {
layout: &'a mut Layout,
view_name: Option<String>,
}
impl<'a> ViewChange<'a> {
fn choose_view(self, view_name: String) -> ViewChange<'a> {
ViewChange {
view_name: Some(view_name),
..self
}
}
fn apply(self) {
if let Some(name) = self.view_name {
try_set_view(self.layout, name);
}
}
}
/// Find all impermanent view changes and undo them in an arbitrary order.
/// Return an obligation to actually switch the view.
/// The final view is the "unlock" view
/// from one of the currently stuck keys.
// As long as only one stuck button is used, this should be fine.
// This is guaranteed because pressing a lock button unlocks all others.
// TODO: Make some broader guarantee about the resulting view,
// e.g. by maintaining a stack of stuck keys.
#[must_use]
fn unstick_locks(layout: &mut Layout) -> ViewChange {
let mut new_view = None;
for key in layout.get_locked_keys().clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow();
let key = RefCell::borrow(key);
match &key.action {
Action::LockView { lock: _, unlock: view } => {
new_view = Some(view.clone());
},
a => log_print!(
logging::Level::Bug,
"Non-locking action {:?} was found inside locked keys",
a,
),
};
}
ViewChange {
layout,
view_name: new_view,
}
}
pub fn handle_press_key(
layout: &mut Layout,
submission: &mut Submission,
@ -1018,37 +1041,22 @@ mod seat {
};
let action = key.action.clone();
layout.apply_view_transition(&action);
// update
let key = key.into_released();
// process changes
// process non-view switching
match action {
Action::Submit { text: _, keys: _ }
| Action::Erase
=> {
unstick_locks(layout).apply();
submission.handle_release(KeyState::get_id(rckey), time);
},
Action::SetView(view) => {
try_set_view(layout, view)
},
Action::LockView { lock, unlock } => {
let gets_locked = !key.action.is_locked(&layout.current_view);
unstick_locks(layout)
// It doesn't matter what the resulting view should be,
// it's getting changed anyway.
.choose_view(
match gets_locked {
true => lock.clone(),
false => unlock.clone(),
}
)
.apply()
},
Action::ApplyModifier(modifier) => {
// FIXME: key id is unneeded with stateless locks
let key_id = KeyState::get_id(rckey);
let gets_locked = !submission.is_modifier_active(modifier.clone());
let gets_locked = !submission.is_modifier_active(modifier);
match gets_locked {
true => submission.handle_add_modifier(
key_id,
@ -1083,6 +1091,8 @@ mod seat {
}
}
},
// Other keys are handled in view switcher before.
_ => {}
};
let pointer = ::util::Pointer(rckey.clone());
@ -1100,14 +1110,20 @@ mod test {
use std::ffi::CString;
use ::keyboard::PressType;
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
pub fn make_state_with_action(action: Action)
-> Rc<RefCell<::keyboard::KeyState>>
{
Rc::new(RefCell::new(::keyboard::KeyState {
pressed: PressType::Released,
keycodes: Vec::new(),
action: Action::SetView("default".into()),
action,
}))
}
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
make_state_with_action(Action::SetView("default".into()))
}
pub fn make_button_with_state(
name: String,
state: Rc<RefCell<::keyboard::KeyState>>,
@ -1121,6 +1137,242 @@ mod test {
})
}
#[test]
fn latch_lock_unlock() {
let action = Action::LockView {
lock: "lock".into(),
unlock: "unlock".into(),
latches: true,
looks_locked_from: vec![],
};
assert_eq!(
Layout::process_action_for_view(&action, "unlock", &LatchedState::Not),
(ViewTransition::ChangeTo("lock"), LatchedState::FromView("unlock".into())),
);
assert_eq!(
Layout::process_action_for_view(&action, "lock", &LatchedState::FromView("unlock".into())),
(ViewTransition::NoChange, LatchedState::Not),
);
assert_eq!(
Layout::process_action_for_view(&action, "lock", &LatchedState::Not),
(ViewTransition::ChangeTo("unlock"), LatchedState::Not),
);
assert_eq!(
Layout::process_action_for_view(&Action::Erase, "lock", &LatchedState::FromView("base".into())),
(ViewTransition::UnlatchAll, LatchedState::Not),
);
}
#[test]
fn latch_pop_layout() {
let switch = Action::LockView {
lock: "locked".into(),
unlock: "base".into(),
latches: true,
looks_locked_from: vec![],
};
let submit = Action::Erase;
let view = View::new(vec![(
0.0,
Row::new(vec![
(
0.0,
make_button_with_state(
"switch".into(),
make_state_with_action(switch.clone())
),
),
(
1.0,
make_button_with_state(
"submit".into(),
make_state_with_action(submit.clone())
),
),
]),
)]);
let mut layout = Layout {
current_view: "base".into(),
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! {
// Both can use the same structure.
// Switching doesn't depend on the view shape
// as long as the switching button is present.
"base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
},
};
// Basic cycle
layout.apply_view_transition(&switch);
assert_eq!(&layout.current_view, "locked");
layout.apply_view_transition(&switch);
assert_eq!(&layout.current_view, "locked");
layout.apply_view_transition(&submit);
assert_eq!(&layout.current_view, "locked");
layout.apply_view_transition(&switch);
assert_eq!(&layout.current_view, "base");
layout.apply_view_transition(&switch);
// Unlatch
assert_eq!(&layout.current_view, "locked");
layout.apply_view_transition(&submit);
assert_eq!(&layout.current_view, "base");
}
#[test]
fn reverse_unlatch_layout() {
let switch = Action::LockView {
lock: "locked".into(),
unlock: "base".into(),
latches: true,
looks_locked_from: vec![],
};
let unswitch = Action::LockView {
lock: "locked".into(),
unlock: "unlocked".into(),
latches: false,
looks_locked_from: vec![],
};
let submit = Action::Erase;
let view = View::new(vec![(
0.0,
Row::new(vec![
(
0.0,
make_button_with_state(
"switch".into(),
make_state_with_action(switch.clone())
),
),
(
1.0,
make_button_with_state(
"submit".into(),
make_state_with_action(submit.clone())
),
),
]),
)]);
let mut layout = Layout {
current_view: "base".into(),
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! {
// Both can use the same structure.
// Switching doesn't depend on the view shape
// as long as the switching button is present.
"base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"unlocked".into() => (c::Point { x: 0.0, y: 0.0 }, view),
},
};
layout.apply_view_transition(&switch);
assert_eq!(&layout.current_view, "locked");
layout.apply_view_transition(&unswitch);
assert_eq!(&layout.current_view, "unlocked");
}
#[test]
fn latch_twopop_layout() {
let switch = Action::LockView {
lock: "locked".into(),
unlock: "base".into(),
latches: true,
looks_locked_from: vec![],
};
let switch_again = Action::LockView {
lock: "ĄĘ".into(),
unlock: "locked".into(),
latches: true,
looks_locked_from: vec![],
};
let submit = Action::Erase;
let view = View::new(vec![(
0.0,
Row::new(vec![
(
0.0,
make_button_with_state(
"switch".into(),
make_state_with_action(switch.clone())
),
),
(
1.0,
make_button_with_state(
"submit".into(),
make_state_with_action(submit.clone())
),
),
]),
)]);
let mut layout = Layout {
current_view: "base".into(),
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! {
// All can use the same structure.
// Switching doesn't depend on the view shape
// as long as the switching button is present.
"base".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"locked".into() => (c::Point { x: 0.0, y: 0.0 }, view.clone()),
"ĄĘ".into() => (c::Point { x: 0.0, y: 0.0 }, view),
},
};
// Latch twice, then Ąto-unlatch across 2 levels
layout.apply_view_transition(&switch);
println!("{:?}", layout.view_latched);
assert_eq!(&layout.current_view, "locked");
layout.apply_view_transition(&switch_again);
println!("{:?}", layout.view_latched);
assert_eq!(&layout.current_view, "ĄĘ");
layout.apply_view_transition(&submit);
println!("{:?}", layout.view_latched);
assert_eq!(&layout.current_view, "base");
}
#[test]
fn check_centering() {
// A B
@ -1193,6 +1445,7 @@ mod test {
]);
let layout = Layout {
current_view: String::new(),
view_latched: LatchedState::Not,
keymaps: Vec::new(),
kind: ArrangementKind::Base,
pressed_keys: HashSet::new(),

View File

@ -24,7 +24,6 @@ mod c {
#[repr(C)]
pub struct GnomeXkbInfo(*const c_void);
#[no_mangle]
extern "C" {
// from libc
pub fn strcoll(cs: *const c_char, ct: *const c_char) -> c_int;

View File

@ -9,7 +9,6 @@ pub mod c {
#[derive(Clone, Copy)]
pub struct Manager(*const c_void);
#[no_mangle]
extern "C" {
pub fn eekboard_context_service_set_overlay(
manager: Manager,

View File

@ -1,9 +0,0 @@
#ifndef POPOVER_H__
#define POPOVER_H__
#include <gtk/gtk.h>
#include "eek/eek-keyboard.h"
void squeek_popover_show(GtkWidget*, struct button_place);
#endif

View File

@ -29,7 +29,6 @@ use ::logging::Warn;
mod c {
use std::os::raw::c_char;
#[no_mangle]
extern "C" {
pub fn popover_open_settings_panel(panel: *const c_char);
}
@ -214,7 +213,16 @@ fn set_visible_layout(
layout_id: LayoutId,
) {
match layout_id {
LayoutId::System { kind, name } => set_layout(kind, name),
LayoutId::System { kind, name } => {
unsafe {
use std::ptr;
manager::c::eekboard_context_service_set_overlay(
manager,
ptr::null(),
);
}
set_layout(kind, name);
}
LayoutId::Local(name) => {
let name = CString::new(name.as_str()).unwrap();
let name_ptr = name.as_ptr();
@ -437,7 +445,8 @@ pub fn show(
let settings_action = gio::SimpleAction::new("settings", None);
settings_action.connect_activate(move |_, _| {
unsafe { c::popover_open_settings_panel(CString::new("region").unwrap().as_ptr()) };
let s = CString::new("region").unwrap();
unsafe { c::popover_open_settings_panel(s.as_ptr()) };
});
let action_group = gio::SimpleActionGroup::new();

View File

@ -10,46 +10,86 @@ use std::iter::FromIterator;
// TODO: keep a list of what is a language layout,
// and what a convenience layout. "_wide" is not a layout,
// neither is "number"
/// List of builtin layouts
const KEYBOARDS: &[(*const str, *const str)] = &[
// layouts: us must be left as first, as it is the,
// fallback layout. The others should be alphabetical.
// fallback layout.
("us", include_str!("../data/keyboards/us.yaml")),
("us+colemak", include_str!("../data/keyboards/us+colemak.yaml")),
("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
("br", include_str!("../data/keyboards/br.yaml")),
("de", include_str!("../data/keyboards/de.yaml")),
// Language layouts: keep alphabetical.
("be", include_str!("../data/keyboards/be.yaml")),
("be_wide", include_str!("../data/keyboards/be_wide.yaml")),
("bg", include_str!("../data/keyboards/bg.yaml")),
("br", include_str!("../data/keyboards/br.yaml")),
("de", include_str!("../data/keyboards/de.yaml")),
("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
("cz", include_str!("../data/keyboards/cz.yaml")),
("cz_wide", include_str!("../data/keyboards/cz_wide.yaml")),
("cz+qwerty", include_str!("../data/keyboards/cz+qwerty.yaml")),
("cz+qwerty_wide", include_str!("../data/keyboards/cz+qwerty_wide.yaml")),
("dk", include_str!("../data/keyboards/dk.yaml")),
("epo", include_str!("../data/keyboards/epo.yaml")),
("es", include_str!("../data/keyboards/es.yaml")),
("es+cat", include_str!("../data/keyboards/es+cat.yaml")),
("fi", include_str!("../data/keyboards/fi.yaml")),
("fr", include_str!("../data/keyboards/fr.yaml")),
("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
("it+fur", include_str!("../data/keyboards/it+fur.yaml")),
("gr", include_str!("../data/keyboards/gr.yaml")),
("il", include_str!("../data/keyboards/il.yaml")),
("ir", include_str!("../data/keyboards/ir.yaml")),
("ir_wide", include_str!("../data/keyboards/ir_wide.yaml")),
("it", include_str!("../data/keyboards/it.yaml")),
("it+fur", include_str!("../data/keyboards/it+fur.yaml")),
("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")),
("no", include_str!("../data/keyboards/no.yaml")),
("number", include_str!("../data/keyboards/number.yaml")),
("pl", include_str!("../data/keyboards/pl.yaml")),
("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
("ru", include_str!("../data/keyboards/ru.yaml")),
("se", include_str!("../data/keyboards/se.yaml")),
("th", include_str!("../data/keyboards/th.yaml")),
("th_wide", include_str!("../data/keyboards/th_wide.yaml")),
("ua", include_str!("../data/keyboards/ua.yaml")),
("bg", include_str!("../data/keyboards/bg.yaml")),
// layout+overlay
("terminal", include_str!("../data/keyboards/terminal.yaml")),
("terminal_wide", include_str!("../data/keyboards/terminal_wide.yaml")),
("us+colemak", include_str!("../data/keyboards/us+colemak.yaml")),
("us+colemak_wide", include_str!("../data/keyboards/us+colemak_wide.yaml")),
("us+dvorak", include_str!("../data/keyboards/us+dvorak.yaml")),
("us+dvorak_wide", include_str!("../data/keyboards/us+dvorak_wide.yaml")),
// Others
("number/us", include_str!("../data/keyboards/number/us.yaml")),
// Terminal
("terminal/fr", include_str!("../data/keyboards/terminal/fr.yaml")),
("terminal/us", include_str!("../data/keyboards/terminal/us.yaml")),
("terminal/us_wide", include_str!("../data/keyboards/terminal/us_wide.yaml")),
// Overlays
("emoji", include_str!("../data/keyboards/emoji.yaml")),
("emoji/us", include_str!("../data/keyboards/emoji/us.yaml")),
];
pub fn get_keyboard(needle: &str) -> Option<&'static str> {
@ -85,6 +125,7 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
("en-US", include_str!("../data/langs/en-US.txt")),
("es-ES", include_str!("../data/langs/es-ES.txt")),
("fur-IT", include_str!("../data/langs/fur-IT.txt")),
("he-IL", include_str!("../data/langs/he-IL.txt")),
("ja-JP", include_str!("../data/langs/ja-JP.txt")),
("pl-PL", include_str!("../data/langs/pl-PL.txt")),
("ru-RU", include_str!("../data/langs/ru-RU.txt")),
@ -131,7 +172,7 @@ mod test {
#[test]
fn check_overlays_present() {
for name in get_overlays() {
assert!(get_keyboard(name).is_some());
assert!(get_keyboard(&format!("{}/us", name)).is_some());
}
}

View File

@ -239,6 +239,13 @@ server_context_service_real_show_keyboard (ServerContextService *self)
gtk_widget_show (GTK_WIDGET(self->window));
}
static gboolean
show_keyboard_source_func(ServerContextService *context)
{
server_context_service_real_show_keyboard(context);
return G_SOURCE_REMOVE;
}
static void
server_context_service_real_hide_keyboard (ServerContextService *self)
{
@ -246,6 +253,13 @@ server_context_service_real_hide_keyboard (ServerContextService *self)
self->visible = FALSE;
}
static gboolean
hide_keyboard_source_func(ServerContextService *context)
{
server_context_service_real_hide_keyboard(context);
return G_SOURCE_REMOVE;
}
static gboolean
on_hide (ServerContextService *self)
{
@ -255,7 +269,7 @@ on_hide (ServerContextService *self)
return G_SOURCE_REMOVE;
}
void
static void
server_context_service_show_keyboard (ServerContextService *self)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
@ -266,17 +280,30 @@ server_context_service_show_keyboard (ServerContextService *self)
}
if (!self->visible) {
server_context_service_real_show_keyboard (self);
g_idle_add((GSourceFunc)show_keyboard_source_func, self);
}
}
void
server_context_service_force_show_keyboard (ServerContextService *self)
{
if (!submission_hint_available(self->submission)) {
eekboard_context_service_set_hint_purpose(
self->state,
ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE,
ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL
);
}
server_context_service_show_keyboard(self);
}
void
server_context_service_hide_keyboard (ServerContextService *self)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
if (self->visible) {
server_context_service_real_hide_keyboard (self);
g_idle_add((GSourceFunc)hide_keyboard_source_func, self);
}
}

View File

@ -31,7 +31,7 @@ G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONT
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman, struct vis_manager *visman);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
void server_context_service_show_keyboard (ServerContextService *self);
void server_context_service_force_show_keyboard (ServerContextService *self);
void server_context_service_hide_keyboard (ServerContextService *self);
G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -50,9 +50,16 @@ struct squeekboard {
};
GMainLoop *loop;
static gboolean opt_system = FALSE;
static gchar *opt_address = NULL;
static void
quit (void)
{
g_main_loop_quit (loop);
}
// D-Bus
static void
@ -131,6 +138,67 @@ static const struct wl_registry_listener registry_listener = {
#define SESSION_NAME "sm.puri.OSK0"
GDBusProxy *_proxy = NULL;
GDBusProxy *_client_proxy = NULL;
gchar *_client_path = NULL;
static void
send_quit_response (GDBusProxy *proxy)
{
g_debug ("Calling EndSessionResponse");
g_dbus_proxy_call (proxy, "EndSessionResponse",
g_variant_new ("(bs)", TRUE, ""), G_DBUS_CALL_FLAGS_NONE,
G_MAXINT, NULL, NULL, NULL);
}
static void
unregister_client (void)
{
g_autoptr (GError) error = NULL;
g_return_if_fail (G_IS_DBUS_PROXY (_proxy));
g_return_if_fail (_client_path != NULL);
g_debug ("Unregistering client");
g_dbus_proxy_call_sync (_proxy,
"UnregisterClient",
g_variant_new ("(o)", _client_path),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
&error);
if (error) {
g_warning ("Failed to unregister client: %s", error->message);
}
g_clear_object (&_client_proxy);
g_clear_pointer (&_client_path, g_free);
}
static void client_proxy_signal (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
if (g_str_equal (signal_name, "QueryEndSession")) {
g_debug ("Received QueryEndSession");
send_quit_response (proxy);
} else if (g_str_equal (signal_name, "CancelEndSession")) {
g_debug ("Received CancelEndSession");
} else if (g_str_equal (signal_name, "EndSession")) {
g_debug ("Received EndSession");
send_quit_response (proxy);
unregister_client ();
quit ();
} else if (g_str_equal (signal_name, "Stop")) {
g_debug ("Received Stop");
unregister_client ();
quit ();
}
}
static void
session_register(void) {
@ -151,7 +219,8 @@ session_register(void) {
return;
}
g_dbus_proxy_call_sync(_proxy, "RegisterClient",
g_autoptr (GVariant) res = NULL;
res = g_dbus_proxy_call_sync(_proxy, "RegisterClient",
g_variant_new("(ss)", SESSION_NAME, autostart_id),
G_DBUS_CALL_FLAGS_NONE, 1000, NULL, &error);
if (error) {
@ -160,6 +229,22 @@ session_register(void) {
g_clear_error(&error);
return;
}
g_variant_get (res, "(o)", &_client_path);
g_debug ("Registered client at '%s'", _client_path);
_client_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
0, NULL, "org.gnome.SessionManager", _client_path,
"org.gnome.SessionManager.ClientPrivate", NULL, &error);
if (error) {
g_warning ("Failed to get client proxy: %s", error->message);
g_clear_error (&error);
g_free (_client_path);
_client_path = NULL;
return;
}
g_signal_connect (_client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), NULL);
}
int
@ -307,8 +392,7 @@ main (int argc, char **argv)
session_register();
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
if (connection) {

View File

@ -17,6 +17,7 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
// Defined in Rust
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state, struct vis_manager *vis_manager);
uint8_t submission_hint_available(struct submission *self);
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time);
#endif

View File

@ -93,6 +93,18 @@ pub mod c {
let layout = unsafe { &*layout };
submission.use_layout(layout, Timestamp(time));
}
#[no_mangle]
pub extern "C"
fn submission_hint_available(submission: *mut Submission) -> u8 {
if submission.is_null() {
panic!("Null submission pointer");
}
let submission: &mut Submission = unsafe { &mut *submission };
let active = submission.imservice.as_ref()
.map(|imservice| imservice.is_active());
(Some(true) == active) as u8
}
}
#[derive(Clone, Copy)]
@ -137,7 +149,7 @@ impl Submission {
enum Outcome {
Submitted(Result<(), imservice::SubmitError>),
NotSubmitted,
};
}
let submit_outcome = match data {
SubmitData::Text(text) => {
@ -258,6 +270,7 @@ impl Submission {
.map(|(_id, m)| match m {
Modifier::Control => Modifiers::CONTROL,
Modifier::Alt => Modifiers::MOD1,
Modifier::Mod4 => Modifiers::MOD4,
})
.fold(Modifiers::empty(), |m, n| m | n);
self.virtual_keyboard.set_modifiers_state(raw_modifiers);

View File

@ -43,7 +43,7 @@ pub fn check_layout_file(path: &str) {
fn check_sym_in_keymap(state: &xkb::State, sym_name: &str) -> bool {
let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
if sym == xkb::KEY_NoSymbol {
panic!(format!("Entered invalid keysym: {}", sym_name));
panic!("Entered invalid keysym: {}", sym_name);
}
let map = state.get_keymap();
let range = map.min_keycode()..=map.max_keycode();

View File

@ -19,7 +19,6 @@ pub mod c {
#[repr(transparent)]
pub struct UIManager(*const c_void);
#[no_mangle]
extern "C" {
pub fn server_context_service_update_visible(imservice: *const UIManager, active: u32);
pub fn server_context_service_release_visibility(imservice: *const UIManager);

View File

@ -37,7 +37,6 @@ pub mod c {
}
}
#[no_mangle]
extern "C" {
// From libc, to let KeyMap get deallocated.
fn close(fd: u32);

View File

@ -46,37 +46,56 @@ endforeach
# The layout test is in the examples directory
# due to the way Cargo builds executables
# and the need to call it manually
# and the need to call it manually.
# This is the list of tested builtin layouts.
# Please keep each block alphabetical!
# Please keep shapes (with _) on the same line,
# variants (with +) on separate lines.
foreach layout : [
'us', 'us+colemak', 'us_wide',
'br',
# This is the fallback layout,
# so stays first to make sure it never goes missing.
'us', 'us_wide',
# Block: Languages
'be', 'be_wide',
'bg',
'br',
'cz', 'cz_wide',
'cz+qwerty', 'cz+qwerty_wide',
'de', 'de_wide',
'dk',
'epo',
'es',
'es+cat',
'fi',
'fr', 'fr_wide',
'it+fur',
'gr',
'il',
'ir',
'it',
'it+fur',
'jp+kana','jp+kana_wide',
'no',
'number',
'pl', 'pl_wide',
'ru',
'se',
'th', 'th_wide',
'ua',
'th',
'terminal', 'terminal_wide',
'us+colemak', 'us+colemak_wide',
'us+dvorak', 'us+dvorak_wide',
'emoji',
# Terminal keyboards
'terminal/fr',
'terminal/us',
'terminal/us_wide',
# Block: Not languages.
'emoji/us',
'number/us',
]
extra = []
if layout == 'emoji'
if layout.startswith('emoji/')
extra += ['allow_missing_return']
endif