Compare commits

...

169 Commits

Author SHA1 Message Date
09f7835eb9 Added update to cursor when string is committed 2020-10-13 23:39:27 +02:00
dd80c59aea Fixed surrounding text not getting updated 2020-10-13 22:43:36 +02:00
b8a35530af Can preedit_string get removed? 2020-10-13 22:24:54 +02:00
2f613ea4c7 Merge branch 'x11_prepare' into 'master'
Cleanups leading to Xwayland compatibility

See merge request Librem5/squeekboard!389
2020-10-12 12:45:13 +00:00
8f526bd357 tests: Check for missing return in builtin layouts except emoji 2020-10-12 10:51:23 +00:00
b77b3f7816 vkeyboard: Use a generic slice instead of a vector 2020-10-12 10:51:23 +00:00
87eb775377 keymap: Keep keymap fd management in one place
At the same time, reduce the distance between this and the Xwayland handling branch.
2020-10-12 10:50:40 +00:00
f64e5a3627 Merge branch 'multi_ke' into 'master'
Improve generation of key maps

See merge request Librem5/squeekboard!388
2020-10-12 09:13:29 +00:00
595bbccfdf Merge branch 'fix_a11y' into 'master'
debian: Insert a "breaks" for librem5-base < 24

See merge request Librem5/squeekboard!392
2020-10-12 09:03:58 +00:00
afd47ef8b4 Merge branch 'optimize-sizing' into 'master'
Optimize sizing

See merge request Librem5/squeekboard!390
2020-10-12 08:18:22 +00:00
7e38d17cb7 debian: Insert a "breaks" for librem5-base < 24
Librem5-base sets the GNOME a11y to true, which solves the invisibility of the keyboard on the phone.
2020-10-11 16:36:16 +00:00
ef7df433d8 Merge branch 'wide-keyboards' into 'master'
Add wide keyboards

See merge request Librem5/squeekboard!391
2020-10-11 14:30:35 +00:00
8e32de86a9 Merge branch 'extend-keys-to-bounding-box' into 'master'
Expand key press detection to the edges of the view's bounding box

Closes #191

See merge request Librem5/squeekboard!379
2020-10-11 14:25:03 +00:00
6871452c7b keyboards: add wide terminal layout
This is a copy of the `terminal` layout with modified key dimensions to 
fit a wide arrangement.
2020-10-10 01:40:12 +02:00
192824be39 keyboards: add wide Belgian layout
This is a copy of the `be` layout with modified key dimensions to fit a 
wide arrangement.
2020-10-10 01:38:54 +02:00
938d3c335e keyboards: add wide French layout
This is a copy of the `fr` layout with modified key dimensions to fit a 
wide arrangement.
2020-10-10 01:37:57 +02:00
9dd67ad2bc server-context-service: optimize height calculation
Even though proper size management is being worked on, this patch
proposes a simple and easily revertable solution to device-dependent
sizing issues.

First, it provides different calculations based on the display
orientation. In landscape mode, this allows us to have a sensible
keyboard size while leaving enough screen estate for apps to be able to
display useful information.

Then, it gets rid of the weird calculation for display widths between
360 and 540px. While having some continuity is a pleasant idea, in the
real world in doesn't work, as shown by port attempts to other devices:
a 480x800 display (scale 1) would show an unusable 190px-high keyboard
(about half the size of the Librem 5 on-screen keyboard on a device I
own).

Finally, this commit makes sure we never use a hardcoded size.

Tested on the PinePhone, PineTab and Librem 5.

Note: Current behavior is preserved on the L5 in portrait mode, but
keyboard is a bit smaller in landscape mode; this is deliberate, as it
was previously using too much space (causing some apps, such as chatty,
to be unusable).
2020-10-09 16:30:59 +02:00
9f4cb3c791 eek-gtk-keyboard: use virtual resolution to check arrangement kind
Using the actual monitor width in pixels can lead to unsatisfying
results, depending on the display orientation and physical size: on a
10" tablet with a 1280x800 resolution (scale 1), portrait orientation
will be using the narrow layout.

If the keyboard is sized in an optimal way (i.e. so the layout fills the
whole area, with no blanks on the sides) this would result in an
unnecessarily huge keyboard being displayed, therefore wasting screen
estate.

Using the virtual display size gives a hint about the physical size of
the device, and can be used to select wide layouts even in portrait
mode, while still preserving current behavior on HiDPI devices.

This has been tested on PineTab, PinePhone and Librem 5.
2020-10-09 16:26:10 +02:00
6ed2a47620 Merge branch 'popover-settings' into 'master'
Add settings option to popover

Closes #154

See merge request Librem5/squeekboard!385
2020-10-09 09:18:26 +00:00
4253bf1299 Add settings option to popover
Fixes #154
2020-10-09 19:29:48 +11:00
d283ced2ce Merge branch 'popover-sorting' into 'master'
Sort layouts by type before sorting by name

Closes #176

See merge request Librem5/squeekboard!384
2020-10-07 15:23:25 +00:00
776c0c5f4b Merge branch 'flags' into 'master'
build: Error on repeating declarations

See merge request Librem5/squeekboard!387
2020-10-06 13:35:22 +00:00
8cf6c5f948 syntax: Let older rustc understand symbolmap's lifetime 2020-10-06 10:32:07 +00:00
de3bf54dc9 data: Restore testability of action->keysym conversion 2020-10-06 10:32:07 +00:00
2219eb67e1 keymap: Generate from symbol map, not layout
Includes changes to the keymap string without which Xwayland won't work.
2020-10-06 10:31:28 +00:00
61400c9584 build: Error on repeating declarations 2020-10-06 09:54:17 +00:00
b21734bf57 Merge branch 'docs-morefixes' into 'master'
docs: Tutorial syntax cleanups, reorganization

See merge request Librem5/squeekboard!381
2020-10-06 09:14:56 +00:00
24adba44be Merge branch 'leak-fixes' into 'master'
Fix 2 leaks

Closes #148

See merge request Librem5/squeekboard!386
2020-10-03 07:38:52 +00:00
5e43a31051 Fix leak endlessly adding a resource path to the default theme 2020-10-03 16:23:13 +10:00
40850267d4 Fix leak in level_keyboard_new
xkb_keymap_get_as_string requires that the string it returns is freed by
the caller.
2020-10-03 16:07:36 +10:00
93ac94b83f Sort layouts by type before sorting by name
This makes it such that local layouts like emoji and terminal appear
below language layouts.

Fixes #176
2020-10-03 02:10:56 +10:00
ec5570a547 Merge branch 'keyboard-layout-belgian' into 'master'
proposal for belgian layout (copy of fr)

See merge request Librem5/squeekboard!382
2020-10-01 13:21:49 +00:00
Al
72bd265065 alphabetical order for src/resources.rs tests/meson.build 2020-10-01 14:54:22 +02:00
07bcaa8e2b docs: Reorganize tutorial
People still ignore adding layouts to builtins and to tests. To unbury that information, and add a sort of checklist, the more interesting info has been moved upwards nd together.
2020-10-01 11:52:53 +00:00
Al
4357052fe7 proposal for belgian layout (copy of fr) 2020-09-30 15:42:31 +02:00
c16c686592 docs: Tutorial syntax cleanups
Promoted bolded "headings" into actual headings, so that they can be linked to.
2020-09-29 13:33:37 +00:00
4b825c26a6 Merge branch 'docs-fixes' into 'master'
Expand the development documentation in the readme

Closes #227

See merge request Librem5/squeekboard!377
2020-09-26 09:00:51 +00:00
74479ff226 Expand key press detection to the edges of the view's bounding box
If you have a keyboard layout like the following:

A B C D
 E F G
H I J K

The E and G keys here should be pressed when clicking in the empty space
next to them. This is achieved by not checking the bounding boxes of
each key and instead just using the button and row offset to extend
buttons/rows to the edges of the view. Caching for the size and
position of rows is introduced to simplify implementation and possibly
improve performance.

Fixes #191
2020-09-26 01:37:23 +10:00
94bfa92c12 Expand the development documentation in the readme
Fixes #227
2020-09-26 00:34:09 +10:00
50fb124b26 Merge branch 'hacking-spelling-fixes' into 'master'
Fix spelling mistakes in doc/hacking.md

Closes #217

See merge request Librem5/squeekboard!378
2020-09-25 12:47:17 +00:00
7aa004ceff Fix spelling mistakes in doc/hacking.md
Fixes #217
2020-09-25 21:44:27 +10:00
60056dcf26 Merge branch 'honor-a11y-setting' into 'master'
Honor org.gnome.desktop.a11y.applications screen-keyboard-enabled

Closes #222

See merge request Librem5/squeekboard!370
2020-09-24 06:49:13 +00:00
5580853f31 Merge branch 'depr' into 'master'
rust: Fix deprecation warnings

See merge request Librem5/squeekboard!374
2020-09-21 17:01:51 +00:00
d93e9c2b11 rust: Fix deprecation warnings 2020-09-21 10:57:01 +00:00
4ccf11f4fd server-context-service: Don't show keyboard when disabled
If the corresponding a11y settings is disbaled don't unfold
the keyboad at all.

This helps e.g. running the same session on laptops or when
an external keyboard is attached.

Closes: #222
2020-09-14 11:34:17 +02:00
6c5df02921 Merge branch 'cleanup' into 'master'
A bunch of cleanups

See merge request Librem5/squeekboard!371
2020-09-14 09:26:20 +00:00
b137e2e3a0 Merge branch 'ukrainian-keyboard-layout' into 'master'
Add Ukrainian keyboard layout

Closes #223

See merge request Librem5/squeekboard!372
2020-09-14 07:48:03 +00:00
820a8b6ca1 Add Ukrainian keyboard layout.
Fixes https://source.puri.sm/Librem5/squeekboard/-/issues/223

Signed-off-by: Nazarii Kretovych <nazarii.kretovych@gmail.com>
2020-09-14 08:34:09 +03:00
8bdfb69dc1 server-context-service: swap signal arguments
This makes sure 'self' comes first. While at that fix the
function signatures and use ServerContextService directly
and add type checks so it's easy to notice when we messed up.
2020-09-11 20:44:36 +02:00
1e6bcef055 server-context-service: Consistenty name self argument 'self'
It's confusing when the object a method acts on is sometimes called
context and sometimes called state. So name it 'self' as we do
in other projects.
2020-09-11 20:05:49 +02:00
07faf906d8 eekboard-context-service: Drop private struct
There's no point having it for a final type and it only
makes the code harder to read.
2020-09-11 18:55:35 +02:00
53f30324f0 eekboard-context-service: Drop the GObject boilerplate
The previous commits show it's not really a derivable type
so make it a finale one.
2020-09-11 18:55:35 +02:00
3e212ddab4 eekboard-context-service: Drop docstrings for inexistent functions 2020-09-11 18:46:22 +02:00
966990ad65 eekboad-context-service: Drop signal class handler
It's unused
2020-09-11 18:45:33 +02:00
a8b81172fc build: Enable '-Wformat-nonliteral' 2020-09-11 18:40:56 +02:00
97f51591b3 ServerContextService: Drop GObject boilerplate
G_DECLARE_FINAL_TYPE does this for us
2020-09-11 18:16:30 +02:00
6756fb423a build: Enable -Wredundant-declarations 2020-09-11 18:16:30 +02:00
eb7673d2c2 treewide: Drop redundant declarations 2020-09-11 18:16:30 +02:00
24b6a04903 build: Enable -Wmaybe-uninitialized
No changes needed.
2020-09-11 18:16:30 +02:00
b197cd839e build: Enable -Wformat-security
No changes needed.
2020-09-11 18:16:30 +02:00
857a916402 build: Enable -Winit-self
No changes needed
2020-09-11 18:16:30 +02:00
ca68fc2040 eek-keyboard: Don't ignore return value
This fixes

../eek/eek-keyboard.c:71:5: warning: ignoring return value of ‘getrandom’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
   71 |     getrandom(r, 6, GRND_NONBLOCK);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[17/32] Compiling C object 'src/25a6634@@libsqueekboard@sta/.._eek_eek-renderer.c.o'
2020-09-11 18:16:30 +02:00
bd661bd4f4 gitlab-ci: Enable --Werror
This makes sure we don't have more warnings creeping in
2020-09-11 18:16:30 +02:00
4228192bda layout: Fix warning
This fixes

warning: unnecessary parentheses around block return value
   --> /var/scratch/librem5/squeekboard/src/layout.rs:110:13
    |
110 | /             (point.x > self.x && point.x < self.x + self.width
111 | |                 && point.y > self.y && point.y < self.y + self.height)
    | |______________________________________________________________________^
    |
    = note: `#[warn(unused_parens)]` on by default
2020-09-11 18:16:30 +02:00
0f7ab99da3 keyboard: Fix warning
warning: unused variable: `name`
   --> /var/scratch/librem5/squeekboard/src/keyboard.rs:195:10
    |
195 |     for (name, state) in keystates.iter() {
    |          ^^^^ help: consider prefixing with an underscore: `_name`
2020-09-11 18:16:30 +02:00
e15d317488 eekboard-context-service: Drop EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE
This fixes the

../eekboard/eekboard-context-service.c:244:13: warning: Deprecated pre-processor symbol, replace with
  244 |     self->priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(self);

warning and makes us use more modern GObject style
2020-09-11 18:16:30 +02:00
93e9ce0dd7 build: Enable '-Wunused-function' 2020-09-11 18:16:13 +02:00
9d63b505ec build: Enable '-Wold-style-definition' '-Wstrict-prototypes' 2020-09-11 18:15:44 +02:00
306c11f1fd treewide: Use new style function definitions 2020-09-11 18:15:02 +02:00
c26feed8b2 eekboard-context-service: Return early if schema is unavailable
This also fixes a leak of GSettingsSchema.
2020-09-11 18:14:19 +02:00
2f4a652f53 Merge branch 'fixmods' into 'master'
virtual_keyboard: Fix desynced modifiers state

See merge request Librem5/squeekboard!362
2020-08-28 11:02:50 +00:00
e5796d0d7b Merge branch 'rel-1.9.3' into 'master'
Document changes and release 1.9.3

Closes #212

See merge request Librem5/squeekboard!369
2020-08-06 15:50:00 +00:00
1ee58ce7a0 Document changes and release 1.9.3 2020-08-05 16:17:44 +02:00
88821e2e82 Merge branch 'terminal' into 'master'
Terminal layout: another approach

See merge request Librem5/squeekboard!368
2020-07-20 09:48:36 +00:00
81344bb9c2 Merge branch 'patch-1' into 'master'
Brazilian Portuguese Keyboard Layout.

See merge request Librem5/squeekboard!365
2020-07-20 09:47:19 +00:00
c87b61d065 Brazilian Portuguese Keyboard Layout. 2020-07-20 09:47:19 +00:00
97da44f059 Terminal layout: another approach 2020-07-18 05:43:15 +02:00
f5a5282219 Revert "Merge branch 'btantau-master-patch-76686' into 'master'"
This reverts commit 0c8feb7687, reversing
changes made to 762e12431d.
2020-07-18 03:26:03 +02:00
123faecb7d Merge branch 'italian' into 'master'
italian: Fix space and period

See merge request Librem5/squeekboard!364
2020-07-16 15:57:38 +00:00
7ec3053aa4 Merge branch 'fixsize' into 'master'
size: Hardcode size to work around screen rotation

See merge request Librem5/squeekboard!361
2020-07-16 15:56:57 +00:00
9512fd8436 virtual_keyboard: Fix desynced modifiers state
This ensures that keymap switches leave modifiers and virtual keys in a known state.
2020-07-15 18:08:59 +00:00
0c8feb7687 Merge branch 'btantau-master-patch-76686' into 'master'
New terminal layout, showing more useful keys at the same time

See merge request Librem5/squeekboard!345
2020-07-15 15:06:36 +00:00
f3f1d58fe1 Add Menu key. 2020-07-15 14:42:55 +00:00
c0c666f1b3 Make f-keys slightly wider. 2020-07-15 14:41:17 +00:00
ea22afba79 Add missing Ê key. 2020-07-15 14:41:01 +00:00
d7c7528d3d italian: Fix space and period 2020-07-10 15:04:43 +00:00
93ff086e3a ci: Re-add x64 Buster build 2020-06-28 17:38:10 +00:00
4f72779681 size: Hardcode size to work around screen rotation
Phoc sends output information *after* changing keyboard surface size. Squeekboard adjusts size on surface events, but not on output in this revision, making it unaware of display size at the time of adjustment, resulting in bad adjustment.

This change hardcodes the proportions again to make it work at least on the Librem5.
2020-06-28 17:14:49 +00:00
762e12431d Merge branch 'fix_gio_unix' into 'master'
build: Add missing gio-unix dependency

See merge request Librem5/squeekboard!356
2020-06-26 07:13:23 +00:00
767bb1745c Merge branch 'fix' into 'master'
debian: Require lsb-release

See merge request Librem5/squeekboard!359
2020-06-26 06:50:44 +00:00
26e0473fc1 debian: Require lsb-release 2020-06-25 17:45:08 +00:00
0735d1c6c6 Merge branch 'bullseye' into 'master'
build: Debian Bullseye-compatible deps

See merge request Librem5/squeekboard!357
2020-06-25 17:03:28 +00:00
67d8926913 ci: Add amber job 2020-06-25 16:17:16 +00:00
0299527700 debian: Add amber to legacy distro list 2020-06-25 11:29:47 +00:00
ecfc45c2de build: Make compatible with Debian Bullseye
This commit is a bit bigger than it could have: Meson changes could have gone in separately from CI and Debian.

This commit looks more complicated than it should reasonably be. Alas, Cargo is a piece of work, and it doesn't let honest people just choose different versions of dependencies, leading to a cascade of misery. Several things were tried to curb the disaster:

- Cargo [feature] supports choosing dependencies, but doesn't support specifying dependency versions
- Cargo has a cfg() syntax in sections for choosing dependencies by build options, but it explicitly doesn't support selecting on features…
- Cargo allows choosing different dependencies based on features, so perhaps dependencies with different versions could live in stub crates pulled in as needed? Nope! If a dependency doesn't exist in the repo (and that's the point here), Cargo throws up its hands.

This means Cargo.toml needs to be generated based on the build type. More misery:

- we lose the simplicity of just doing `cargo.sh` for simple housekeeping like deps updates. HACKING.md was updated to reflect that. Perhaps that's inevitable - build options need to be like this.
- Some flaky adjustments needed in `cargo.sh` because of an additional argument that can be mistaken for an argument to the exec in `cargo run`.
- Specifying a custom `Cargo.toml` means Cargo can no longer find any tests, examples, benchmarks, or binaries, because it searches relative to the directory of `Cargo.toml`, which is now the build dir. Extra care needed to not forget about them now.

As soon as Cargo allows anything better for managing deps versions, the above should be undone in its favor.

Good side is that a couple bugs went away:

- build flags not always making it to Cargo
- arm64 builds were optional while they shouldn't
- test layouts in unit tests are loaded from an explicit directory now

The Bullseye versions of dependencies are canonical now, Buster considered legacy.
2020-06-24 15:51:21 +00:00
3ba6aca99d build: Add missing gio-unix dependency 2020-06-13 08:27:33 +00:00
40b3172de8 Merge branch '1.9.2' into 'master'
Release 1.9.2

See merge request Librem5/squeekboard!354
2020-06-07 15:23:02 +00:00
1a2df96c02 Release v1.9.2 "Aristotelian physics"
- Swedish
- Russian
- Danish
- French
- minor fixes
2020-06-07 14:38:23 +00:00
d2989e8ecd Merge branch 'lfb' into 'master'
Provide haptic feedback via libfeedback

Closes #166

See merge request Librem5/squeekboard!350
2020-06-04 13:31:24 +00:00
ec58442724 eek-gtk-keyboard: Trigger event feedback on button press
Use libfeedback to trigger feedback for the button press event.

Closes: #166
2020-06-04 12:13:58 +02:00
e89c9b02a0 debian: Build-depend on libfeedback
Will be needed in the following commits.
2020-06-02 10:10:43 +02:00
1b2725250b eek: Drop libcanberra usage 2020-06-02 10:10:43 +02:00
e285ecce93 d/rules: Only remove Cargo.lock if it exists
This allows to invoke the build target twice in a row
2020-06-02 10:10:43 +02:00
75ecf9059a Update rust deps for release 2020-06-01 09:36:36 +00:00
5715458d33 Merge branch 'master' into 'master'
Add danish keylayout

See merge request Librem5/squeekboard!353
2020-05-27 07:14:35 +00:00
8196117269 Merge branch 'desktopfile-absolute' into 'master'
sm.puri.Squeekboard.desktop: make path to Exec= absolute

See merge request Librem5/squeekboard!352
2020-05-26 18:20:49 +00:00
7fed1339ed add test for danish layout 2020-05-26 16:37:14 +02:00
0339d13ce4 Danish keyboard layout 2020-05-26 16:22:38 +02:00
9f1e49da5d Danish keyboard layout 2020-05-26 16:21:17 +02:00
c1737c763f sm.puri.Squeekboard.desktop: make path to Exec= absolute
This uses the same logic from Phosh to render absolute paths in .desktop
files.
2020-05-26 12:02:55 +02:00
fe22fc2271 Merge branch 'remove-dep-libcroco' into 'master'
Removed unused dependency 'libcroco'

See merge request Librem5/squeekboard!351
2020-05-24 18:19:15 +00:00
f473a47eb8 Removed unused dependency 'libcroco' 2020-05-24 16:31:43 +02:00
76bd87686a Merge branch 'ru-layout' into 'master'
Fresh Russian layout

See merge request Librem5/squeekboard!347
2020-05-20 17:59:22 +00:00
ef85823528 Fresh Russian layout 2020-05-20 17:59:22 +00:00
54ac7511cd Merge branch 'scaling' into 'master'
Stop scaling

See merge request Librem5/squeekboard!339
2020-05-13 11:01:21 +00:00
080bbb4d4e Merge branch 'fix_doc_reference' into 'master'
Folder is doc, not docs

See merge request Librem5/squeekboard!349
2020-05-08 16:44:44 +00:00
7c35307011 Folder is doc, not docs 2020-05-07 16:06:44 +02:00
cd3255b301 Merge branch 'swedish_wide_button' into 'master'
Swedish keyboard, use wide button switching between numbers, symbols and base

See merge request Librem5/squeekboard!344
2020-05-06 11:22:13 +00:00
f8b6b98633 Merge branch '1.9.1' into 'master'
Release 1.9.1

See merge request Librem5/squeekboard!348
2020-04-29 13:04:04 +00:00
6414b57e57 Merge branch 'french-layout' into 'master'
French layout

See merge request Librem5/squeekboard!346
2020-04-28 12:31:44 +00:00
46de67a4db Add Ctrl and Alt modifier keys. 2020-04-28 08:29:41 +00:00
2a20bbbf2a tests: add french layout 2020-04-27 09:51:40 +02:00
13ebf9449f keyboards: fr: improve diacritics layout
This commit changes the layout of the `eschars` view, in an attempt at 
making it more logical and optimized to reach more frequent symbols more 
easily.
2020-04-23 14:49:34 +02:00
fa8449eb1e keyboards: fr: improve consistency with other layouts
In order to stay consistent with other layouts, the bottom line has been 
re-arranged and keys dimensions have been optimized (based on the 
`terminal` layout, which has similar features).

Furthermore, the ç/Ç keys in the 'normal' views have been replaced with 
./, so that all diacritics are on the same view.

This commit also fixes the Backspace key behavior.
2020-04-23 14:48:58 +02:00
0bdb5f1f33 resources: include French keyboard layout 2020-04-23 13:07:33 +02:00
b631817896 keyboards: fr: make sure the layout fits the screen
Due to an excessive amount of keys in the `eschars` view, one of the
rows would overflow the screen. This commit removes the `Ç` key, which
is already present in the `upper` view.
2020-04-21 19:39:17 +02:00
09c78da7a7 keyboards: fr: fix keyboard layout
A number of keys would not give the expected result, due to the lack of
a `text` of `action` property. Additionally, the layout wouldn't load
because of an old file format (it seems `bounds` are not supported
anymore).

This patch makes sure this layout can be loaded and used by squeekboard
v0.1.9.
2020-04-21 19:39:07 +02:00
7de7dc1b33 Replace duplicated show_symbols by show_eschars and removed "Delete" button that it's doing nothing 2020-04-21 19:17:27 +02:00
323d89e4cb Add new file 2020-04-21 19:17:27 +02:00
a5ce25055f Show more useful keys at the same time. 2020-04-10 16:43:36 +00:00
184bdaa0b6 More fixes of button sizes 2020-03-26 18:02:32 +01:00
0459d33c4c Swedish keyboard, wide button switching between numbers, symbols and base 2020-03-26 15:12:01 +01:00
3d1a641ca3 Merge remote-tracking branch 'upstream/master' into scaling 2020-03-12 10:51:30 +00:00
0466a520f2 Merge branch 'predictoin_ui' into 'master'
Cleanups to make EekGtkKeyboard more standalone

See merge request Librem5/squeekboard!336
2020-03-12 10:46:14 +00:00
9e8aca1cbf Merge branch 'unavailable' into 'master'
Crash less when outside resources are unavailable

See merge request Librem5/squeekboard!341
2020-03-11 10:55:05 +00:00
334898c5a5 Release version 1.9.1 "Expected value"
- Preview support for "Control" and "Alt" modifiers in buttons, use field "modifiers: Control" inside a button
- Don't crash when gsettings has an empty list of settings
- Documentation rearrangement
2020-03-08 10:06:53 +00:00
67a5f1bb0b cargo: Bump package versions before release 2020-03-08 10:02:54 +00:00
eb84e52897 Merge branch 'release_check' into 'master'
CI: Fix typo

See merge request Librem5/squeekboard!343
2020-03-07 11:38:34 +00:00
0f7ff1636d CI: Fix typo 2020-03-07 11:17:25 +00:00
8ff8e8ac48 Merge remote-tracking branch 'upstream/master' into scaling 2020-03-07 10:46:09 +00:00
d4bb9038c5 Merge branch 'release_check' into 'master'
CI: Test that any bump to changelog has a corresponding tag

See merge request Librem5/squeekboard!337
2020-03-07 10:33:53 +00:00
f3caeb8fc6 Merge branch 'docs' into 'master'
Docs: describe project priorities

See merge request Librem5/squeekboard!338
2020-03-07 10:33:03 +00:00
abaaf04b87 Merge branch 'modifiers' into 'master'
Add simple modifiers support

See merge request Librem5/squeekboard!306
2020-03-07 10:32:33 +00:00
2770e1769c sizing: Ignore scaling factor for layout selection 2020-03-07 10:31:39 +00:00
3cd170acc3 sizing: Create a standalone UI shape manager
The manager is used for sizing the layer surface. It promises never to exceed half the output height.

The selection of the current layout is not being done here, leading to worse behaviour in 1:1 scaling.

In the future, it could be used for sizing the keyboard itself and the suggestion box, as well as decide which layout to use, because layouts should have some sizing hints.
2020-03-07 10:31:39 +00:00
24f709ab13 Remove unused code 2020-03-07 10:31:39 +00:00
784f9127fa layout: Minor generalizations 2020-03-07 10:31:39 +00:00
22daefba3a levelkeyboard: Rearrange to make future conversion easier 2020-03-07 10:31:39 +00:00
4ff9cf087b renderer: Simplify by dropping gobjectness 2020-03-07 10:31:39 +00:00
61e1ab5c5a layout: Split out choice to a struct on its own 2020-03-07 10:26:52 +00:00
7fbc9ed56e Merge branch 'master' into 'master'
Fix minor comment typos

See merge request Librem5/squeekboard!342
2020-03-04 07:18:02 +00:00
&t
67cc4f11cf Fix minor comment typos 2020-03-04 04:53:53 +00:00
8ac2b5a713 gsettings: Don't crash on switching when unavailable 2020-03-03 19:46:53 +00:00
8bae8fe5bb dbus: Don't crash if can't make a connection 2020-03-03 19:25:49 +00:00
b3cfc8a0f3 gsettings: Don't crash when unavailable 2020-03-03 19:10:50 +00:00
5a591127a1 Merge branch 'doap' into 'master'
meta: Add doap file

See merge request Librem5/squeekboard!335
2020-03-01 14:21:45 +00:00
8f3d010349 hacking: Move into docs/ 2020-02-28 14:10:44 +00:00
7eb5c6d466 docs: Add the guiding principle 2020-02-28 13:26:09 +00:00
9f6fe8318c CI: Test that any bump to changelog has a corresponding tag
Prevents forgetting to sign the tag, which is currently done out of band and independently of review.
2020-02-28 12:14:18 +00:00
92e9d994fe modifiers: Support Control and Alt
Control and Alt are special in that they aren't expected to switch levels, and so don't need to change what characters are output.

Use in layouts by adding `modifier: Control` or `modifier: Alt` in place of `text: "foo"`.

The latching of the modifier will force the keyboard to emit raw key presses and prevent it from outputting text.
2020-02-28 11:21:07 +00:00
c28f07fcfd Merge branch 'fix_variant' into 'master'
settings: Handle empty settings

See merge request Librem5/squeekboard!333
2020-02-28 11:09:11 +00:00
80919dbc42 Merge branch 'fix_ref' into 'master'
Variant: Use proper pointer conversion between C and Rust

See merge request Librem5/squeekboard!334
2020-02-28 11:08:46 +00:00
cc369f6f81 Merge branch 'press' into 'master'
layout: Improve press handling

See merge request Librem5/squeekboard!330
2020-02-26 18:43:21 +00:00
99f2f286e3 Merge branch '1.9.0' into 'master'
Release 1.9.0

See merge request Librem5/squeekboard!328
2020-02-24 12:55:52 +00:00
46cbaf8e87 keyboard: Remove unused code 2020-02-23 12:15:19 +00:00
53b4466899 meta: Add doap file 2020-02-23 10:42:07 +00:00
c0aee5de26 Variant: Use proper pointer conversion between C and Rust 2020-02-20 12:17:50 +00:00
404f94638f settings: Handle empty settings 2020-02-20 12:06:47 +00:00
cb802cfb50 layout: Improve press handling
Makes it more similar to release handling, removes some redundant checks.

This makes it easier to integrate modifiers in the future.
2020-02-19 15:40:39 +00:00
74 changed files with 3430 additions and 1384 deletions

View File

@ -1,4 +1,4 @@
image: debian:buster
image: debian:bullseye
stages:
- build
@ -11,7 +11,7 @@ stages:
before_script:
- apt-get -y update
- apt-get -y install wget ca-certificates gnupg
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- echo "deb [trusted=yes] http://ci.puri.sm/ bullseyeci main" > /etc/apt/sources.list.d/ci.list
- wget -O- https://ci.puri.sm/ci-repo.key | apt-key add -
- apt-get -y update
@ -27,7 +27,8 @@ build_docs:
- ./doc/build.sh _build
build_meson:
<<: *tags
tags:
- librem5
stage: build
artifacts:
paths:
@ -35,7 +36,7 @@ build_meson:
expire_in: 3h
script:
- apt-get -y build-dep .
- meson . _build/ -Ddepdatadir=/usr/share
- meson . _build/ -Ddepdatadir=/usr/share --werror
- ninja -C _build install
build_deb:
@ -52,15 +53,66 @@ build_deb:
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:arm64:
build_deb:amber:
image: pureos/amber
tags:
- librem5:arm64
allow_failure: true
- librem5
stage: build
artifacts:
paths:
- "*.deb"
script:
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- apt-get -y update
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:buster:
image: "debian:buster"
tags:
- librem5
stage: build
artifacts:
paths:
- "*.deb"
script:
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- apt-get -y update
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:arm64:
tags:
- librem5:arm64
stage: build
artifacts:
paths:
- "*.deb"
script:
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
build_deb:arm64_buster:
image: "debian:buster"
tags:
- librem5:arm64
stage: build
artifacts:
paths:
- "*.deb"
script:
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- apt-get -y update
- rm -f ../*.deb
- apt-get -y build-dep .
- apt-get -y install devscripts
@ -77,10 +129,21 @@ test_lintian:
- lintian *.deb
test:
<<: *tags
tags:
- librem5
stage: test
needs:
- build_meson
script:
- apt-get -y build-dep .
- ninja -C _build test
check_release:
<<: *tags
stage: test
only:
refs:
- master
script:
- apt-get -y install git python3
- (head -n 1 ./debian/changelog && git tag) | ./debian/check_release.py

22
Cargo.deps Normal file
View File

@ -0,0 +1,22 @@
# Dependencies which change based on build flags
bitflags = "1.2.*"
clap = { version = "2.33.*", default-features = false }
regex = { version = "1.3.*", default-features = false, features = ["std", "unicode-case"] }
[dependencies.cairo-rs]
version = "0.7.*"
[dependencies.gdk]
version = "0.11.*"
[dependencies.gio]
version = "0.7.*"
features = ["v2_44"]
[dependencies.glib]
version = "0.8.*"
features = ["v2_44"]
[dependencies.gtk]
version = "0.7.*"
features = ["v3_22"]

22
Cargo.deps.legacy Normal file
View File

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

406
Cargo.lock generated
View File

@ -1,66 +1,69 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.8"
name = "atk"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"atk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atk-sys"
version = "0.7.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.4"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cairo-rs"
version = "0.5.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cairo-sys-rs"
version = "0.7.0"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.50"
version = "1.0.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.32.0"
version = "2.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -76,159 +79,161 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "gdk"
version = "0.9.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gdk-pixbuf"
version = "0.5.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gdk-pixbuf-sys"
version = "0.7.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gdk-sys"
version = "0.7.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gio"
version = "0.5.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gio-sys"
version = "0.7.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glib"
version = "0.6.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glib-sys"
version = "0.7.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gobject-sys"
version = "0.7.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gtk"
version = "0.5.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gtk-sys"
version = "0.7.0"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -239,12 +244,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.66"
version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "linked-hash-map"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -252,42 +257,37 @@ name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pango"
version = "0.5.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pango-sys"
version = "0.7.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -298,7 +298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "1.0.8"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -306,105 +306,93 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.2"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "1.1.9"
version = "1.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.6.14"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rs"
version = "0.1.0"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
"xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde"
version = "1.0.104"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
version = "1.0.104"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_yaml"
version = "0.8.11"
version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "1.0.14"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-width"
version = "0.1.7"
@ -415,11 +403,6 @@ name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
@ -444,63 +427,60 @@ name = "xkbcommon"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "yaml-rust"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811"
"checksum atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7017e53393e713212aed7aea336b6553be4927f58c37070a56c2fe3d107e489"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd940f0d609699e343ef71c4af5f66423afbf30d666f796dabd8fd15229cf5b6"
"checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2"
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86b7499272acf036bb5820c6e346bbfb5acc5dceb104bc2c4fd7e6e33dfcde6a"
"checksum atk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e552c1776737a4c80110d06b36d099f47c727335f9aaa5d942a72b6863a8ec6f"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e05db47de3b0f09a222fa4bba2eab957d920d4243962a86b2d77ab401e4a359c"
"checksum cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff65ba02cac715be836f63429ab00a767d48336efc5497c5637afb53b4f14d63"
"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96"
"checksum gdk-pixbuf 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc3aa730cb4df3de5d9fed59f43afdf9e5fb2d3d10bfcbd04cec031435ce87f5"
"checksum gdk-pixbuf-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08284f16ce4d909b10d785a763ba190e222d2c1557b29908bf0a661e27a8ac3b"
"checksum gdk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "108548ebf5329b551f2b97ab356908d14627905abb74b936c3372de1535aee81"
"checksum gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "29a44b051990573448edc80b1995237f8b97b5734d2aec05105b9242aa10af11"
"checksum gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6975ada29f7924dc1c90b30ed3b32d777805a275556c05e420da4fbdc22eb250"
"checksum glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a333edf5b9f1411c246ef14e7881b087255f04c56dbef48c64a0cb039b4b340"
"checksum glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3573351e846caed9f11207b275cd67bc07f0c2c94fb628e5d7c92ca056c7882d"
"checksum gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08475e4a08f27e6e2287005950114735ed61cec2cb8c1187682a5aec8c69b715"
"checksum gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a6b30f194f09a17bb7ffa95c3ecdb405abd3b75ff981f831b1f6d18fe115ff"
"checksum gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d487d333a4b87072e6bf9f2e55befa0ebef01b9496c2e263c0f4a1ff3d6c04b1"
"checksum gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6243e995f41f3a61a31847e54cc719edce93dd9140c89dca3b9919be1cfe22d5"
"checksum gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9726408ee1bbada83094326a99b9c68fea275f9dbb515de242a69e72051f4fcc"
"checksum gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d8991b060a9e9161bafd09bf4a202e6fd404f5b4dd1a08d53a1e84256fb34ab0"
"checksum gdk-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6adf679e91d1bff0c06860287f80403e7db54c2d2424dce0a470023b56c88fbb"
"checksum gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6261b5d34c30c2d59f879e643704cf54cb44731f3a2038000b68790c03e360e3"
"checksum gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4fad225242b9eae7ec8a063bb86974aca56885014672375e5775dc0ea3533911"
"checksum glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "be27232841baa43e0fd5ae003f7941925735b2f733a336dc75f07b9eff415e7b"
"checksum glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2"
"checksum gobject-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9"
"checksum gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "709f1074259d4685b96133f92b75c7f35b504715b0fcdc96ec95de2607296a60"
"checksum gtk-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53def660c7b48b00b510c81ef2d2fbd3c570f1527081d8d7947f471513e1a4c1"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
"checksum linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2cb169402a3eb1ba034a7cc7d95b8b1c106e9be5ba4be79a5a93dc1a2795f4"
"checksum pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6eb49268e69dd0c1da5d3001a61aac08e2e9d2bfbe4ae4b19b9963c998f6453"
"checksum pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393fa071b144f8ffb83ede273758983cf414ca3c0b1d2a5a9ce325b3ba3dd786"
"checksum pango-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86b93d84907b3cf0819bff8f13598ba72843bee579d5ebc2502e4b0367b4be7d"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad"
"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
"checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
"checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
"checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
"checksum serde_yaml 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
"checksum syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fda0ea5f7ddabd51deeeda7799bee06274112f577da7dd3d954b8eda731b2fce"
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
"checksum yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"

View File

@ -2,46 +2,39 @@
name = "rs"
version = "0.1.0"
[dependencies]
bitflags = "1.0.*"
clap = { version = "2.32.*", default-features = false }
maplit = "1.0.*"
regex = "1.1.*"
serde = { version = "1.0.*", features = ["derive"] }
serde_yaml = "0.8.*"
xkbcommon = { version = "0.4.*", features = ["wayland"] }
[lib]
name = "rs"
path = "@path@/src/lib.rs"
crate-type = ["staticlib", "rlib"]
[dependencies.cairo-rs]
version = "0.5.*"
# Cargo can't do autodiscovery if Cargo.toml is not in the root.
[[bin]]
name = "test_layout"
path = "@path@/src/bin/test_layout.rs"
[[example]]
name = "test_layout"
path = "@path@/examples/test_layout.rs"
[features]
gio_v0_5 = []
gtk_v0_5 = []
# Dependencies which don't change based on build flags
[dependencies.cairo-sys-rs]
version = ""
[dependencies.gdk]
version = ""
[dependencies.gio]
version = ""
features = ["v2_44"]
[dependencies.glib]
version = ""
features = ["v2_44"]
[dependencies.glib-sys]
version = ""
features = ["v2_44"]
[dependencies.gtk]
version = "0.5.*"
features = ["v3_22"]
[dependencies.gtk-sys]
version = ""
features = ["v3_22"]
[lib]
name = "rs"
path = "src/lib.rs"
crate-type = ["staticlib", "rlib"]
[dependencies]
maplit = "1.0.*"
serde = { version = "1.0.*", features = ["derive"] }
serde_yaml = "0.8.*"
xkbcommon = { version = "0.4.*", features = ["wayland"] }
# Here is inserted the Cargo.deps file

View File

@ -30,30 +30,43 @@ Building
### Dependencies
See `.gitlab-ci.yml`.
See `.gitlab-ci.yml` or run `apt-get build-dep .`
### Build from git repo
```
```bash
$ git clone https://source.puri.sm/Librem5/squeekboard.git
$ cd squeekboard
$ mkdir ../build
$ meson ../build/
$ cd ../build
$ ninja test
$ ninja install
$ mkdir _build
$ meson _build/
$ cd _build
$ ninja
```
To run tests use `ninja test`. To install squeekboard run `ninja install`.
Running
-------
```
```bash
$ phoc # if no compatible Wayland compositor is running yet
$ cd ../build/
$ src/squeekboard
```
Squeekboard honors the gnome "screen-keyboard-enabled" setting. Either enable this through gnome-settings under accessibility or run:
```bash
$ gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true
```
To make the keyboard show you can use either an application that does so automatically, like a text editor or `python3 ./tests/entry.py`, or you can manually trigger it with:
```bash
busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
```
Developing
----------
See `HACKING.md`
See [`doc/hacking.md`](doc/hacking.md) for this copy, or the [official documentation](https://developer.puri.sm/projects/squeekboard/) for the current release.

View File

@ -13,5 +13,10 @@ CARGO_TARGET_DIR="$(pwd)"
export CARGO_TARGET_DIR
cd "$SOURCE_DIR"
cargo "$@"
# the 'run" command takes arguments at the end,
# so --manifest-path must not be last
CMD="$1"
shift
cargo "$CMD" --manifest-path "$CARGO_TARGET_DIR"/Cargo.toml "$@"

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

@ -0,0 +1,89 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 52 }
spaceline: { width: 140, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "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 Return"
upper:
- "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 Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "âÂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

View File

@ -0,0 +1,89 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 100, height: 42 }
spaceline: { width: 205, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "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 Return"
upper:
- "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 Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "âÂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

78
data/keyboards/br.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:
- "q w e r t y u i o p"
- "a s d f g h j k l ç"
- "Shift_L z x c v b n m BackSpace"
- "show_numbers preferences space , Return"
upper:
- "Q W E R T Y U I O P"
- "A S D F G H J K L Ç"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space period Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "à À á Á ã Ã â Â é É"
- "show_symbols ê Ê í Í ó Ó ô Ô"
- "show_letters õ Õ ú Ú ü Ü period BackSpace"
symbols:
- "@ # $ % - + ÷ × = ≠"
- "( ) § & < > / * { }"
- "show_numbers_from_symbols º \" ' colon ; ! ? 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: "1ã"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "1ã"
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: ":"

98
data/keyboards/dk.yaml Normal file
View File

@ -0,0 +1,98 @@
---
outlines:
default: { width: 32, height: 52 }
altline: { width: 48.39024, height: 52 }
wide: { width: 62, height: 52 }
outline7: { width: 88.97561, height: 52 }
spaceline: { width: 150.5853, height: 52 }
views:
base:
- "q w e r t y u i o p å"
- "a s d f g h j k l ø æ"
- "Shift_L z x c v b n m BackSpace"
- "show_numbers preferences space . Return"
upper:
- "Q W E R T Y U I O P Å"
- "A S D F G H J K L Ø Æ"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space . Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' : ; ! ? BackSpace"
- "show_letters preferences space . Return"
symbols:
- "~ ` | U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree * { }"
- "show_numbers \\ / < > = [ ] BackSpace"
- "show_letters preferences space . Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "altline"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "altline"
label: "ABC"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
".":
outline: altline
space:
outline: spaceline
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
U00B7:
text: "·"
squareroot:
text: "√"
Greek_pi:
text: "π"
division:
text: "÷"
multiply:
text: "×"
paragraph:
text: "¶"
Greek_tau:
text: "τ"
copyright:
text: "©"
U00AE:
text: "®"
U00A3:
text: "£"
EuroSign:
text: "€"
U00A5:
text: "¥"
asciicircum:
text: "^"
degree:
text: "°"

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

@ -0,0 +1,89 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 52 }
spaceline: { width: 140, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "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 Return"
upper:
- "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 Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "âÂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

View File

@ -0,0 +1,89 @@
---
outlines:
default: { width: 54, height: 42 }
altline: { width: 81, height: 42 }
wide: { width: 100, height: 42 }
spaceline: { width: 205, height: 42 }
special: { width: 54, height: 42 }
views:
base:
- "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 Return"
upper:
- "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 Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space show_eschars Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space show_eschars Return"
eschars:
- "à â ç é è ê î ô ù û"
- "À Â Ç É È Ê Î Ô Ù Û"
- "show_numbers_from_symbols æ œ ä ë ï ö ü BackSpace"
- "show_letters preferences space show_eschars Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "abc"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
show_eschars:
action:
locking:
lock_view: "eschars"
unlock_view: "base"
outline: "altline"
label: "âÂ"
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
"\"":
keysym: "quotedbl"

View File

@ -13,22 +13,22 @@ views:
- "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"
- "show_numbers show_eschars preferences space , . Return"
upper:
- "Q W E R T Y U I O P"
- "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers show_eschars preferences space ? period Return"
- "show_numbers show_eschars preferences space ? . Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # € % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters show_eschars preferences space ? period Return"
- "show_letters show_eschars preferences space ? . Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }"
- "show_numbers \\ / < > = [ ] BackSpace"
- "show_letters show_eschars preferences space ? period Return"
- "show_letters show_eschars preferences space ? . Return"
eschars:
- "á é í ó ú Á É Í Ó Ú"
- "à è ì ò « » ù ! { }"
@ -76,12 +76,10 @@ buttons:
set_view: "eschars"
outline: "altline"
label: "àè"
period:
outline: "default"
label: "."
space:
outline: "spaceline"
label: " "
text: " "
Return:
outline: "altline"
icon: "key-enter"

94
data/keyboards/ru.yaml Normal file
View File

@ -0,0 +1,94 @@
---
outlines:
default: { width: 32, height: 52 }
altline: { width: 32, height: 52 }
wide: { width: 57, height: 52 }
narrow: { width: 26, height: 52 }
spaceline: { width: 107, height: 52 }
fill: { width: 159, height: 52 }
special: { width: 42, height: 52 }
views:
base:
- "й ц у к е н г ш щ з х"
- "ф ы в а п р о л д ж э"
- "Shift_L я ч с м и т ь б ю BackSpace"
- "show_numbers preferences ё space ъ period Return"
upper:
- "Й Ц У К Е Н Г Ш Щ З Х"
- "Ф Ы В А П Р О Л Д Ж Э"
- "Shift_L Я Ч С М И Т Ь Б Ю BackSpace"
- "show_numbers preferences Ё space Ъ comma Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space_fill period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space_fill 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: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "АБВ"
show_symbols:
action:
set_view: "symbols"
outline: "wide"
label: "*/="
period:
outline: "special"
text: "."
comma:
outline: "special"
text: ","
space:
outline: "spaceline"
text: " "
space_fill:
outline: "fill"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
ё:
outline: "narrow"
Ё:
outline: "narrow"
ъ:
outline: "narrow"
Ъ:
outline: "narrow"

View File

@ -2,9 +2,10 @@
outlines:
default: { width: 32, height: 52 }
altline: { width: 48.39024, height: 52 }
wide: { width: 62, height: 52 }
outline7: { width: 88.97561, height: 52 }
spaceline: { width: 150.5853, height: 52 }
wide: { width: 64, height: 52 }
spaceline: { width: 142, height: 52 }
special: { width: 44, height: 52 }
views:
base:
@ -25,7 +26,7 @@ views:
symbols:
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
- "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
- "show_numbers_from_symbols backslash slash less greater equal bracketleft bracketright BackSpace"
- "show_letters preferences space . Return"
buttons:
@ -37,22 +38,27 @@ buttons:
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
outline: altline
icon: "edit-clear-symbolic"
action: erase
preferences:
action: "show_prefs"
outline: "altline"
outline: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
outline: "wide"
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: altline
label: "123"
show_letters:
outline: "wide"
action:
set_view: "base"
outline: "altline"
label: "ABC"
show_symbols:
action:
@ -60,7 +66,7 @@ buttons:
outline: "altline"
label: "*/="
".":
outline: altline
outline: "special"
space:
outline: spaceline
text: " "
@ -156,4 +162,3 @@ buttons:
text: "["
bracketright:
text: "]"

View File

@ -1,38 +1,44 @@
---
outlines:
default: { width: 35.33, height: 52 }
action: { width: 59, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 52 }
spaceline: { width: 140, height: 52 }
special: { width: 44, height: 52 }
default: { width: 35.33, height: 46 }
action: { width: 59, height: 46 }
altline: { width: 52.67, height: 46 }
wide: { width: 59, height: 46 }
spaceline: { width: 140, height: 46 }
special: { width: 44, height: 46 }
small: { width: 59, height: 22 }
views:
base:
- "Ctrl Alt ↑ ↓ ← →"
- "q w e r t y u i o p"
- "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace"
- "show_numbers preferences space show_actions Return"
upper:
- "Ctrl Alt PgUp PgDn Home End"
- "Q W E R T Y U I O P"
- "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space show_actions Return"
numbers:
- "Ctrl Alt ↑ ↓ ← →"
- "1 2 3 4 5 6 7 8 9 0"
- "* # $ / & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "Ctrl Alt ↑ ↓ ← →"
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° @ { }"
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace"
- "show_letters preferences space period Return"
actions:
- "Ctrl Alt PgUp PgDn Home End"
- "F1 F2 F3 F4 F5 F6"
- "F7 F8 F9 F10 F11 F12"
- "Esc Tab Del PgUp ↑ PgDn"
- "show_letters Home End ← ↓ →"
- "Esc Tab Pause Insert Up Del"
- "show_letters Menu Break Left Down Right"
buttons:
Shift_L:
@ -132,28 +138,67 @@ buttons:
Del:
outline: "action"
keysym: "Delete"
Home:
Insert:
outline: "action"
keysym: "Insert"
Menu:
outline: "action"
keysym: "Menu"
Pause:
outline: "action"
keysym: "Pause"
Menu:
outline: "action"
keysym: "Menu"
Break:
outline: "action"
keysym: "Break"
Home:
outline: "small"
keysym: "Home"
End:
outline: "action"
outline: "small"
keysym: "End"
PgUp:
outline: "action"
outline: "small"
keysym: "Page_Up"
PgDn:
outline: "action"
outline: "small"
keysym: "Page_Down"
"↑":
outline: "action"
outline: "small"
keysym: "Up"
"↓":
outline: "action"
outline: "small"
keysym: "Down"
"←":
outline: "action"
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"

View File

@ -0,0 +1,211 @@
---
outlines:
default: { width: 54, height: 37 }
action: { width: 90, height: 37 }
altline: { width: 81, height: 37 }
wide: { width: 90, height: 37 }
spaceline: { width: 225, height: 37 }
special: { width: 54, height: 37 }
small: { width: 67.4, height: 22 }
views:
base:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "q w e r t y u i o p"
- "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace"
- "show_numbers preferences space show_actions Return"
upper:
- "EscSmall TabSmall Ctrl Alt PgUp PgDn Home End"
- "Q W E R T Y U I O P"
- "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space 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 period Return"
symbols:
- "EscSmall TabSmall Ctrl Alt ↑ ↓ ← →"
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° @ { }"
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace"
- "show_letters preferences space period 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:
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_actions:
action:
set_view: "actions"
outline: "altline"
label: ">_"
period:
outline: "altline"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
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"
Menu:
outline: "action"
keysym: "Menu"
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"

94
data/keyboards/ua.yaml Normal file
View File

@ -0,0 +1,94 @@
---
outlines:
default: { width: 32, height: 52 }
altline: { width: 32, height: 52 }
wide: { width: 57, height: 52 }
narrow: { width: 26, height: 52 }
spaceline: { width: 107, height: 52 }
fill: { width: 159, height: 52 }
special: { width: 42, height: 52 }
views:
base:
- "й ц у к е н г ш щ з х"
- і в а п р о л д ж є"
- "Shift_L я ч с м и т ь б ю BackSpace"
- "show_numbers preferences ґ space ї period Return"
upper:
- "Й Ц У К Е Н Г Ш Щ З Х"
- І В А П Р О Л Д Ж Є"
- "Shift_L Я Ч С М И Т Ь Б Ю BackSpace"
- "show_numbers preferences Ґ space Ї comma Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space_fill period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space_fill 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: "wide"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "АБВ"
show_symbols:
action:
set_view: "symbols"
outline: "wide"
label: "*/="
period:
outline: "special"
text: "."
comma:
outline: "special"
text: ","
space:
outline: "spaceline"
text: " "
space_fill:
outline: "fill"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"
ґ:
outline: "narrow"
Ґ:
outline: "narrow"
ї:
outline: "narrow"
Ї:
outline: "narrow"

11
data/langs/ru-RU.txt Normal file
View File

@ -0,0 +1,11 @@
de Немецкий
es Испанский
fi Финский
gr Греческий
it Итальянский
no Норвежский
pl Польский
ru Русский
se Шведский
terminal Терминал
us Английский (США)

View File

@ -7,13 +7,20 @@ squeekboard_resources = gnome.compile_resources(
c_name: 'squeekboard',
)
desktopconf = configuration_data()
desktopconf.set('bindir', bindir)
desktop_file = 'sm.puri.Squeekboard.desktop'
i18n.merge_file('desktop',
input: desktop_file + '.in',
input: configure_file(
input: desktop_file + '.in.in',
output: desktop_file + '.in',
configuration: desktopconf
),
output: desktop_file,
po_dir: '../po',
install: true,
install_dir: join_paths(datadir, 'applications'),
install_dir: desktopdir,
type: 'desktop'
)

View File

@ -2,7 +2,7 @@
Name=Squeekboard
GenericName=Squeekboard Virtual Keyboard
Comment=Virtual Keyboard
Exec=squeekboard
Exec=@bindir@/squeekboard
Terminal=false
Type=Application
NoDisplay=true

View File

@ -40,6 +40,10 @@ sq_button.action {
font-size: 0.75em;
}
sq_button.small {
font-size: 0.5em;
}
#Return {
background: #1c71d8;
border-color: #1a5fb4;

View File

@ -43,6 +43,10 @@ sq_button.action {
font-size: 0.75em;
}
sq_button.small {
font-size: 0.5em;
}
#Return {
background: @theme_selected_bg_color; /* #1c71d8; */
border-color: @borders; /*#1a5fb4;*/

104
debian/changelog vendored
View File

@ -1,3 +1,107 @@
squeekboard (1.9.3) amber-phone; urgency=medium
[ Björn Tantau ]
* Show more useful keys at the same time.
* Add Ctrl and Alt modifier keys.
* Add missing Ê key.
* Make f-keys slightly wider.
* Add Menu key.
[ Guido Günther ]
* d/rules: Only remove Cargo.lock if it exists
* eek: Drop libcanberra usage
* debian: Build-depend on libfeedback
* eek-gtk-keyboard: Trigger event feedback on button press (Closes: #166)
[ Dorota Czaplejewicz ]
* build: Add missing gio-unix dependency
* build: Make compatible with Debian Bullseye
* debian: Add amber to legacy distro list
* ci: Add amber job
* debian: Require lsb-release
* size: Hardcode size to work around screen rotation
* ci: Re-add x64 Buster build
* italian: Fix space and period
[ Sebastian Krzyszkowiak ]
* Revert "Merge branch 'btantau-master-patch-76686' into 'master'"
* Terminal layout: another approach
[ Luís Fernando Stürmer da Rosa ]
* Brazilian Portuguese Keyboard Layout.
-- Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm> Wed, 05 Aug 2020 16:16:08 +0200
squeekboard (1.9.2) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* keyboard: Remove unused code
* gsettings: Don't crash when unavailable
* dbus: Don't crash if can't make a connection
* gsettings: Don't crash on switching when unavailable
* layout: Split out choice to a struct on its own
* renderer: Simplify by dropping gobjectness
* levelkeyboard: Rearrange to make future conversion easier
* layout: Minor generalizations
* Remove unused code
* sizing: Create a standalone UI shape manager
* sizing: Ignore scaling factor for layout selection
* CI: Fix typo
* Update rust deps for release
[ Andreas Rönnquist ]
* Swedish keyboard, wide button switching between numbers, symbols and base
* More fixes of button sizes
* Folder is doc, not docs
[ uzanto ]
* Add new file
* Replace duplicated show_symbols by show_eschars and removed "Delete" button that it's doing nothing
[ Arnaud Ferraris ]
* keyboards: fr: fix keyboard layout
* keyboards: fr: make sure the layout fits the screen
* resources: include French keyboard layout
* keyboards: fr: improve consistency with other layouts
* keyboards: fr: improve diacritics layout
* tests: add french layout
[ Vlad ]
* Fresh Russian layout
[ Jordi Masip ]
* Removed unused dependency 'libcroco'
[ Florian Klink ]
* sm.puri.Squeekboard.desktop: make path to Exec= absolute
[ Ole Guldberg ]
* Danish keyboard layout
* Danish keyboard layout
* add test for danish layout
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 01 Jun 2020 09:39:12 +0000
squeekboard (1.9.1) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* layout: Improve press handling
* settings: Handle empty settings
* Variant: Use proper pointer conversion between C and Rust
* meta: Add doap file
* modifiers: Support Control and Alt
* CI: Test that any bump to changelog has a corresponding tag
* docs: Add the guiding principle
* hacking: Move into docs/
[ &t ]
* Fix minor comment typos
[ Dorota Czaplejewicz ]
* cargo: Bump package versions before release
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Sun, 08 Mar 2020 10:04:29 +0000
squeekboard (1.9.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]

10
debian/check_release.py vendored Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python3
"""Checks tag before release.
Feed it the first changelog line, and then all available tags.
"""
import re, sys
tag = "v" + re.findall("\\((.*)\\)", input())[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!")

5
debian/control vendored
View File

@ -11,7 +11,7 @@ Build-Depends:
libglib2.0-dev,
libgnome-desktop-3-dev,
libgtk-3-dev,
libcroco3-dev,
libfeedback-dev,
librust-bitflags-1-dev (>= 1.0),
librust-clap-2+default-dev (>= 2.32),
librust-gio+v2-44-dev,
@ -25,6 +25,7 @@ Build-Depends:
librust-serde-yaml-0.8-dev (>= 0.8),
librust-xkbcommon-0.4+wayland-dev (>= 0.4),
libwayland-dev (>= 1.16),
lsb-release,
rustc,
wayland-protocols (>= 1.14),
Standards-Version: 4.1.3
@ -37,6 +38,8 @@ Depends:
gnome-themes-extra-data,
${shlibs:Depends},
${misc:Depends},
Breaks:
librem5-base (<< 24),
Description: On-screen keyboard for Wayland
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.

13
debian/rules vendored
View File

@ -3,13 +3,20 @@
export CARGO_HOME = $(CURDIR)/debian/cargo
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
distrel := $(shell lsb_release --codename --short)
ifneq (,$(filter $(distrel),buster amber))
legacy = true
else
legacy = false
endif
%:
dh $@ --builddirectory=_build --buildsystem=meson
# The Debian version of linked-hash-map doesn't provide any hash,
# causing Cargo to refuse to build with a crates.io copy
build-arch:
rm Cargo.lock
dh $@ --builddirectory=_build --buildsystem=meson
override_dh_auto_configure:
[ ! -f Cargo.lock ] || rm Cargo.lock
dh_auto_configure -- -Dlegacy=$(legacy)
override_dh_autoreconf:

View File

@ -3,15 +3,47 @@ Hacking
This document describes the standards for modifying and maintaining the *squeekboard* project.
Principles
----------
The project was built upon some guiding principles, which should be respected primarily by the maintainers, but also by contributors to avoid needlessly rejected changes.
The overarching principle of *squeekboard* is to empower users.
Software is primarily meant to solve problems of its users. Often in the quest to make software better, a hard distinction is made between the developer, who becomes the creator, and the user, who takes the role of the consumer, without direct influence on the software they use.
This project aims to give users the power to make the software work for them by blurring the lines between users and developers.
Notwithstanding its current state, *squeekboard* must be structured in a way that provides users a gradual way to gain more experience and power to adjust it. It must be easy, in order of importance:
- to use the software,
- to modify its resources,
- to change its behavior,
- to contribute upstream.
To give an idea of what it means in practice, those are some examples of what has been important for *squeekboard* so far:
- being quick and usable,
- allowing local overrides of resources and config,
- storing resources and config as editable, standard files,
- having complete, up to date documentation of interfaces,
- having an easy process of sending contributions,
- adapting to to user's settings and constrains without overriding them,
- avoiding compiling whenever possible,
- making it easy to build,
- having code that is [simple and obvious](https://www.python.org/dev/peps/pep-0020/),
- having an easy process of testing and accepting contributions.
You may notice that they are ordered roughly from "user-focused" to "maintainer-focused". While good properties are desired, sometimes they conflict, and maintainers should give additional weight to those benefiting the user compared to those benefiting regular contributors.
Sending patches
---------------
By submitting a change to this project, you agree to license it under the [GPL license version 3](./COPYING), or any later version. You also certify that your contribution fulfills the [Developer's Certificate of Origin 1.1](./dco.txt).
By submitting a change to this project, you agree to license it under the [GPL license version 3](https://source.puri.sm/Librem5/squeekboard/blob/master/COPYING), or any later version. You also certify that your contribution fulfills the [Developer's Certificate of Origin 1.1](https://source.puri.sm/Librem5/squeekboard/blob/master/dco.txt).
Development environment
-----------------------
*Squeekboard* is regularly built and tested on [the develpment environment](https://developer.puri.sm/Librem5/Development_Environment.html).
*Squeekboard* is regularly built and tested on [the development environment](https://developer.puri.sm/Librem5/Development_Environment.html).
Recent Fedora releases are likely to be tested as well.
@ -24,8 +56,7 @@ sudo apt-get -y install build-essential
sudo apt-get -y build-dep .
```
For an explicit list of dependencies check the `Build-Depends` entry in the
[`debian/control`](./debian/control) file.
For an explicit list of dependencies check the `Build-Depends` entry in the [`debian/control`](https://source.puri.sm/Librem5/squeekboard/blob/master/debian/control) file.
Testing
-------
@ -131,7 +162,7 @@ Maintenance
Squeekboard uses Rust & Cargo for some of its dependencies.
Use the `cargo.sh` script for maintaining the Cargo part of the build. The script takes the usual Cargo commands, after the first 2 positionsl arguments: source directory, and output artifact. So, `cargo test` becomes:
Use the `cargo.sh` script for maintaining the Cargo part of the build. The script takes the usual Cargo commands, after the first 2 positional arguments: source directory, and output artifact. So, `cargo test` becomes:
```
cd build_dir
@ -144,10 +175,11 @@ All Cargo dependencies must be selected in the version available in PureOS, and
Dependencies must be specified in `Cargo.toml` with 2 numbers: "major.minor". Since bugfix version number is meant to not affect the interface, this allows for safe updates.
`Cargo.lock` is used for remembering the revisions of all Rust dependencies. It should be updated often, preferably with each bugfix revision, and in a commit on its own:
`Cargo.lock` is used for remembering the revisions of all Rust dependencies. It must correspond to the default dependency configuration: without flags to use older or newer versions of dependencies. It should be updated often, preferably with each bugfix revision, and in a commit on its own:
```
cd build_dir
ninja build src/Cargo.toml
sh /source_path/cargo.sh update
ninja test
```

View File

@ -5,17 +5,23 @@ Contents
--------
* [Tutorial](tutorial.md)
* [Contributing](hacking.md)
Introduction
------------
Squeekboard is the on-screen keyboard for the Librem 5 phone. For more information, look at the [README](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md).
Squeekboard is the on-screen keyboard for the Librem 5 phone. For information about building, look at the [README](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md).
Layouts
-------
Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The [tutorial](/tutorial.md) explains the process in detail.
Squeekboard allows user-provided keyboard layouts. They can be created without recompiling the keyboard code. The [tutorial](tutorial.md) explains the process in detail.
Layouts are created using a text-based format, based on YAML.
TODO: Provide a description of the format.
Contributions
-------------
Anyone is free to modify *squeekboard*. See the [contributing document](hacking.md).

View File

@ -1,55 +1,92 @@
Kareema's guide to creating layouts
===================================
A guide to creating layouts
===========================
Its long overdue to write a comprehensive guide how to add a keyboard layout from start. But unfortunately, I dont have much time left ATM. A lot of information can be found in [this ](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5) thread.
This guide is based on the original Kareema's [forum post](https://forums.puri.sm/t/translations-and-virtual-touch-keyboards-tracking-localization/7669/48).
So at least I will try to start writing a short how-to here and edit this post as I find the time. Hope this helps a bit - comments and corrections welcome.
Its long overdue to write a comprehensive guide how to add a keyboard layout from start. But unfortunately, I dont have much time left ATM. A lot of information can be found in [this](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5) thread.
**Get one of the existing keyboard layouts**
So at least I will try to start writing a short how-to here and edit this post as I find the time. Hope this helps a bit - comments and corrections [welcome](https://source.puri.sm/Librem5/squeekboard/-/merge_requests/)
* You can get one of the keyboards from the squeekboard git repository : [https://source.puri.sm/Librem5/squeekboard ](https://source.puri.sm/Librem5/squeekboard)
* The keyboard layouts are located in the subdirectory `data/keyboard/` in the `.yaml` files
## Creating a new layout
Creating a layout is easy. You don't need to recompile things, just edit and test. It's easiest to start with an existing layout.
### Get one of the existing keyboard layouts
* You can get one of the keyboards from the squeekboard git repository : [https://source.puri.sm/Librem5/squeekboard](https://source.puri.sm/Librem5/squeekboard)
* The keyboard layouts are located in the subdirectory [`data/keyboards/`](https://source.puri.sm/Librem5/squeekboard/-/tree/master/data/keyboards) in the `.yaml` files
* Take a look and try to understand them :slight_smile:
**Fork your own copy of squeekboard**
### Creating the keyboard layout
* To be written: For the time being, take a look at [Using non-latin language on Librem 5](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5)
* The correct name of the .yaml file can be found with the command
```
gsettings get org.gnome.desktop.input-sources sources
```
The output should be something like this: `[('xkb', 'us'), ('xkb', 'de')]`
So for example “de.yaml” would be the correct name for the German keyboard layout.
If the name of your layout is not translated correctly in the list, you can fix it by adding it and recompiling Squeekboard.
### Testing the layout
Copy your yaml file to `~/.local/share/squeekboard/keyboards/` for testing purposes. From there it should get picked up by squeekboard automatically.
You can also use the `test_layout` tool from the -devel package to check it for errors:
```
# squeekboard_test_layout ./mylayout.yaml
Test result: OK
```
## Contributing your changes
If you want to share your layout with the world, the best way is to submit it to the Squeekboard project. The workflow is similar to any other Gitlab-based project.
Above all, your layout should be working, be tested, not break anything, and make sense.
### Fork your own copy of squeekboard
* Best way would be to start with a fork of the squeekboard repository: Create a user account at https://source.puri.sm/, go the the squeekboard git repository, press “Fork” in the web interface. You can find further instructions [here](https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html#creating-a-fork).
* Clone your fork locally with `git clone` and use the uri of your forked repo there
**Workflow to edit your keyboard and get it merged**
### Edit your keyboard and get it merged
* A generic guide how the workflow to contribute works, can be found at https://developer.puri.sm/Librem5/Contact/Contributing.html
* It may be useful to check out the [generic guide how the workflow to contribute works](https://developer.puri.sm/Librem5/Contact/Contributing.html)
* Create a branch: Name it “keyboard-layout-mylanguage” or whatever
* Checkout your branch, edit your keyboard layout and commit your changes
* Your layout **must** be correctly named, and in `data/keyboards/`.
* Your layout **must** pass the `test_layout` tool with zero problems.
* Your translation **must** be correctly named, and in `data/langs/`.
* Your layout or translation **must** be added to automatic tests. **Dont forget to add it** to `src/resources.rs` and the layout to `tests/meson.build` (thats for me, because I always forget it).
### Get it merged
It's always recommended to **compile and run** squeekboard before submitting your changes. This serves as a test that all is working. See instructions in the [compiling section](#compiling-and-running-squeekboard).
* Push the local changes (to the branch of your fork of squeekboard)
* Create a merge request for the branch to get your changes merged to the official squeekboard git repository
**Compile squeekboard**
If your changes pass automated tests (CI), then the merge request will be reviewed by the maintainers, and you might be asked to change a thing or two.
* Follow the instructions found in “Building” section of the squeekboards README: Running squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building ](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building)
## Compiling and running Squeekboard
**Running squeekboard**
If you want your change to become part of official Squeekboard, or if you want to add a translation of your layout name, you will have to recompile Squeekboard and test your changes there.
* Follow these instructions to run squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running ](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running)
* Additionally take a look at https://source.puri.sm/Librem5/squeekboard/blob/master/HACKING.md#testing
* You can either test it locally on your Linux system or use the [QEMU Librem 5 image ](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html)
### Compile squeekboard
* Follow the instructions found in “Building” section of the squeekboards README: Running squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#building)
### Run squeekboard
* Follow these instructions to run squeekboard: [https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running](https://source.puri.sm/Librem5/squeekboard/blob/master/README.md#running)
* Additionally take a look at the contribution document for [testing info](HACKING.md#testing)
* You can either test it locally on your Linux system or use the [QEMU Librem 5 image](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html)
* To test squeekboard locally, you need phoc. Either compile that from the sources as well or use the CI repository ci.puri.sm for Debian based systems:
`deb [arch=amd64] http://ci.puri.sm/ scratch librem5`
Squeekboard can be installed from there as a Debian package, too (thats what I often do). But beware - there be dragons! You could bork your system with these packages and you should probably disable this repository again after installing what you need - these packages are not meant for production systems (or so I heard :wink: )
**Creating the keyboard layout**
* To be written: For the time being, take a look at [Using non-latin language on Librem 5 ](https://forums.puri.sm/t/using-non-latin-language-on-librem-5/7103/5)
* The correct name of the .yaml file can be found with the command `gsettings get org.gnome.desktop.input-sources sources`
The output should be something like this: `[('xkb', 'us'), ('xkb', 'de')]`
So f.ex. “de.yaml” would be the correct name for the German keyboard layout.
* The translations for the keyboard layout names in the different languages can be found at `data/langs/`
* Dont forget to add your newly created layout or translation to `src/resources.rs` and the layout to `tests/meson.build` (thats for me, because I always forget it)
**Testing the layout**
* Copy your yaml file to `~/.local/share/squeekboard/keyboards/` for testing purposes. From there it should get picked up by squeekboard
* To test the translations in `data/langs/` , you have to compile squeekboard

View File

@ -38,8 +38,6 @@ struct _EekElementClass
GObjectClass parent_class;
};
GType eek_element_get_type (void) G_GNUC_CONST;
void eek_element_set_name (EekElement *element,
const gchar *name);

View File

@ -25,10 +25,6 @@
#include "config.h"
#ifdef HAVE_LIBCANBERRA
#include <canberra-gtk.h>
#endif
#include <math.h>
#include <string.h>
@ -41,25 +37,22 @@
#include "src/layout.h"
#include "src/submission.h"
enum {
PROP_0,
PROP_LAST
};
#define LIBFEEDBACK_USE_UNSTABLE_API
#include <libfeedback.h>
/* since 2.91.5 GDK_DRAWABLE was removed and gdk_cairo_create takes
GdkWindow as the argument */
#ifndef GDK_DRAWABLE
#define GDK_DRAWABLE(x) (x)
#endif
#define SQUEEKBOARD_APP_ID "sm.puri.squeekboard"
typedef struct _EekGtkKeyboardPrivate
{
EekRenderer *renderer;
EekRenderer *renderer; // owned, nullable
EekboardContextService *eekboard_context; // unowned reference
struct submission *submission; // unowned reference
LevelKeyboard *keyboard; // unowned reference; it's kept in server-context (FIXME)
struct squeek_layout_state *layout; // unowned
LevelKeyboard *keyboard; // unowned reference; it's kept in server-context
GdkEventSequence *sequence; // unowned reference
LfbEvent *event;
} EekGtkKeyboardPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA)
@ -88,31 +81,57 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
GtkAllocation allocation;
gtk_widget_get_allocation (self, &allocation);
if (!priv->keyboard) {
return FALSE;
}
if (!priv->renderer) {
PangoContext *pcontext = gtk_widget_get_pango_context (self);
priv->renderer = eek_renderer_new (priv->keyboard, pcontext);
priv->renderer = eek_renderer_new (
priv->keyboard,
pcontext);
eek_renderer_set_allocation_size (priv->renderer,
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, cr);
eek_renderer_render_keyboard (priv->renderer, priv->submission, cr, priv->keyboard);
return FALSE;
}
// Units of virtual pixels size
static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 540) {
return ARRANGEMENT_KIND_BASE;
}
return ARRANGEMENT_KIND_WIDE;
}
static void
eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
GtkAllocation *allocation)
{
EekGtkKeyboardPrivate *priv =
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
// check if the change would switch types
enum squeek_arrangement_kind new_type = get_type(
(uint32_t)(allocation->width - allocation->x),
(uint32_t)(allocation->height - allocation->y));
if (priv->layout->arrangement != new_type) {
priv->layout->arrangement = new_type;
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(priv->eekboard_context, priv->layout, time);
}
if (priv->renderer)
eek_renderer_set_allocation_size (priv->renderer,
priv->keyboard->layout,
allocation->width,
allocation->height);
@ -120,11 +139,26 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
size_allocate (self, allocation);
}
static void
on_event_triggered (LfbEvent *event,
GAsyncResult *res,
gpointer unused)
{
g_autoptr (GError) err = NULL;
if (!lfb_event_trigger_feedback_finish (event, res, &err)) {
g_warning ("Failed to trigger feedback for '%s': %s",
lfb_event_get_event (event), err->message);
}
}
static void depress(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (!priv->keyboard) {
return;
}
squeek_layout_depress(priv->keyboard->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, self);
@ -134,7 +168,10 @@ static void drag(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
squeek_layout_drag(priv->keyboard->layout,
if (!priv->keyboard) {
return;
}
squeek_layout_drag(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, self);
@ -143,8 +180,10 @@ static void drag(EekGtkKeyboard *self,
static void release(EekGtkKeyboard *self, guint32 time)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
squeek_layout_release(priv->keyboard->layout,
if (!priv->keyboard) {
return;
}
squeek_layout_release(eekboard_context_service_get_keyboard(priv->eekboard_context)->layout,
priv->submission,
eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, self);
@ -264,7 +303,7 @@ eek_gtk_keyboard_dispose (GObject *object)
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (priv->renderer) {
g_object_unref (priv->renderer);
eek_renderer_free(priv->renderer);
priv->renderer = NULL;
priv->renderer = NULL;
}
@ -277,6 +316,11 @@ eek_gtk_keyboard_dispose (GObject *object)
priv->keyboard = NULL;
}
if (priv->event) {
g_clear_object (&priv->event);
lfb_uninit ();
}
G_OBJECT_CLASS (eek_gtk_keyboard_parent_class)->dispose (object);
}
@ -308,29 +352,80 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
static void
eek_gtk_keyboard_init (EekGtkKeyboard *self)
{
(void)self;
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
g_autoptr(GError) err = NULL;
if (lfb_init(SQUEEKBOARD_APP_ID, &err))
priv->event = lfb_event_new ("button-pressed");
else
g_warning ("Failed to init libfeedback: %s", err->message);
GtkIconTheme *theme = gtk_icon_theme_get_default ();
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
}
static void
on_notify_keyboard (GObject *object,
GParamSpec *spec,
EekGtkKeyboard *self) {
(void)spec;
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (self);
priv->keyboard = eekboard_context_service_get_keyboard(EEKBOARD_CONTEXT_SERVICE(object));
if (priv->renderer) {
eek_renderer_free(priv->renderer);
}
priv->renderer = NULL;
gtk_widget_queue_draw(GTK_WIDGET(self));
}
/**
* eek_gtk_keyboard_new:
* @keyboard: an #EekKeyboard
*
* Create a new #GtkWidget displaying @keyboard.
* Returns: a #GtkWidget
*/
GtkWidget *
eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice,
struct submission *submission)
eek_gtk_keyboard_new (EekboardContextService *eekservice,
struct submission *submission,
struct squeek_layout_state *layout)
{
EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL));
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret);
priv->keyboard = keyboard;
priv->eekboard_context = eekservice;
priv->submission = submission;
priv->layout = layout;
priv->renderer = NULL;
g_signal_connect (eekservice,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ret);
on_notify_keyboard(G_OBJECT(eekservice), NULL, ret);
/* TODO: this is how a compound keyboard
* made out of a layout and a suggestion bar could start.
* GtkBox *box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
GtkEntry *fill = GTK_ENTRY(gtk_entry_new());
gtk_box_pack_start(box, GTK_WIDGET(fill), FALSE, FALSE, 0);
gtk_box_pack_start(box, GTK_WIDGET(ret), TRUE, TRUE, 0);
return GTK_WIDGET(box);*/
return GTK_WIDGET(ret);
}
EekRenderer *eek_gtk_keyboard_get_renderer(EekGtkKeyboard *self) {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
return priv->renderer;
/**
* eek_gtk_keyboard_emit_feedback:
*
* Emit button press haptic feedback via libfeedack.
*/
void
eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self)
{
EekGtkKeyboardPrivate *priv;
g_return_if_fail (EEK_IS_GTK_KEYBOARD (self));
priv = eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
if (priv->event) {
lfb_event_trigger_feedback_async (priv->event,
NULL,
(GAsyncReadyCallback)on_event_triggered,
NULL);
}
}

View File

@ -31,7 +31,7 @@
#include "eek/eek-types.h"
struct submission;
typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs
struct squeek_layout_state;
G_BEGIN_DECLS
#define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type())
@ -47,8 +47,8 @@ struct _EekGtkKeyboardClass
gpointer pdummy[24];
};
GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST;
GtkWidget *eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice, struct submission *submission);
GtkWidget *eek_gtk_keyboard_new (EekboardContextService *eekservice, struct submission *submission, struct squeek_layout_state *layout);
void eek_gtk_keyboard_emit_feedback (EekGtkKeyboard *self);
G_END_DECLS
#endif /* EEK_GTK_KEYBOARD_H */

View File

@ -21,41 +21,31 @@
#include "config.h"
#define _XOPEN_SOURCE 500
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h> // TODO: this is Linux-specific
#include <xkbcommon/xkbcommon.h>
#include "eek-keyboard.h"
void level_keyboard_free(LevelKeyboard *self) {
xkb_keymap_unref(self->keymap);
close(self->keymap_fd);
squeek_layout_free(self->layout);
g_free(self);
static void eek_key_map_deinit(struct keymap *self) {
if (self->fd < 0) {
g_error("Deinit called multiple times on KeyMap");
} else {
close(self->fd);
}
self->fd = -1;
}
LevelKeyboard*
level_keyboard_new (const gchar *keyboard_type,
enum squeek_arrangement_kind t)
{
struct squeek_layout *layout = squeek_load_layout(keyboard_type, t);
LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
keyboard->layout = layout;
static struct keymap eek_key_map_from_str(const char *keymap_str) {
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
g_error("No context created");
}
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
@ -63,14 +53,14 @@ level_keyboard_new (const gchar *keyboard_type,
g_error("Bad keymap:\n%s", keymap_str);
xkb_context_unref(context);
keyboard->keymap = keymap;
keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
keyboard->keymap_len = strlen(keymap_str) + 1;
char *xkb_keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
size_t keymap_len = strlen(xkb_keymap_str) + 1;
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
char *r = &path[strlen(path) - 6];
getrandom(r, 6, GRND_NONBLOCK);
if (getrandom(r, 6, GRND_NONBLOCK) < 0)
g_error("Failed to get random numbers: %s", strerror(errno));
for (unsigned i = 0; i < 6; i++) {
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
@ -79,17 +69,42 @@ level_keyboard_new (const gchar *keyboard_type,
if (keymap_fd < 0) {
g_error("Failed to set up keymap fd");
}
keyboard->keymap_fd = keymap_fd;
shm_unlink(path);
if (ftruncate(keymap_fd, (off_t)keyboard->keymap_len)) {
if (ftruncate(keymap_fd, (off_t)keymap_len)) {
g_error("Failed to increase keymap fd size");
}
char *ptr = mmap(NULL, keyboard->keymap_len, PROT_WRITE, MAP_SHARED,
char *ptr = mmap(NULL, keymap_len, PROT_WRITE, MAP_SHARED,
keymap_fd, 0);
if ((void*)ptr == (void*)-1) {
g_error("Failed to set up mmap");
}
strncpy(ptr, keymap_str, keyboard->keymap_len);
munmap(ptr, keyboard->keymap_len);
strncpy(ptr, xkb_keymap_str, keymap_len);
munmap(ptr, keymap_len);
free(xkb_keymap_str);
xkb_keymap_unref(keymap);
struct keymap km = {
.fd = keymap_fd,
.fd_len = keymap_len,
};
return km;
}
void level_keyboard_free(LevelKeyboard *self) {
eek_key_map_deinit(&self->keymap);
squeek_layout_free(self->layout);
g_free(self);
}
LevelKeyboard*
level_keyboard_new (struct squeek_layout *layout)
{
LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
keyboard->layout = layout;
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
keyboard->keymap = eek_key_map_from_str(keymap_str);
return keyboard;
}

View File

@ -1,17 +1,17 @@
/*
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
@ -32,23 +32,23 @@
G_BEGIN_DECLS
/// Keymap container for Rust interoperability.
struct keymap {
uint32_t fd; // keymap formatted as XKB string
size_t fd_len; // length of the data inside keymap_fd
};
/// Keyboard state holder
struct _LevelKeyboard {
struct squeek_layout *layout; // owned
struct xkb_keymap *keymap; // owned
int keymap_fd; // keymap formatted as XKB string
size_t keymap_len; // length of the data inside keymap_fd
guint id; // as a key to layout choices
struct keymap keymap; // owned
};
typedef struct _LevelKeyboard LevelKeyboard;
gchar * eek_keyboard_get_keymap
(LevelKeyboard *keyboard);
gchar *eek_keyboard_get_keymap(LevelKeyboard *keyboard);
LevelKeyboard*
level_keyboard_new (const gchar *keyboard_type,
enum squeek_arrangement_kind t);
level_keyboard_new (struct squeek_layout *layout);
void level_keyboard_free(LevelKeyboard *self);
G_END_DECLS

View File

@ -28,27 +28,6 @@
#include "eek-renderer.h"
#include "src/style.h"
enum {
PROP_0,
PROP_PCONTEXT,
PROP_LAST
};
typedef struct _EekRendererPrivate
{
LevelKeyboard *keyboard; // unowned
PangoContext *pcontext; // owned
GtkCssProvider *css_provider; // owned
GtkStyleContext *view_context; // owned
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
gdouble allocation_width;
gdouble allocation_height;
gint scale_factor; /* the outputs scale factor */
struct transformation widget_to_layout;
} EekRendererPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
/* eek-keyboard-drawing.c */
static void render_button_label (cairo_t *cr, GtkStyleContext *ctx,
@ -138,9 +117,7 @@ eek_render_button (EekRenderer *self,
gboolean pressed,
gboolean locked)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
GtkStyleContext *ctx = priv->button_context;
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;
@ -160,7 +137,7 @@ eek_render_button (EekRenderer *self,
}
gtk_style_context_add_class(ctx, outline_name);
render_button_in_context(priv->scale_factor, cr, ctx, button);
render_button_in_context(self->scale_factor, cr, ctx, button);
// Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
@ -214,118 +191,46 @@ render_button_label (cairo_t *cr,
g_object_unref (layout);
}
// FIXME: Pass just the active modifiers instead of entire submission
void
eek_renderer_render_keyboard (EekRenderer *self,
cairo_t *cr)
struct submission *submission,
cairo_t *cr,
LevelKeyboard *keyboard)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
g_return_if_fail (priv->keyboard);
g_return_if_fail (priv->allocation_width > 0.0);
g_return_if_fail (priv->allocation_height > 0.0);
g_return_if_fail (self->allocation_width > 0.0);
g_return_if_fail (self->allocation_height > 0.0);
/* Paint the background covering the entire widget area */
gtk_render_background (priv->view_context,
gtk_render_background (self->view_context,
cr,
0, 0,
priv->allocation_width, priv->allocation_height);
self->allocation_width, self->allocation_height);
cairo_save(cr);
cairo_translate (cr, priv->widget_to_layout.origin_x, priv->widget_to_layout.origin_y);
cairo_scale (cr, priv->widget_to_layout.scale, priv->widget_to_layout.scale);
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);
squeek_draw_layout_base_view(priv->keyboard->layout, self, cr);
squeek_layout_draw_all_changed(priv->keyboard->layout, self, cr);
squeek_draw_layout_base_view(keyboard->layout, self, cr);
squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);
cairo_restore (cr);
}
static void
eek_renderer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
void
eek_renderer_free (EekRenderer *self)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (
EEK_RENDERER(object));
switch (prop_id) {
case PROP_PCONTEXT:
priv->pcontext = g_value_get_object (value);
g_object_ref (priv->pcontext);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
if (self->pcontext) {
g_object_unref (self->pcontext);
self->pcontext = NULL;
}
}
static void
eek_renderer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
(void)value;
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
eek_renderer_dispose (GObject *object)
{
EekRenderer *self = EEK_RENDERER (object);
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
if (priv->keyboard) {
priv->keyboard = NULL;
}
if (priv->pcontext) {
g_object_unref (priv->pcontext);
priv->pcontext = NULL;
}
g_object_unref(self->css_provider);
g_object_unref(self->view_context);
g_object_unref(self->button_context);
// this is where renderer-specific surfaces would be released
G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object);
free(self);
}
static void
eek_renderer_finalize (GObject *object)
{
EekRenderer *self = EEK_RENDERER(object);
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
g_object_unref(priv->css_provider);
g_object_unref(priv->view_context);
g_object_unref(priv->button_context);
G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object);
}
static void
eek_renderer_class_init (EekRendererClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = eek_renderer_set_property;
gobject_class->get_property = eek_renderer_get_property;
gobject_class->dispose = eek_renderer_dispose;
gobject_class->finalize = eek_renderer_finalize;
pspec = g_param_spec_object ("pango-context",
"Pango Context",
"Pango Context",
PANGO_TYPE_CONTEXT,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_PCONTEXT,
pspec);
}
static GType new_type(char *name) {
GTypeInfo info = {0};
info.class_size = sizeof(GtkWidgetClass);
@ -336,7 +241,7 @@ static GType new_type(char *name) {
);
}
static GType view_type() {
static GType view_type(void) {
static GType type = 0;
if (!type) {
type = new_type("sq_view");
@ -344,7 +249,7 @@ static GType view_type() {
return type;
}
static GType button_type() {
static GType button_type(void) {
static GType type = 0;
if (!type) {
type = new_type("sq_button");
@ -353,81 +258,71 @@ static GType button_type() {
}
static void
eek_renderer_init (EekRenderer *self)
renderer_init (EekRenderer *self)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
self->pcontext = NULL;
self->allocation_width = 0.0;
self->allocation_height = 0.0;
self->scale_factor = 1;
priv->keyboard = NULL;
priv->pcontext = NULL;
priv->allocation_width = 0.0;
priv->allocation_height = 0.0;
priv->scale_factor = 1;
GtkIconTheme *theme = gtk_icon_theme_get_default ();
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
priv->css_provider = squeek_load_style();
self->css_provider = squeek_load_style();
}
EekRenderer *
eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext)
{
EekRenderer *renderer = g_object_new (EEK_TYPE_RENDERER,
"pango-context", pcontext,
NULL);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->keyboard = keyboard;
EekRenderer *renderer = calloc(1, sizeof(EekRenderer));
renderer_init(renderer);
renderer->pcontext = pcontext;
g_object_ref (renderer->pcontext);
/* Create a style context for the layout */
GtkWidgetPath *path = gtk_widget_path_new();
gtk_widget_path_append_type(path, view_type());
priv->view_context = gtk_style_context_new();
gtk_style_context_set_path(priv->view_context, path);
renderer->view_context = gtk_style_context_new();
gtk_style_context_set_path(renderer->view_context, path);
gtk_widget_path_unref(path);
if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_style_context_add_class(priv->view_context, "wide");
if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_style_context_add_class(renderer->view_context, "wide");
}
gtk_style_context_add_provider (priv->view_context,
GTK_STYLE_PROVIDER(priv->css_provider),
gtk_style_context_add_provider (renderer->view_context,
GTK_STYLE_PROVIDER(renderer->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
/* Create a style context for the buttons */
path = gtk_widget_path_new();
gtk_widget_path_append_type(path, view_type());
if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
if (squeek_layout_get_kind(keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_widget_path_iter_add_class(path, -1, "wide");
}
gtk_widget_path_append_type(path, button_type());
priv->button_context = gtk_style_context_new ();
gtk_style_context_set_path(priv->button_context, path);
renderer->button_context = gtk_style_context_new ();
gtk_style_context_set_path(renderer->button_context, path);
gtk_widget_path_unref(path);
gtk_style_context_set_parent(priv->button_context, priv->view_context);
gtk_style_context_set_state (priv->button_context, GTK_STATE_FLAG_NORMAL);
gtk_style_context_add_provider (priv->button_context,
GTK_STYLE_PROVIDER(priv->css_provider),
gtk_style_context_set_parent(renderer->button_context, renderer->view_context);
gtk_style_context_set_state (renderer->button_context, GTK_STATE_FLAG_NORMAL);
gtk_style_context_add_provider (renderer->button_context,
GTK_STYLE_PROVIDER(renderer->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
return renderer;
}
void
eek_renderer_set_allocation_size (EekRenderer *renderer,
struct squeek_layout *layout,
gdouble width,
gdouble height)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
g_return_if_fail (width > 0.0 && height > 0.0);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
renderer->allocation_width = width;
renderer->allocation_height = height;
priv->allocation_width = width;
priv->allocation_height = height;
priv->widget_to_layout = squeek_layout_calculate_transformation(
priv->keyboard->layout,
priv->allocation_width, priv->allocation_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
}
@ -435,10 +330,7 @@ eek_renderer_set_allocation_size (EekRenderer *renderer,
void
eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->scale_factor = scale;
renderer->scale_factor = scale;
}
cairo_surface_t *
@ -467,9 +359,5 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
struct transformation
eek_renderer_get_transformation (EekRenderer *renderer) {
struct transformation failed = {0};
g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
return priv->widget_to_layout;
return renderer->widget_to_layout;
}

View File

@ -25,31 +25,36 @@
#include <pango/pangocairo.h>
#include "eek-types.h"
#include "src/submission.h"
G_BEGIN_DECLS
struct squeek_layout;
#define EEK_TYPE_RENDERER (eek_renderer_get_type())
G_DECLARE_DERIVABLE_TYPE (EekRenderer, eek_renderer, EEK, RENDERER, GObject)
struct _EekRendererClass
/// Renders LevelKayboards
/// It cannot adjust styles at runtime.
typedef struct EekRenderer
{
GObjectClass parent_class;
PangoContext *pcontext; // owned
GtkCssProvider *css_provider; // owned
GtkStyleContext *view_context; // owned
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
/// Style class for rendering the view and button CSS.
gchar *extra_style; // owned
cairo_surface_t *(* get_icon_surface) (EekRenderer *self,
const gchar *icon_name,
gint size,
gint scale);
// Mutable state
/// Background extents
gdouble allocation_width;
gdouble allocation_height;
gint scale_factor; /* the outputs scale factor */
/// Coords transformation
struct transformation widget_to_layout;
} EekRenderer;
/*< private >*/
/* padding */
gpointer pdummy[23];
};
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,
(EekRenderer *renderer, struct squeek_layout *layout,
gdouble width,
gdouble height);
void eek_renderer_set_scale_factor (EekRenderer *renderer,
@ -59,8 +64,10 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name,
gint size,
gint scale);
void eek_renderer_render_keyboard (EekRenderer *renderer,
cairo_t *cr);
void eek_renderer_render_keyboard (EekRenderer *renderer, struct submission *submission,
cairo_t *cr, LevelKeyboard *keyboard);
void
eek_renderer_free (EekRenderer *self);
struct transformation
eek_renderer_get_transformation (EekRenderer *renderer);

View File

@ -42,18 +42,23 @@ enum {
static guint signals[LAST_SIGNAL] = { 0, };
#define EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServicePrivate))
/**
* EekboardContextService:
*
* Handles layout state, gsettings, and virtual-keyboard.
*
* TODO: Restrict to managing keyboard layouts, and maybe button repeats,
* and the virtual keyboard protocol.
*
* The #EekboardContextService structure contains only private data
* and should only be accessed using the provided API.
*/
struct _EekboardContextService {
GObject parent;
struct squeek_layout_state *layout; // Unowned
struct _EekboardContextServicePrivate {
LevelKeyboard *keyboard; // currently used keyboard
GHashTable *keyboard_hash; // a table of available keyboards, per layout
char *overlay;
GSettings *settings; // Owned reference
uint32_t hint;
uint32_t purpose;
// Maybe TODO: it's used only for fetching layout type.
// Maybe let UI push the type to this structure?
@ -62,7 +67,7 @@ struct _EekboardContextServicePrivate {
struct submission *submission; // unowned
};
G_DEFINE_TYPE_WITH_PRIVATE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT);
G_DEFINE_TYPE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT);
static void
eekboard_context_service_set_property (GObject *object,
@ -88,7 +93,7 @@ eekboard_context_service_get_property (GObject *object,
switch (prop_id) {
case PROP_KEYBOARD:
g_value_set_object (value, context->priv->keyboard);
g_value_set_object (value, context->keyboard);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -99,13 +104,6 @@ eekboard_context_service_get_property (GObject *object,
static void
eekboard_context_service_dispose (GObject *object)
{
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
if (context->priv->keyboard_hash) {
g_hash_table_destroy (context->priv->keyboard_hash);
context->priv->keyboard_hash = NULL;
}
G_OBJECT_CLASS (eekboard_context_service_parent_class)->
dispose (object);
}
@ -113,55 +111,58 @@ eekboard_context_service_dispose (GObject *object)
static void
settings_get_layout(GSettings *settings, char **type, char **layout)
{
if (!settings) {
return;
}
GVariant *inputs = g_settings_get_value(settings, "sources");
// current layout is always first
g_variant_get_child(inputs, 0, "(ss)", type, layout);
if (g_variant_n_children(inputs) == 0) {
g_warning("No system layout present");
*type = NULL;
*layout = NULL;
} else {
// current layout is always first
g_variant_get_child(inputs, 0, "(ss)", type, layout);
}
g_variant_unref(inputs);
}
void
eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t)
{
g_autofree gchar *keyboard_layout = NULL;
if (context->priv->overlay) {
keyboard_layout = g_strdup(context->priv->overlay);
} else {
g_autofree gchar *keyboard_type = NULL;
settings_get_layout(context->priv->settings,
&keyboard_type, &keyboard_layout);
}
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *state, uint32_t timestamp) {
gchar *layout_name = state->overlay_name;
if (!keyboard_layout) {
keyboard_layout = g_strdup("us");
}
if (layout_name == NULL) {
layout_name = state->layout_name;
EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
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:
;
}
switch (priv->purpose) {
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
g_free(keyboard_layout);
keyboard_layout = g_strdup("number");
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
g_free(keyboard_layout);
keyboard_layout = g_strdup("terminal");
break;
default:
;
if (layout_name == NULL) {
layout_name = "us";
}
}
// generic part follows
LevelKeyboard *keyboard = level_keyboard_new(keyboard_layout, t);
struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement);
LevelKeyboard *keyboard = level_keyboard_new(layout);
// set as current
LevelKeyboard *previous_keyboard = context->priv->keyboard;
context->priv->keyboard = keyboard;
LevelKeyboard *previous_keyboard = context->keyboard;
context->keyboard = keyboard;
// Update the keymap if necessary.
// TODO: Update submission on change event
if (context->priv->submission) {
submission_set_keyboard(context->priv->submission, keyboard);
if (context->submission) {
submission_set_keyboard(context->submission, keyboard, timestamp);
}
// Update UI
g_object_notify (G_OBJECT(context), "keyboard");
// replacing the keyboard above will cause the previous keyboard to get destroyed from the UI side (eek_gtk_keyboard_dispose)
@ -170,13 +171,23 @@ eekboard_context_service_update_layout(EekboardContextService *context, enum squ
}
}
static void update_layout_and_type(EekboardContextService *context) {
EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
enum squeek_arrangement_kind layout_kind = ARRANGEMENT_KIND_BASE;
if (priv->ui) {
layout_kind = server_context_service_get_layout_type(priv->ui);
static void eekboard_context_service_update_settings_layout(EekboardContextService *context) {
g_autofree gchar *keyboard_layout = NULL;
g_autofree gchar *keyboard_type = NULL;
settings_get_layout(context->settings,
&keyboard_type, &keyboard_layout);
if (g_strcmp0(context->layout->layout_name, keyboard_layout) != 0 || context->layout->overlay_name) {
g_free(context->layout->overlay_name);
context->layout->overlay_name = NULL;
if (keyboard_layout) {
g_free(context->layout->layout_name);
context->layout->layout_name = g_strdup(keyboard_layout);
}
// This must actually update the UI.
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
}
eekboard_context_service_update_layout(context, layout_kind);
}
static gboolean
@ -187,17 +198,14 @@ settings_handle_layout_changed(GSettings *s,
(void)keys;
(void)n_keys;
EekboardContextService *context = user_data;
g_free(context->priv->overlay);
context->priv->overlay = NULL;
update_layout_and_type(context);
eekboard_context_service_update_settings_layout(context);
return TRUE;
}
static void
eekboard_context_service_constructed (GObject *object)
{
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE (object);
update_layout_and_type(context);
(void)object;
}
static void
@ -220,7 +228,7 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
g_signal_new (I_("destroyed"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekboardContextServiceClass, destroyed),
0,
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
@ -244,24 +252,31 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
static void
eekboard_context_service_init (EekboardContextService *self)
{
self->priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(self);
const char *schema_name = "org.gnome.desktop.input-sources";
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
g_autoptr(GSettingsSchema) schema = NULL;
self->priv->keyboard_hash =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify)g_object_unref);
self->priv->settings = g_settings_new ("org.gnome.desktop.input-sources");
gulong conn_id = g_signal_connect(self->priv->settings, "change-event",
G_CALLBACK(settings_handle_layout_changed),
self);
if (conn_id == 0) {
g_warning ("Could not connect to gsettings updates, layout"
" changing unavailable");
if (!ssrc) {
g_warning("No gsettings schemas installed. Layout switching unavailable.");
return;
}
self->priv->overlay = NULL;
schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE);
if (schema) {
// Not referencing the found schema directly,
// because it's not clear how...
self->settings = g_settings_new (schema_name);
gulong conn_id = g_signal_connect(self->settings, "change-event",
G_CALLBACK(settings_handle_layout_changed),
self);
if (conn_id == 0) {
g_warning ("Could not connect to gsettings updates, "
"automatic layout changing unavailable");
}
} else {
g_warning("Gsettings schema %s is not installed on the system. "
"Layout switching unavailable", schema_name);
}
}
/**
@ -274,8 +289,6 @@ void
eekboard_context_service_destroy (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
g_free(context->priv->overlay);
g_signal_emit (context, signals[DESTROYED], 0);
}
@ -289,44 +302,53 @@ eekboard_context_service_destroy (EekboardContextService *context)
LevelKeyboard *
eekboard_context_service_get_keyboard (EekboardContextService *context)
{
return context->priv->keyboard;
return context->keyboard;
}
void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint, uint32_t purpose)
{
EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
if (priv->hint != hint || priv->purpose != purpose) {
priv->hint = hint;
priv->purpose = purpose;
update_layout_and_type(context);
if (context->layout->hint != hint || context->layout->purpose != purpose) {
context->layout->hint = hint;
context->layout->purpose = purpose;
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
}
}
void
eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) {
context->priv->overlay = g_strdup(name);
update_layout_and_type(context);
if (g_strcmp0(context->layout->overlay_name, name)) {
g_free(context->layout->overlay_name);
context->layout->overlay_name = g_strdup(name);
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
}
}
const char*
eekboard_context_service_get_overlay(EekboardContextService *context) {
return context->priv->overlay;
return context->layout->overlay_name;
}
EekboardContextService *eekboard_context_service_new(void)
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state)
{
return g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
EekboardContextService *context = g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
context->layout = state;
eekboard_context_service_update_settings_layout(context);
uint32_t time = gdk_event_get_time(NULL);
eekboard_context_service_use_layout(context, context->layout, time);
return context;
}
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
context->priv->submission = submission;
if (context->priv->submission) {
submission_set_keyboard(context->priv->submission, context->priv->keyboard);
context->submission = submission;
if (context->submission) {
uint32_t time = gdk_event_get_time(NULL);
submission_set_keyboard(context->submission, context->keyboard, time);
}
}
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
context->priv->ui = ui;
context->ui = ui;
}

View File

@ -34,58 +34,10 @@ G_BEGIN_DECLS
#define EEKBOARD_CONTEXT_SERVICE_INTERFACE "org.fedorahosted.Eekboard.Context"
#define EEKBOARD_TYPE_CONTEXT_SERVICE (eekboard_context_service_get_type())
#define EEKBOARD_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextService))
#define EEKBOARD_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
#define EEKBOARD_IS_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE))
#define EEKBOARD_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE))
#define EEKBOARD_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
G_DECLARE_FINAL_TYPE(EekboardContextService, eekboard_context_service, EEKBOARD, CONTEXT_SERVICE, GObject)
typedef struct _EekboardContextServiceClass EekboardContextServiceClass;
typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
/**
* EekboardContextService:
*
* Handles layout state, gsettings, and virtual-keyboard.
*
* TODO: Restrict to managing keyboard layouts, and maybe button repeats,
* and the virtual keyboard protocol.
*
* The #EekboardContextService structure contains only private data
* and should only be accessed using the provided API.
*/
struct _EekboardContextService {
GObject parent;
EekboardContextServicePrivate *priv;
};
/**
* EekboardContextServiceClass:
* @create_keyboard: virtual function for create a keyboard from string
* @enabled: class handler for #EekboardContextService::enabled signal
* @disabled: class handler for #EekboardContextService::disabled signal
*/
struct _EekboardContextServiceClass {
/*< private >*/
GObjectClass parent_class;
/*< public >*/
struct squeek_view *(*create_keyboard) (EekboardContextService *self,
const gchar *keyboard_type);
/* signals */
void (*destroyed) (EekboardContextService *self);
/*< private >*/
/* padding */
gpointer pdummy[24];
};
GType eekboard_context_service_get_type
(void) G_GNUC_CONST;
EekboardContextService *eekboard_context_service_new(void);
EekboardContextService *eekboard_context_service_new(struct squeek_layout_state *state);
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission);
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
void eekboard_context_service_destroy (EekboardContextService *context);
@ -98,6 +50,6 @@ void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint,
uint32_t purpose);
void
eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t);
eekboard_context_service_use_layout(EekboardContextService *context, struct squeek_layout_state *layout, uint32_t timestamp);
G_END_DECLS
#endif /* EEKBOARD_CONTEXT_SERVICE_H */

View File

@ -5,6 +5,7 @@ use std::env;
fn main() -> () {
check_builtin_layout(
env::args().nth(1).expect("No argument given").as_str()
env::args().nth(1).expect("No argument given").as_str(),
env::args().nth(2).map(|s| s == "allow_missing_return").unwrap_or(false),
);
}

View File

@ -1,7 +1,7 @@
project(
'squeekboard',
'c', 'rust',
version: '1.8.0',
version: '1.9.3',
license: 'GPLv3',
meson_version: '>=0.51.0',
default_options: [
@ -19,6 +19,15 @@ add_project_arguments(
'-Werror=missing-field-initializers',
'-Werror=incompatible-pointer-types',
'-Werror=int-conversion',
'-Werror=redundant-decls',
'-Wformat-nonliteral',
'-Wformat-security',
'-Winit-self',
'-Wmaybe-uninitialized',
'-Wold-style-definition',
'-Wredundant-decls',
'-Wstrict-prototypes',
'-Wunused-function',
],
language: 'c'
)
@ -42,6 +51,7 @@ endif
prefix = get_option('prefix')
bindir = join_paths(prefix, get_option('bindir'))
datadir = join_paths(prefix, get_option('datadir'))
desktopdir = join_paths(datadir, 'applications')
pkgdatadir = join_paths(datadir, meson.project_name())
if get_option('depdatadir') == ''
depdatadir = datadir
@ -60,6 +70,32 @@ summary = [
]
message('\n'.join(summary))
# Rust deps are changing, depending on compile flags. Cargo can't handle it alone.
cargo_toml_in = files('Cargo.toml.in')
path_data = configuration_data()
path_data.set('path', meson.source_root())
cargo_toml_base = configure_file(
input: 'Cargo.toml.in',
output: 'Cargo.toml.base',
configuration: path_data,
)
cargo_deps = files('Cargo.deps')
if get_option('legacy') == true
cargo_build_flags += ['--features', 'gtk_v0_5,gio_v0_5']
cargo_deps = files('Cargo.deps.legacy')
endif
cat = find_program('cat')
cargo_toml = custom_target(
'Cargo.toml',
output: 'Cargo.toml',
command: [cat, cargo_toml_base, cargo_deps],
capture: true,
)
dep_cargo = find_program('cargo')
cargo_script = find_program('cargo.sh')
cargo_build = find_program('cargo_build.sh')

View File

@ -6,3 +6,7 @@ option('depdatadir',
option('tests',
type: 'boolean', value: true,
description: 'Whether to compile unit tests')
option('legacy',
type: 'boolean', value: false,
description: 'Build with Deban Buster versions of dependencies')

17
squeekboard.doap Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:admin="http://webns.net/mvcb/">
<name>squeekboard</name>
<shortdesc>A Wayland virtual keyboard</shortdesc>
<description>A virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.</description>
<homepage rdf:resource="https://source.puri.sm/Librem5/squeekboard" />
<bug-database rdf:resource="https://source.puri.sm/Librem5/squeekboard/issues" />
<os>Linux</os>
<license rdf:resource="http://usefulinc.com/doap/licenses/gpl" />
<maintainer>
<foaf:Person>
<foaf:name>Dorota Czaplejewicz</foaf:name>
<foaf:mbox rdf:resource="mailto:dorota.czaplejewicz@puri.sm" />
</foaf:Person>
</maintainer>
</Project>

View File

@ -10,8 +10,11 @@ pub struct KeySym(pub String);
type View = String;
/// Use to send modified keypresses
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, 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,
}
@ -27,8 +30,8 @@ pub enum Action {
/// When unlocked by pressing it or emitting a key
unlock: View,
},
/// Set this modifier TODO: release?
SetModifier(Modifier),
/// Hold this modifier for as long as the button is pressed
ApplyModifier(Modifier),
/// Submit some text
Submit {
/// Text to submit with input-method.

View File

@ -240,14 +240,20 @@ type ButtonIds = String;
#[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
struct ButtonMeta {
/// Special action to perform on activation. Conflicts with keysym, text.
// TODO: structure (action, keysym, text, modifier) as an enum
// to detect conflicts and missing values at compile time
/// Special action to perform on activation.
/// Conflicts with keysym, text, modifier.
action: Option<Action>,
/// The name of the XKB keysym to emit on activation.
/// Conflicts with action, text
/// Conflicts with action, text, modifier.
keysym: Option<String>,
/// The text to submit on activation. Will be derived from ID if not present
/// Conflicts with action, keysym
/// Conflicts with action, keysym, modifier.
text: Option<String>,
/// The modifier to apply while the key is locked
/// Conflicts with action, keysym, text
modifier: Option<Modifier>,
/// If not present, will be derived from text or the button ID
label: Option<String>,
/// Conflicts with label
@ -270,6 +276,20 @@ enum Action {
Erase,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
enum Modifier {
Control,
Shift,
Lock,
#[serde(alias="Mod1")]
Alt,
Mod2,
Mod3,
Mod4,
Mod5,
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
struct Outline {
@ -362,56 +382,43 @@ impl Layout {
)
)}).collect();
let keymap: HashMap<String, u32> = generate_keycodes(
button_actions.iter()
.filter_map(|(_name, action)| {
match action {
::action::Action::Submit {
text: _, keys,
} => Some(keys),
_ => None,
}
})
.flatten()
.map(|named_keysym| named_keysym.0.as_str())
let symbolmap: HashMap<String, u32> = generate_keycodes(
extract_symbol_names(&button_actions)
);
let button_states = button_actions.into_iter().map(|(name, action)| {
let keycodes = match &action {
::action::Action::Submit { text: _, keys } => {
keys.iter().map(|named_keycode| {
*keymap.get(named_keycode.0.as_str())
.expect(
format!(
"keycode {} in key {} missing from keymap",
named_keycode.0,
name
).as_str()
)
}).collect()
},
action::Action::Erase => vec![
*keymap.get("BackSpace")
.expect(&format!("BackSpace missing from keymap")),
],
_ => Vec::new(),
};
(
name.into(),
KeyState {
pressed: PressType::Released,
keycodes,
action,
}
)
});
let button_states = HashMap::<String, KeyState>::from_iter(
button_states
button_actions.into_iter().map(|(name, action)| {
let keycodes = match &action {
::action::Action::Submit { text: _, keys } => {
keys.iter().map(|named_keysym| {
*symbolmap.get(named_keysym.0.as_str())
.expect(
format!(
"keysym {} in key {} missing from symbol map",
named_keysym.0,
name
).as_str()
)
}).collect()
},
action::Action::Erase => vec![
*symbolmap.get("BackSpace")
.expect(&format!("BackSpace missing from symbol map")),
],
_ => Vec::new(),
};
(
name.into(),
KeyState {
pressed: PressType::Released,
keycodes,
action,
}
)
})
);
// TODO: generate from symbols
let keymap_str = match generate_keymap(&button_states) {
let keymap_str = match generate_keymap(symbolmap) {
Err(e) => { return (Err(e), warning_handler) },
Ok(v) => v,
};
@ -439,14 +446,14 @@ impl Layout {
&mut warning_handler,
))
});
layout::Row {
buttons: add_offsets(
layout::Row::new(
add_offsets(
buttons,
|button| button.size.width,
).collect()
}
)
});
let rows = add_offsets(rows, |row| row.get_height())
let rows = add_offsets(rows, |row| row.get_size().height)
.collect();
(
name.clone(),
@ -464,8 +471,8 @@ impl Layout {
name,
(
layout::c::Point {
x: (total_size.width - view.get_width()) / 2.0,
y: (total_size.height - view.get_height()) / 2.0,
x: (total_size.width - view.get_size().width) / 2.0,
y: (total_size.height - view.get_size().height) / 2.0,
},
view,
),
@ -510,22 +517,27 @@ fn create_action<H: logging::Handler>(
Action(Action),
Text(String),
Keysym(String),
Modifier(Modifier),
};
let submission = match (
&symbol_meta.action,
&symbol_meta.keysym,
&symbol_meta.text
&symbol_meta.text,
&symbol_meta.modifier,
) {
(Some(action), None, None) => SubmitData::Action(action.clone()),
(None, Some(keysym), None) => SubmitData::Keysym(keysym.clone()),
(None, None, Some(text)) => SubmitData::Text(text.clone()),
(None, None, None) => SubmitData::Text(name.into()),
(Some(action), None, None, None) => SubmitData::Action(action.clone()),
(None, Some(keysym), None, None) => SubmitData::Keysym(keysym.clone()),
(None, None, Some(text), None) => SubmitData::Text(text.clone()),
(None, None, None, Some(modifier)) => {
SubmitData::Modifier(modifier.clone())
},
(None, None, None, None) => SubmitData::Text(name.into()),
_ => {
warning_handler.handle(
logging::Level::Warning,
&format!(
"Button {} has more than one of (action, keysym, text)",
"Button {} has more than one of (action, keysym, text, modifier)",
name,
),
);
@ -614,6 +626,26 @@ fn create_action<H: logging::Handler>(
})
}).collect(),
},
SubmitData::Modifier(modifier) => match modifier {
Modifier::Control => action::Action::ApplyModifier(
action::Modifier::Control,
),
Modifier::Alt => action::Action::ApplyModifier(
action::Modifier::Alt,
),
unsupported_modifier => {
warning_handler.handle(
logging::Level::Bug,
&format!(
"Modifier {:?} unsupported", unsupported_modifier,
),
);
action::Action::Submit {
text: None,
keys: Vec::new(),
}
},
},
}
}
@ -689,17 +721,41 @@ fn create_button<H: logging::Handler>(
}
}
fn extract_symbol_names<'a>(actions: &'a [(&str, action::Action)])
-> impl Iterator<Item=&'a str>
{
actions.iter()
.filter_map(|(_name, act)| {
match act {
action::Action::Submit {
text: _, keys,
} => Some(keys),
_ => None,
}
})
.flatten()
.map(|named_keysym| named_keysym.0.as_str())
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error as ErrorTrait;
use ::logging::ProblemPanic;
const THIS_FILE: &str = file!();
fn path_from_root(file: &'static str) -> PathBuf {
PathBuf::from(THIS_FILE)
.parent().unwrap()
.parent().unwrap()
.join(file)
}
#[test]
fn test_parse_path() {
assert_eq!(
Layout::from_file(PathBuf::from("tests/layout.yaml")).unwrap(),
Layout::from_file(path_from_root("tests/layout.yaml")).unwrap(),
Layout {
margins: Margins { top: 0f64, bottom: 0f64, side: 0f64 },
views: hashmap!(
@ -711,6 +767,7 @@ mod tests {
keysym: None,
action: None,
text: None,
modifier: None,
label: Some("test".into()),
outline: None,
}
@ -725,13 +782,14 @@ mod tests {
/// Check if the default protection works
#[test]
fn test_empty_views() {
let out = Layout::from_file(PathBuf::from("tests/layout2.yaml"));
let out = Layout::from_file(path_from_root("tests/layout2.yaml"));
match out {
Ok(_) => assert!(false, "Data mistakenly accepted"),
Err(e) => {
let mut handled = false;
if let Error::Yaml(ye) = &e {
handled = ye.description() == "missing field `views`";
handled = ye.to_string()
.starts_with("missing field `views`");
};
if !handled {
println!("Unexpected error {:?}", e);
@ -743,13 +801,13 @@ mod tests {
#[test]
fn test_extra_field() {
let out = Layout::from_file(PathBuf::from("tests/layout3.yaml"));
let out = Layout::from_file(path_from_root("tests/layout3.yaml"));
match out {
Ok(_) => assert!(false, "Data mistakenly accepted"),
Err(e) => {
let mut handled = false;
if let Error::Yaml(ye) = &e {
handled = ye.description()
handled = ye.to_string()
.starts_with("unknown field `bad_field`");
};
if !handled {
@ -762,14 +820,14 @@ mod tests {
#[test]
fn test_layout_punctuation() {
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
let out = Layout::from_file(path_from_root("tests/layout_key1.yaml"))
.unwrap()
.build(ProblemPanic).0
.unwrap();
assert_eq!(
out.views["base"].1
.get_rows()[0].1
.buttons[0].1
.get_buttons()[0].1
.label,
::layout::Label::Text(CString::new("test").unwrap())
);
@ -777,14 +835,14 @@ mod tests {
#[test]
fn test_layout_unicode() {
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
let out = Layout::from_file(path_from_root("tests/layout_key2.yaml"))
.unwrap()
.build(ProblemPanic).0
.unwrap();
assert_eq!(
out.views["base"].1
.get_rows()[0].1
.buttons[0].1
.get_buttons()[0].1
.label,
::layout::Label::Text(CString::new("test").unwrap())
);
@ -793,7 +851,24 @@ mod tests {
/// Test multiple codepoints
#[test]
fn test_layout_unicode_multi() {
let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
let out = Layout::from_file(path_from_root("tests/layout_key3.yaml"))
.unwrap()
.build(ProblemPanic).0
.unwrap();
assert_eq!(
out.views["base"].1
.get_rows()[0].1
.get_buttons()[0].1
.state.borrow()
.keycodes.len(),
2
);
}
/// Test if erase yields a useable keycode
#[test]
fn test_layout_erase() {
let out = Layout::from_file(path_from_root("tests/layout_erase.yaml"))
.unwrap()
.build(ProblemPanic).0
.unwrap();
@ -803,7 +878,7 @@ mod tests {
.buttons[0].1
.state.borrow()
.keycodes.len(),
2
1
);
}
@ -852,6 +927,7 @@ mod tests {
keysym: None,
text: None,
action: None,
modifier: None,
label: Some("test".into()),
outline: None,
}
@ -869,7 +945,7 @@ mod tests {
#[test]
fn test_layout_margins() {
let out = Layout::from_file(PathBuf::from("tests/layout_margins.yaml"))
let out = Layout::from_file(path_from_root("tests/layout_margins.yaml"))
.unwrap()
.build(ProblemPanic).0
.unwrap();
@ -883,4 +959,35 @@ mod tests {
}
);
}
#[test]
fn test_extract_symbols() {
let actions = [(
"ac",
action::Action::Submit {
text: None,
keys: vec![
action::KeySym("a".into()),
action::KeySym("c".into()),
],
},
)];
assert_eq!(
extract_symbol_names(&actions[..]).collect::<Vec<_>>(),
vec!["a", "c"],
);
}
#[test]
fn test_extract_symbols_erase() {
let actions = [(
"Erase",
action::Action::Erase,
)];
assert_eq!(
extract_symbol_names(&actions[..]).collect::<Vec<_>>(),
Vec::<&str>::new(), //"BackSpace"], // TODO: centralize handling of BackSpace
);
}
}

View File

@ -3,9 +3,11 @@
use cairo;
use std::cell::RefCell;
use ::action::Action;
use ::keyboard;
use ::layout::{ Button, Layout };
use ::layout::c::{ EekGtkKeyboard, Point };
use ::submission::Submission;
use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt;
@ -44,13 +46,21 @@ mod c {
layout: *mut Layout,
renderer: EekRenderer,
cr: *mut cairo_sys::cairo_t,
submission: *const Submission,
) {
let layout = unsafe { &mut *layout };
let submission = unsafe { &*submission };
let cr = unsafe { cairo::Context::from_raw_none(cr) };
let active_modifiers = submission.get_active_modifiers();
layout.foreach_visible_button(|offset, button| {
let state = RefCell::borrow(&button.state).clone();
let locked = state.action.is_active(&layout.current_view);
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 {
render_button_at_position(
renderer, &cr,

View File

@ -54,7 +54,7 @@ pub mod c {
im: *const InputMethod)
{
let imservice = check_imservice(imservice, im).unwrap();
imservice.preedit_string = String::new();
imservice.preedit_string = String::new(); // I don't think this is ever used. Remove it?
imservice.pending = IMProtocolState {
active: true,
..IMProtocolState::default()
@ -84,7 +84,7 @@ pub mod c {
surrounding_text: into_cstring(text)
.expect("Received invalid string")
.expect("Received null string"),
surrounding_cursor: cursor,
cursor,
..imservice.pending.clone()
};
}
@ -146,10 +146,6 @@ pub mod c {
let active_changed = imservice.current.active ^ imservice.pending.active;
imservice.current = imservice.pending.clone();
imservice.pending = IMProtocolState {
active: imservice.current.active,
..IMProtocolState::default()
};
if active_changed {
if imservice.current.active {
@ -314,7 +310,7 @@ impl TryFrom<u32> for ChangeCause {
#[derive(Clone)]
struct IMProtocolState {
surrounding_text: CString,
surrounding_cursor: u32,
cursor: u32,
content_purpose: ContentPurpose,
content_hint: ContentHint,
text_change_cause: ChangeCause,
@ -325,7 +321,7 @@ impl Default for IMProtocolState {
fn default() -> IMProtocolState {
IMProtocolState {
surrounding_text: CString::default(),
surrounding_cursor: 0, // TODO: mark that there's no cursor
cursor: 0, // TODO: mark that there's no cursor
content_hint: ContentHint::NONE,
content_purpose: ContentPurpose::Normal,
text_change_cause: ChangeCause::InputMethod,
@ -344,7 +340,7 @@ pub struct IMService {
pending: IMProtocolState,
current: IMProtocolState, // turn current into an idiomatic representation?
preedit_string: String,
preedit_string: String, // I don't think this is ever used. Remove it?
serial: Wrapping<u32>,
}
@ -366,7 +362,7 @@ impl IMService {
state_manager,
pending: IMProtocolState::default(),
current: IMProtocolState::default(),
preedit_string: String::new(),
preedit_string: String::new(), // I don't think this is ever used. Remove it?
serial: Wrapping(0u32),
});
unsafe {
@ -378,9 +374,14 @@ impl IMService {
imservice
}
pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
pub fn commit_string(&mut self, text: &CString) -> Result<(), SubmitError> {
match self.current.active {
true => {
let cursor_position = self.pending.cursor.try_into().unwrap(); // Converts u32 of cursor to usize
self.pending
.surrounding_text
.insert_str(cursor_position, text);
self.pending.cursor += text.len() as u32;
unsafe {
c::eek_input_method_commit_string(self.im, text.as_ptr())
}
@ -391,11 +392,16 @@ impl IMService {
}
pub fn delete_surrounding_text(
&self,
&mut self,
before: u32, after: u32,
) -> Result<(), SubmitError> {
match self.current.active {
true => {
let cursor_position: usize = self.pending.cursor.try_into().unwrap(); // Converts u32 of cursor to usize
self.pending.surrounding_text.replace_range(
cursor_position - (before as usize)..cursor_position + (after as usize),
"",
);
unsafe {
c::eek_input_method_delete_surrounding_text(
self.im,
@ -415,6 +421,7 @@ impl IMService {
c::eek_input_method_commit(self.im, self.serial.0)
}
self.serial += Wrapping(1u32);
imservice_handle_done(self);
Ok(())
},
false => Err(SubmitError::NotActive),

View File

@ -9,7 +9,6 @@ use std::rc::Rc;
use std::string::FromUtf8Error;
use ::action::Action;
use ::logging;
// Traits
use std::io::Write;
@ -23,15 +22,33 @@ pub enum PressType {
pub type KeyCode = u32;
bitflags!{
/// Map to `virtual_keyboard.modifiers` modifiers values
/// From https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Keyboard_State
pub struct Modifiers: u8 {
const SHIFT = 0x1;
const LOCK = 0x2;
const CONTROL = 0x4;
/// Alt
const MOD1 = 0x8;
const MOD2 = 0x10;
const MOD3 = 0x20;
/// Meta
const MOD4 = 0x40;
/// AltGr
const MOD5 = 0x80;
}
}
/// When the submitted actions of keys need to be tracked,
/// they need a stable, comparable ID
#[derive(PartialEq)]
#[derive(Clone, PartialEq)]
pub struct KeyStateId(*const KeyState);
#[derive(Debug, Clone)]
pub struct KeyState {
pub pressed: PressType,
/// A cache of raw keycodes derived from Action::Sumbit given a keymap
/// A cache of raw keycodes derived from Action::Submit given a keymap
pub keycodes: Vec<KeyCode>,
/// Static description of what the key does when pressed or released
pub action: Action,
@ -46,6 +63,14 @@ impl KeyState {
}
}
#[must_use]
pub fn into_pressed(self) -> KeyState {
KeyState {
pressed: PressType::Pressed,
..self
}
}
/// KeyStates instances are the unique identifiers of pressed keys,
/// and the actions submitted with them.
pub fn get_id(keystate: &Rc<RefCell<KeyState>>) -> KeyStateId {
@ -66,7 +91,7 @@ fn sorted<'a, I: Iterator<Item=&'a str>>(
/// HACK: starting from 9, because 8 results in keycode 0,
/// which the compositor likes to discard
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C
key_names: C,
) -> HashMap<String, u32> {
let special_keysyms = ["BackSpace", "Return"].iter().map(|&s| s);
HashMap::from_iter(
@ -99,11 +124,9 @@ impl From<io::Error> for FormattingError {
}
/// Generates a de-facto single level keymap.
// TODO: don't rely on keys and their order,
// but rather on what keysyms and keycodes are in use.
// Iterating actions makes it hard to deduplicate keysyms.
/// Key codes must not repeat and should remain between 9 and 255.
pub fn generate_keymap(
keystates: &HashMap::<String, KeyState>
symbolmap: HashMap::<String, KeyCode>,
) -> Result<String, FormattingError> {
let mut buf: Vec<u8> = Vec::new();
writeln!(
@ -112,88 +135,75 @@ pub fn generate_keymap(
xkb_keycodes \"squeekboard\" {{
minimum = 8;
maximum = 255;"
maximum = 999;"
)?;
for (name, state) in keystates.iter() {
match &state.action {
Action::Submit { text: _, keys } => {
if let 0 = keys.len() {
log_print!(
logging::Level::Warning,
"Key {} has no keysyms", name,
);
};
for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
write!(
buf,
"
<{}> = {};",
named_keysym.0,
keycode,
)?;
}
},
Action::Erase => {
let mut keycodes = state.keycodes.iter();
write!(
buf,
"
<BackSpace> = {};",
keycodes.next().expect("Erase key has no keycode"),
)?;
if let Some(_) = keycodes.next() {
log_print!(
logging::Level::Bug,
"Erase key has multiple keycodes",
);
}
},
_ => {},
}
// Xorg can only consume up to 255 keys, so this may not work in Xwayland.
// Two possible solutions:
// - use levels to cram multiple characters into one key
// - swap layouts on key presses
for keycode in symbolmap.values() {
write!(
buf,
"
<I{}> = {0};",
keycode,
)?;
}
writeln!(
buf,
"
indicator 1 = \"Caps Lock\"; // Xwayland won't accept without it.
}};
xkb_symbols \"squeekboard\" {{
name[Group1] = \"Letters\";
name[Group2] = \"Numbers/Symbols\";
key <BackSpace> {{ [ BackSpace ] }};"
"
)?;
for (name, state) in keystates.iter() {
if let Action::Submit { text: _, keys } = &state.action {
for keysym in keys.iter() {
write!(
buf,
"
key <{}> {{ [ {0} ] }};",
keysym.0,
)?;
}
}
for (name, keycode) in symbolmap.iter() {
write!(
buf,
"
key <I{}> {{ [ {} ] }};",
keycode,
name,
)?;
}
writeln!(
buf,
"
}};
xkb_types \"squeekboard\" {{
virtual_modifiers Squeekboard; // No modifiers! Needed for Xorg for some reason.
// Those names are needed for Xwayland.
type \"ONE_LEVEL\" {{
modifiers= none;
level_name[Level1]= \"Any\";
}};
type \"TWO_LEVEL\" {{
level_name[Level1]= \"Base\";
}};
type \"ALPHABETIC\" {{
level_name[Level1]= \"Base\";
}};
type \"KEYPAD\" {{
level_name[Level1]= \"Base\";
}};
type \"SHIFT+ALT\" {{
level_name[Level1]= \"Base\";
}};
type \"TWO_LEVEL\" {{
modifiers = Shift;
map[Shift] = Level2;
level_name[Level1] = \"Base\";
level_name[Level2] = \"Shift\";
}};
}};
xkb_compatibility \"squeekboard\" {{
// Needed for Xwayland again.
interpret Any+AnyOf(all) {{
action= SetMods(modifiers=modMapMods,clearLocks);
}};
}};
}};"
)?;
@ -208,21 +218,13 @@ mod tests {
use xkbcommon::xkb;
use ::action::KeySym;
#[test]
fn test_keymap_multi() {
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let keymap_str = generate_keymap(&hashmap!{
"ac".into() => KeyState {
action: Action::Submit {
text: None,
keys: vec!(KeySym("a".into()), KeySym("c".into())),
},
keycodes: vec!(9, 10),
pressed: PressType::Released,
},
let keymap_str = generate_keymap(hashmap!{
"a".into() => 9,
"c".into() => 10,
}).unwrap();
let keymap = xkb::Keymap::new_from_string(

View File

@ -7,13 +7,23 @@
#include "eek/eek-gtk-keyboard.h"
#include "eek/eek-renderer.h"
#include "eek/eek-types.h"
#include "src/submission.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h"
enum squeek_arrangement_kind {
ARRANGEMENT_KIND_BASE = 0,
ARRANGEMENT_KIND_WIDE = 1,
};
struct squeek_layout_state {
enum squeek_arrangement_kind arrangement;
enum zwp_text_input_v3_content_purpose purpose;
enum zwp_text_input_v3_content_hint hint;
char *layout_name;
char *overlay_name;
};
struct squeek_layout;
EekBounds squeek_button_get_bounds(const struct squeek_button*);
@ -53,6 +63,6 @@ void squeek_layout_drag(struct squeek_layout *layout,
struct transformation widget_to_layout,
uint32_t timestamp, EekboardContextService *manager,
EekGtkKeyboard *ui_keyboard);
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission);
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
#endif

View File

@ -26,10 +26,10 @@ use std::vec::Vec;
use ::action::Action;
use ::drawing;
use ::keyboard::{ KeyState, PressType };
use ::keyboard::KeyState;
use ::logging;
use ::manager;
use ::submission::{ Submission, Timestamp };
use ::submission::{ Submission, SubmitData, Timestamp };
use ::util::find_max_double;
// Traits
@ -52,6 +52,14 @@ 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(
keyboard: EekGtkKeyboard,
);
}
/// Defined in eek-types.h
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
@ -99,8 +107,8 @@ pub mod c {
impl Bounds {
pub fn contains(&self, point: &Point) -> bool {
(point.x > self.x && point.x < self.x + self.width
&& point.y > self.y && point.y < self.y + self.height)
point.x > self.x && point.x < self.x + self.width
&& point.y > self.y && point.y < self.y + self.height
}
}
@ -249,7 +257,7 @@ pub mod c {
unsafe { Box::from_raw(layout) };
}
/// Entry points for more complex procedures and algoithms which span multiple modules
/// Entry points for more complex procedures and algorithms which span multiple modules
pub mod procedures {
use super::*;
@ -341,6 +349,9 @@ pub mod c {
);
// maybe TODO: draw on the display buffer here
drawing::queue_redraw(ui_keyboard);
unsafe {
eek_gtk_keyboard_emit_feedback(ui_keyboard);
}
};
}
@ -404,6 +415,9 @@ pub mod c {
&state,
);
// maybe TODO: draw on the display buffer here
unsafe {
eek_gtk_keyboard_emit_feedback(ui_keyboard);
}
}
} else {
for wrapped_key in pressed {
@ -479,38 +493,63 @@ pub struct Button {
}
/// The graphical representation of a row of buttons
#[derive(Clone, Debug)]
pub struct Row {
/// Buttons together with their offset from the left
pub buttons: Vec<(f64, Box<Button>)>,
/// Buttons together with their offset from the left relative to the row.
/// ie. the first button always start at 0.
buttons: Vec<(f64, Box<Button>)>,
/// Total size of the row
size: Size,
}
impl Row {
pub fn get_height(&self) -> f64 {
find_max_double(
self.buttons.iter(),
impl Row {
pub fn new(buttons: Vec<(f64, Box<Button>)>) -> Row {
// Make sure buttons are sorted by offset.
debug_assert!({
let mut sorted = buttons.clone();
sorted.sort_by(|(f1, _), (f2, _)| f1.partial_cmp(f2).unwrap());
sorted.iter().map(|(f, _)| *f).collect::<Vec<_>>()
== buttons.iter().map(|(f, _)| *f).collect::<Vec<_>>()
});
let width = buttons.iter().next_back()
.map(|(x_offset, button)| button.size.width + x_offset)
.unwrap_or(0.0);
let height = find_max_double(
buttons.iter(),
|(_offset, button)| button.size.height,
)
);
Row { buttons, size: Size { width, height } }
}
fn get_width(&self) -> f64 {
self.buttons.iter().next_back()
.map(|(x_offset, button)| button.size.width + x_offset)
.unwrap_or(0.0)
pub fn get_size(&self) -> Size {
self.size.clone()
}
pub fn get_buttons(&self) -> &Vec<(f64, Box<Button>)> {
&self.buttons
}
/// Finds the first button that covers the specified point
/// relative to row's position's origin
fn find_button_by_position(&self, point: c::Point)
-> Option<(f64, &Box<Button>)>
fn find_button_by_position(&self, x: f64) -> &(f64, Box<Button>)
{
self.buttons.iter().find(|(x_offset, button)| {
c::Bounds {
x: *x_offset, y: 0.0,
width: button.size.width,
height: button.size.height,
}.contains(&point)
})
.map(|(x_offset, button)| (*x_offset, button))
// Buttons are sorted so we can use a binary search to find the clicked
// button. Note this doesn't check whether the point is actually within
// a button. This is on purpose as we want a click past the left edge of
// the left-most button to register as a click.
let result = self.buttons.binary_search_by(
|&(f, _)| f.partial_cmp(&x).unwrap()
);
let index = result.unwrap_or_else(|r| r);
let index = if index > 0 { index - 1 } else { 0 };
&self.buttons[index]
}
}
@ -521,56 +560,85 @@ pub struct Spacing {
}
pub struct View {
/// Rows together with their offsets from the top
rows: Vec<(f64, Row)>,
/// Rows together with their offsets from the top left
rows: Vec<(c::Point, Row)>,
/// Total size of the view
size: Size,
}
impl View {
pub fn new(rows: Vec<(f64, Row)>) -> View {
View { rows }
// Make sure rows are sorted by offset.
debug_assert!({
let mut sorted = rows.clone();
sorted.sort_by(|(f1, _), (f2, _)| f1.partial_cmp(f2).unwrap());
sorted.iter().map(|(f, _)| *f).collect::<Vec<_>>()
== rows.iter().map(|(f, _)| *f).collect::<Vec<_>>()
});
// No need to call `get_rows()`,
// as the biggest row is the most far reaching in both directions
// because they are all centered.
let width = find_max_double(rows.iter(), |(_offset, row)| row.size.width);
let height = rows.iter().next_back()
.map(|(y_offset, row)| row.size.height + y_offset)
.unwrap_or(0.0);
// Center the rows
let rows = rows.into_iter().map(|(y_offset, row)| {(
c::Point {
x: (width - row.size.width) / 2.0,
y: y_offset,
},
row,
)}).collect::<Vec<_>>();
View { rows, size: Size { width, height } }
}
/// Finds the first button that covers the specified point
/// relative to view's position's origin
fn find_button_by_position(&self, point: c::Point)
-> Option<ButtonPlace>
{
self.get_rows().iter().find_map(|(row_offset, row)| {
// make point relative to the inside of the row
row.find_button_by_position({
c::Point { x: point.x, y: point.y } - row_offset
}).map(|(button_x_offset, button)| ButtonPlace {
button,
offset: row_offset + c::Point {
x: button_x_offset,
y: 0.0,
},
})
// Only test bounds of the view here, letting rows/column search extend
// to the edges of these bounds.
let bounds = c::Bounds {
x: 0.0,
y: 0.0,
width: self.size.width,
height: self.size.height,
};
if !bounds.contains(&point) {
return None;
}
// Rows are sorted so we can use a binary search to find the row.
let result = self.rows.binary_search_by(
|(f, _)| f.y.partial_cmp(&point.y).unwrap()
);
let index = result.unwrap_or_else(|r| r);
let index = if index > 0 { index - 1 } else { 0 };
let row = &self.rows[index];
let button = row.1.find_button_by_position(point.x - row.0.x);
Some(ButtonPlace {
button: &button.1,
offset: &row.0 + c::Point { x: button.0, y: 0.0 },
})
}
pub fn get_width(&self) -> f64 {
// No need to call `get_rows()`,
// as the biggest row is the most far reaching in both directions
// because they are all centered.
find_max_double(self.rows.iter(), |(_offset, row)| row.get_width())
}
pub fn get_height(&self) -> f64 {
self.rows.iter().next_back()
.map(|(y_offset, row)| row.get_height() + y_offset)
.unwrap_or(0.0)
pub fn get_size(&self) -> Size {
self.size.clone()
}
/// Returns positioned rows, with appropriate x offsets (centered)
pub fn get_rows(&self) -> Vec<(c::Point, &Row)> {
let available_width = self.get_width();
self.rows.iter().map(|(y_offset, row)| {(
c::Point {
x: (available_width - row.get_width()) / 2.0,
y: *y_offset,
},
row,
)}).collect()
pub fn get_rows(&self) -> &Vec<(c::Point, Row)> {
&self.rows
}
/// Returns a size which contains all the views
@ -579,11 +647,11 @@ impl View {
Size {
height: find_max_double(
views.iter(),
|view| view.get_height(),
|view| view.size.height,
),
width: find_max_double(
views.iter(),
|view| view.get_width(),
|view| view.size.width,
),
}
}
@ -731,7 +799,7 @@ impl Layout {
where F: FnMut(c::Point, &Box<Button>)
{
let (view_offset, view) = self.get_current_view_position();
for (row_offset, row) in &view.get_rows() {
for (row_offset, row) in view.get_rows() {
for (x_offset, button) in &row.buttons {
let offset = view_offset
+ row_offset.clone()
@ -744,7 +812,7 @@ impl Layout {
pub fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
let mut out = Vec::new();
let view = self.get_current_view();
for (_, row) in &view.get_rows() {
for (_, row) in view.get_rows() {
for (_, button) in &row.buttons {
let locked = {
let state = RefCell::borrow(&button.state).clone();
@ -806,13 +874,9 @@ mod procedures {
let button = make_button_with_state("1".into(), state);
let button_ptr = as_ptr(&button);
let row = Row {
buttons: vec!((0.1, button)),
};
let row = Row::new(vec!((0.1, button)));
let view = View {
rows: vec!((1.2, row)),
};
let view = View::new(vec!((1.2, row)));
assert_eq!(
find_key_places(&view, &state_clone.clone()).into_iter()
@ -823,9 +887,7 @@ mod procedures {
)
);
let view = View {
rows: Vec::new(),
};
let view = View::new(vec![]);
assert_eq!(
find_key_places(&view, &state_clone.clone()).is_empty(),
true
@ -916,9 +978,38 @@ mod seat {
"Key {:?} was already pressed", rckey,
);
}
let mut key = rckey.borrow_mut();
submission.handle_press(&key, KeyState::get_id(rckey), time);
key.pressed = PressType::Pressed;
let key: KeyState = {
RefCell::borrow(rckey).clone()
};
let action = key.action.clone();
match action {
Action::Submit {
text: Some(text),
keys: _,
} => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Text(&text),
&key.keycodes,
time,
),
Action::Submit {
text: None,
keys: _,
} => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Keycodes,
&key.keycodes,
time,
),
Action::Erase => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Erase,
&key.keycodes,
time,
),
_ => {},
};
RefCell::replace(rckey, key.into_pressed());
}
pub fn handle_release_key(
@ -961,6 +1052,18 @@ mod seat {
)
.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());
match gets_locked {
true => submission.handle_add_modifier(
key_id,
modifier, time,
),
false => submission.handle_drop_modifier(key_id, time),
}
}
// only show when UI is present
Action::ShowPreferences => if let Some(ui) = &ui {
// only show when layout manager is available
@ -987,10 +1090,6 @@ mod seat {
}
}
},
Action::SetModifier(_) => log_print!(
logging::Level::Bug,
"Modifiers unsupported",
),
};
let pointer = ::util::Pointer(rckey.clone());
@ -1006,6 +1105,7 @@ mod test {
use super::*;
use std::ffi::CString;
use ::keyboard::PressType;
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
Rc::new(RefCell::new(::keyboard::KeyState {
@ -1030,37 +1130,56 @@ mod test {
#[test]
fn check_centering() {
// foo
// A B
// ---bar---
let view = View::new(vec![
(
0.0,
Row {
buttons: vec![(
Row::new(vec![
(
0.0,
Box::new(Button {
size: Size { width: 10.0, height: 10.0 },
..*make_button_with_state("foo".into(), make_state())
size: Size { width: 5.0, height: 10.0 },
..*make_button_with_state("A".into(), make_state())
}),
)]
},
),
(
5.0,
Box::new(Button {
size: Size { width: 5.0, height: 10.0 },
..*make_button_with_state("B".into(), make_state())
}),
),
]),
),
(
10.0,
Row {
buttons: vec![(
Row::new(vec![
(
0.0,
Box::new(Button {
size: Size { width: 30.0, height: 10.0 },
..*make_button_with_state("bar".into(), make_state())
}),
)]
},
),
]),
)
]);
assert!(
view.find_button_by_position(c::Point { x: 5.0, y: 5.0 })
.is_none()
.unwrap().button.name.to_str().unwrap() == "A"
);
assert!(
view.find_button_by_position(c::Point { x: 14.99, y: 5.0 })
.unwrap().button.name.to_str().unwrap() == "A"
);
assert!(
view.find_button_by_position(c::Point { x: 15.01, y: 5.0 })
.unwrap().button.name.to_str().unwrap() == "B"
);
assert!(
view.find_button_by_position(c::Point { x: 25.0, y: 5.0 })
.unwrap().button.name.to_str().unwrap() == "B"
);
}
@ -1070,15 +1189,13 @@ mod test {
let view = View::new(vec![
(
0.0,
Row {
buttons: vec![(
0.0,
Box::new(Button {
size: Size { width: 1.0, height: 1.0 },
..*make_button_with_state("foo".into(), make_state())
}),
)]
},
Row::new(vec![(
0.0,
Box::new(Button {
size: Size { width: 1.0, height: 1.0 },
..*make_button_with_state("foo".into(), make_state())
}),
)]),
),
]);
let layout = Layout {

View File

@ -35,5 +35,6 @@ mod style;
mod submission;
pub mod tests;
pub mod util;
mod ui_manager;
mod vkeyboard;
mod xdg;

View File

@ -31,22 +31,16 @@ pub enum Error {
impl ::std::fmt::Display for Error {
fn fmt(&self, out: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use ::std::error::Error;
out.write_str(self.description())
}
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
match self {
out.write_str(match self {
&Error::NotWellFormed => "Language tag is not well-formed.",
// this is exception: here we do want exhaustive match so we don't publish version with
// missing descriptions by mistake.
&Error::__NonExhaustive => panic!("Placeholder error must not be instantiated!"),
}
})
}
}
/// Convenience Result alias.
type Result<T> = ::std::result::Result<T, Error>;

View File

@ -14,6 +14,7 @@ sources = [
config_h,
'dbus.c',
'imservice.c',
'popover.c',
'server-context-service.c',
'wayland.c',
'../eek/eek.c',
@ -36,9 +37,10 @@ cc = meson.get_compiler('c')
deps = [
# dependency('glib-2.0', version: '>=2.26.0'),
dependency('gio-2.0', version: '>=2.26.0'),
dependency('gio-unix-2.0'),
dependency('gnome-desktop-3.0', version: '>=3.0'),
dependency('gtk+-3.0', version: '>=3.0'),
dependency('libcroco-0.6'),
dependency('libfeedback-0.0'),
dependency('wayland-client', version: '>=1.14'),
dependency('xkbcommon'),
cc.find_library('m'),
@ -55,7 +57,8 @@ rslibs = custom_target(
output: ['librs.a'],
install: false,
console: true,
command: [cargo_build] + cargo_build_flags + ['@OUTPUT@', '--lib']
command: [cargo_build] + ['@OUTPUT@', '--lib'] + cargo_build_flags,
depends: cargo_toml,
)
build_rstests = custom_target(
@ -69,17 +72,17 @@ build_rstests = custom_target(
output: ['src'],
install: false,
console: true,
command: [cargo_script, 'test', '--no-run'],
depends: rslibs, # no point building tests if the code itself fails
command: [cargo_script, 'test', '--no-run'] + cargo_build_flags,
depends: [rslibs, cargo_toml], # no point building tests if the code itself fails
)
test(
'rstest',
cargo_script,
args: ['test'],
args: ['test'] + cargo_build_flags,
# this is a whole Carg-based test suite, let it run for a while
timeout: 900,
depends: build_rstests,
depends: [build_rstests, cargo_toml],
)
libsqueekboard = static_library('libsqueekboard',

View File

@ -4,10 +4,14 @@
#include "wayland-client-protocol.h"
struct squeek_outputs;
struct squeek_output_handle {
struct wl_output *output;
struct squeek_outputs *outputs;
};
struct squeek_outputs *squeek_outputs_new();
struct squeek_outputs *squeek_outputs_new(void);
void squeek_outputs_free(struct squeek_outputs*);
void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output);
struct wl_output *squeek_outputs_get_current(struct squeek_outputs*);
struct squeek_output_handle squeek_outputs_get_current(struct squeek_outputs*);
int32_t squeek_outputs_get_perceptual_width(struct squeek_outputs*, struct wl_output *output);
#endif

View File

@ -17,7 +17,7 @@ pub mod c {
// Defined in C
#[repr(transparent)]
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Copy)]
pub struct WlOutput(*const c_void);
#[repr(C)]
@ -105,6 +105,24 @@ pub mod c {
type COutputs = ::util::c::Wrapped<Outputs>;
/// A stable reference to an output.
#[derive(Clone)]
#[repr(C)]
pub struct OutputHandle {
wl_output: WlOutput,
outputs: COutputs,
}
impl OutputHandle {
// Cannot return an Output reference
// because COutputs is too deeply wrapped
pub fn get_state(&self) -> Option<OutputState> {
let outputs = self.outputs.clone_ref();
let outputs = outputs.borrow();
find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone())
}
}
// Defined in Rust
extern fn outputs_handle_geometry(
@ -240,46 +258,15 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn squeek_outputs_get_current(raw_collection: COutputs) -> WlOutput {
fn squeek_outputs_get_current(raw_collection: COutputs) -> OutputHandle {
let collection = raw_collection.clone_ref();
let collection = collection.borrow();
collection.outputs[0].output.clone()
}
#[no_mangle]
pub extern "C"
fn squeek_outputs_get_perceptual_width(
raw_collection: COutputs,
wl_output: WlOutput,
) -> i32 {
let collection = raw_collection.clone_ref();
let collection = collection.borrow();
let output_state = find_output(&collection, wl_output)
.map(|o| &o.current);
match output_state {
Some(OutputState {
current_mode: Some(super::Mode { width, height } ),
transform: Some(transform),
scale,
}) => {
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => width / scale,
_ => height / scale,
}
},
_ => {
log_print!(
logging::Level::Surprise,
"Not enough info received on output",
);
0
},
OutputHandle {
wl_output: collection.outputs[0].output.clone(),
outputs: raw_collection.clone(),
}
}
// TODO: handle unregistration
fn find_output(
@ -305,6 +292,14 @@ pub mod c {
}
}
/// Generic size
#[derive(Clone)]
pub struct Size {
pub width: u32,
pub height: u32,
}
/// wl_output mode
#[derive(Clone)]
struct Mode {
width: i32,
@ -315,10 +310,16 @@ struct Mode {
pub struct OutputState {
current_mode: Option<Mode>,
transform: Option<c::Transform>,
scale: i32,
pub scale: i32,
}
impl OutputState {
// More properly, this would have been a builder kind of struct,
// with wl_output gradually adding properties to it
// before it reached a fully initialized state,
// when it would transform into a struct without all (some?) of the Options.
// However, it's not clear which state is fully initialized,
// and whether it would make things easier at all anyway.
fn uninitialized() -> OutputState {
OutputState {
current_mode: None,
@ -326,6 +327,32 @@ impl OutputState {
scale: 1,
}
}
pub fn get_pixel_size(&self) -> Option<Size> {
use self::c::Transform;
match self {
OutputState {
current_mode: Some(Mode { width, height } ),
transform: Some(transform),
scale: _,
} => Some(
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => Size {
width: *width as u32,
height: *height as u32,
},
_ => Size {
width: *height as u32,
height: *width as u32,
},
}
),
_ => None,
}
}
}
pub struct Output {

69
src/popover.c Normal file
View File

@ -0,0 +1,69 @@
#include <gio/gio.h>
static void
call_dbus_cb (GDBusProxy *proxy,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GError) err = NULL;
g_autoptr (GVariant) output = NULL;
output = g_dbus_proxy_call_finish (proxy, res, &err);
if (err) {
g_warning ("Can't open panel %s", err->message);
}
g_object_unref (proxy);
}
static void
create_dbus_proxy_cb (GObject *source_object, GAsyncResult *res, char *panel)
{
GDBusProxy *proxy;
g_autoptr (GError) err = NULL;
GVariantBuilder builder;
GVariant *params[3];
GVariant *array[1];
proxy = g_dbus_proxy_new_for_bus_finish (res, &err);
if (err != NULL) {
g_warning ("Can't open panel %s: %s", panel, err->message);
g_free (panel);
return;
}
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
g_variant_builder_add (&builder, "v", g_variant_new_string (""));
array[0] = g_variant_new ("v", g_variant_new ("(sav)", panel, &builder));
params[0] = g_variant_new_string ("launch-panel");
params[1] = g_variant_new_array (G_VARIANT_TYPE ("v"), array, 1);
params[2] = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
g_dbus_proxy_call (proxy,
"Activate",
g_variant_new_tuple (params, 3),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) call_dbus_cb,
NULL);
g_free (panel);
}
void
popover_open_settings_panel (char *panel)
{
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.gnome.ControlCenter",
"/org/gnome/ControlCenter",
"org.gtk.Actions",
NULL,
(GAsyncReadyCallback) create_dbus_proxy_cb,
g_strdup (panel));
}

View File

@ -3,6 +3,7 @@
use gio;
use gtk;
use std::ffi::CString;
use std::cmp::Ordering;
use ::layout::c::{ Bounds, EekGtkKeyboard };
use ::locale;
use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
@ -11,16 +12,29 @@ use ::logging;
use ::manager;
use ::resources;
// Traits
use gio::ActionMapExt;
use gio::SettingsExt;
#[cfg(feature = "gio_v0_5")]
use gio::SimpleActionExt;
use glib::translate::FromGlibPtrNone;
use glib::variant::ToVariant;
#[cfg(not(feature = "gtk_v0_5"))]
use gtk::BuilderExtManual;
use gtk::PopoverExt;
use gtk::WidgetExt;
use std::io::Write;
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);
}
}
mod variants {
use glib;
use glib::Variant;
@ -29,6 +43,7 @@ mod variants {
use glib::ToVariant;
use glib::translate::FromGlibPtrFull;
use glib::translate::FromGlibPtrNone;
use glib::translate::ToGlibPtr;
/// Unpacks tuple & array variants
@ -91,12 +106,7 @@ mod variants {
unsafe {
let ret = glib_sys::g_variant_builder_end(builder);
glib_sys::g_variant_builder_unref(builder);
// HACK: This is to prevent C taking ownership
// of "floating" Variants,
// where Rust gets to keep a stale reference
// and crash when trying to drop it.
glib_sys::g_variant_ref_sink(ret);
glib::Variant::from_glib_full(ret)
glib::Variant::from_glib_none(ret)
}
}
}
@ -128,6 +138,12 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
xml,
"
</section>
<section>
<item>
<attribute name=\"label\" translatable=\"yes\">Keyboard Settings</attribute>
<attribute name=\"action\">settings</attribute>
</item>
</section>
</menu>
</interface>"
).unwrap();
@ -136,19 +152,40 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
)
}
fn get_settings(schema_name: &str) -> Option<gio::Settings> {
let mut error_handler = logging::Print{};
gio::SettingsSchemaSource::get_default()
.or_warn(
&mut error_handler,
logging::Problem::Surprise,
"No gsettings schemas installed.",
)
.and_then(|sss|
sss.lookup(schema_name, true)
.or_warn(
&mut error_handler,
logging::Problem::Surprise,
&format!("Gsettings schema {} not installed", schema_name),
)
)
.map(|_sschema| gio::Settings::new(schema_name))
}
fn set_layout(kind: String, name: String) {
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
let inputs = settings.get_value("sources").unwrap();
let current = (kind.clone(), name.clone());
let inputs = variants::get_tuples(inputs).into_iter()
.filter(|t| t != &current);
let inputs = vec![(kind, name)].into_iter()
.chain(inputs).collect();
settings.set_value(
"sources",
&variants::ArrayPairString(inputs).to_variant(),
);
settings.apply();
let settings = get_settings("org.gnome.desktop.input-sources");
if let Some(settings) = settings {
let inputs = settings.get_value("sources").unwrap();
let current = (kind.clone(), name.clone());
let inputs = variants::get_tuples(inputs).into_iter()
.filter(|t| t != &current);
let inputs = vec![(kind, name)].into_iter()
.chain(inputs).collect();
settings.set_value(
"sources",
&variants::ArrayPairString(inputs).to_variant(),
);
settings.apply();
}
}
/// A reference to what the user wants to see
@ -288,9 +325,13 @@ pub fn show(
let overlay_layouts = resources::get_overlays().into_iter()
.map(|name| LayoutId::Local(name.to_string()));
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
let inputs = settings.get_value("sources").unwrap();
let inputs = variants::get_tuples(inputs);
let settings = get_settings("org.gnome.desktop.input-sources");
let inputs = settings
.map(|settings| {
let inputs = settings.get_value("sources").unwrap();
variants::get_tuples(inputs)
})
.unwrap_or_else(|| Vec::new());
let system_layouts: Vec<LayoutId> = inputs.into_iter()
.map(|(kind, name)| LayoutId::System { kind, name })
@ -309,8 +350,13 @@ pub fn show(
.zip(all_layouts.clone().into_iter())
.collect();
human_names.sort_unstable_by(|(tr_a, _), (tr_b, _)| {
compare_current_locale(&tr_a.0, &tr_b.0)
human_names.sort_unstable_by(|(tr_a, layout_a), (tr_b, layout_b)| {
// Sort first by layout then name
match (layout_a, layout_b) {
(LayoutId::Local(_), LayoutId::System { .. }) => Ordering::Greater,
(LayoutId::System { .. }, LayoutId::Local(_)) => Ordering::Less,
_ => compare_current_locale(&tr_a.0, &tr_b.0)
}
});
// GVariant doesn't natively support `enum`s,
@ -389,8 +435,14 @@ pub fn show(
menu_inner.popdown();
});
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 action_group = gio::SimpleActionGroup::new();
action_group.add_action(&layout_action);
action_group.add_action(&settings_action);
menu.insert_action_group("popup", Some(&action_group));
};

View File

@ -11,13 +11,20 @@ use std::iter::FromIterator;
// and what a convenience layout. "_wide" is not a layout,
// neither is "number"
const KEYBOARDS: &[(*const str, *const str)] = &[
// layouts
// layouts: us must be left as first, as it is the,
// fallback layout. The others should be alphabetical.
("us", include_str!("../data/keyboards/us.yaml")),
("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
("br", include_str!("../data/keyboards/br.yaml")),
("de", include_str!("../data/keyboards/de.yaml")),
("be", include_str!("../data/keyboards/be.yaml")),
("be_wide", include_str!("../data/keyboards/be_wide.yaml")),
("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
("dk", include_str!("../data/keyboards/dk.yaml")),
("es", include_str!("../data/keyboards/es.yaml")),
("fi", include_str!("../data/keyboards/fi.yaml")),
("fr", include_str!("../data/keyboards/fr.yaml")),
("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
("gr", include_str!("../data/keyboards/gr.yaml")),
("it", include_str!("../data/keyboards/it.yaml")),
("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
@ -26,9 +33,12 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
("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")),
("ua", include_str!("../data/keyboards/ua.yaml")),
// layout+overlay
("terminal", include_str!("../data/keyboards/terminal.yaml")),
("terminal_wide", include_str!("../data/keyboards/terminal_wide.yaml")),
// Overlays
("emoji", include_str!("../data/keyboards/emoji.yaml")),
];
@ -67,6 +77,7 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
("es-ES", include_str!("../data/langs/es-ES.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")),
];
pub fn get_layout_names(lang: &str)

View File

@ -30,125 +30,116 @@
enum {
PROP_0,
PROP_SIZE_CONSTRAINT_LANDSCAPE,
PROP_SIZE_CONSTRAINT_PORTRAIT,
PROP_VISIBLE,
PROP_ENABLED,
PROP_LAST
};
typedef struct _ServerContextServiceClass ServerContextServiceClass;
struct _ServerContextService {
GObject parent;
EekboardContextService *state; // unowned
/// Needed for instantiating the widget
struct submission *submission; // unowned
struct squeek_layout_state *layout;
struct ui_manager *manager; // unowned
gboolean visible;
gboolean enabled;
PhoshLayerSurface *window;
GtkWidget *widget; // nullable
guint hiding;
guint last_requested_height;
enum squeek_arrangement_kind last_type;
gdouble size_constraint_landscape[2];
gdouble size_constraint_portrait[2];
};
struct _ServerContextServiceClass {
GObjectClass parent_class;
};
G_DEFINE_TYPE(ServerContextService, server_context_service, G_TYPE_OBJECT);
static void
on_destroy (GtkWidget *widget, gpointer user_data)
on_destroy (ServerContextService *self, GtkWidget *widget)
{
ServerContextService *context = user_data;
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
g_assert (widget == GTK_WIDGET(context->window));
g_assert (widget == GTK_WIDGET(self->window));
context->window = NULL;
context->widget = NULL;
self->window = NULL;
self->widget = NULL;
//eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context));
}
static void
make_widget (ServerContextService *context);
static void
on_notify_keyboard (GObject *object,
GParamSpec *spec,
ServerContextService *context)
on_notify_map (ServerContextService *self, GtkWidget *widget)
{
/* Recreate the keyboard widget to keep in sync with the keymap. */
if (context->window)
make_widget(context);
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
gboolean visible;
g_object_get (context, "visible", &visible, NULL);
if (visible) {
server_context_service_hide_keyboard(context);
server_context_service_show_keyboard(context);
}
}
static void
on_notify_map (GObject *object,
ServerContextService *context)
{
g_object_set (context, "visible", TRUE, NULL);
g_object_set (self, "visible", TRUE, NULL);
}
static void
on_notify_unmap (GObject *object,
ServerContextService *context)
on_notify_unmap (ServerContextService *self, GtkWidget *widget)
{
(void)object;
g_object_set (context, "visible", FALSE, NULL);
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
g_object_set (self, "visible", FALSE, NULL);
}
static uint32_t
calculate_height(int32_t width)
calculate_height(int32_t width, GdkRectangle *geometry)
{
uint32_t height = 180;
if (width < 360 && width > 0) {
height = ((unsigned)width * 7 / 12); // to match 360×210
} else if (width < 540) {
height = 180 + (540 - (unsigned)width) * 30 / 180; // smooth transition
uint32_t height;
if (geometry->width > geometry->height) {
// 1:5 ratio works fine on lanscape mode, and makes sure there's
// room left for the app window
height = width / 5;
} else {
if (width < 540 && width > 0) {
height = ((unsigned)width * 7 / 12); // to match 360×210
} else {
// Here we switch to wide layout, less height needed
height = ((unsigned)width * 7 / 22);
}
}
return height;
}
enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 540) {
return ARRANGEMENT_KIND_BASE;
}
return ARRANGEMENT_KIND_WIDE;
}
static void
on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context)
on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
{
GdkDisplay *display = NULL;
GdkWindow *window = NULL;
GdkMonitor *monitor = NULL;
GdkRectangle geometry;
gint width;
gint height;
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
g_return_if_fail (PHOSH_IS_LAYER_SURFACE (surface));
g_object_get(G_OBJECT(surface),
"configured-width", &width,
"configured-height", &height,
NULL);
// check if the change would switch types
enum squeek_arrangement_kind new_type = get_type((uint32_t)width, (uint32_t)height);
if (context->last_type != new_type) {
context->last_type = new_type;
eekboard_context_service_update_layout(context->state, context->last_type);
}
guint desired_height = calculate_height(width);
// In order to improve height calculation, we need the monitor geometry so
// we can use different algorithms for portrait and landscape mode.
// Note: this is a temporary fix until the size manager is complete.
display = gdk_display_get_default ();
if (display)
window = gtk_widget_get_window (GTK_WIDGET (surface));
if (window)
monitor = gdk_display_get_monitor_at_window (display, window);
if (monitor)
gdk_monitor_get_geometry (monitor, &geometry);
else
geometry.width = geometry.height = 0;
// When the geometry event comes after surface.configure,
// this entire height calculation does nothing.
// guint desired_height = squeek_uiman_get_perceptual_height(context->manager);
// Temporarily use old method, until the size manager is complete.
guint desired_height = calculate_height(width, &geometry);
guint configured_height = (guint)height;
// if height was already requested once but a different one was given
// (for the same set of surrounding properties),
@ -156,8 +147,8 @@ on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context)
// as it's likely to create pointless loops
// of request->reject->request_again->...
if (desired_height != configured_height
&& context->last_requested_height != desired_height) {
context->last_requested_height = desired_height;
&& self->last_requested_height != desired_height) {
self->last_requested_height = desired_height;
phosh_layer_surface_set_size(surface, 0,
(gint)desired_height);
phosh_layer_surface_set_exclusive_zone(surface, (gint)desired_height);
@ -166,19 +157,19 @@ on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context)
}
static void
make_window (ServerContextService *context)
make_window (ServerContextService *self)
{
if (context->window)
if (self->window)
g_error("Window already present");
struct wl_output *output = squeek_outputs_get_current(squeek_wayland->outputs);
int32_t width = squeek_outputs_get_perceptual_width(squeek_wayland->outputs, output);
uint32_t height = calculate_height(width);
struct squeek_output_handle output = squeek_outputs_get_current(squeek_wayland->outputs);
squeek_uiman_set_output(self->manager, output);
uint32_t height = squeek_uiman_get_perceptual_height(self->manager);
context->window = g_object_new (
self->window = g_object_new (
PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell,
"wl-output", output,
"wl-output", output.output,
"height", height,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
@ -190,11 +181,11 @@ make_window (ServerContextService *context)
NULL
);
g_object_connect (context->window,
"signal::destroy", G_CALLBACK(on_destroy), context,
"signal::map", G_CALLBACK(on_notify_map), context,
"signal::unmap", G_CALLBACK(on_notify_unmap), context,
"signal::configured", G_CALLBACK(on_surface_configure), context,
g_object_connect (self->window,
"swapped-signal::destroy", G_CALLBACK(on_destroy), self,
"swapped-signal::map", G_CALLBACK(on_notify_map), self,
"swapped-signal::unmap", G_CALLBACK(on_notify_unmap), self,
"swapped-signal::configured", G_CALLBACK(on_surface_configure), self,
NULL);
// The properties below are just to make hacking easier.
@ -202,90 +193,90 @@ make_window (ServerContextService *context)
// and there's no space in the protocol for others.
// Those may still be useful in the future,
// or for hacks with regular windows.
gtk_widget_set_can_focus (GTK_WIDGET(context->window), FALSE);
g_object_set (G_OBJECT(context->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(context->window),
gtk_widget_set_can_focus (GTK_WIDGET(self->window), FALSE);
g_object_set (G_OBJECT(self->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(self->window),
_("Squeekboard"));
gtk_window_set_icon_name (GTK_WINDOW(context->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(context->window), TRUE);
gtk_window_set_icon_name (GTK_WINDOW(self->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(self->window), TRUE);
}
static void
destroy_window (ServerContextService *context)
destroy_window (ServerContextService *self)
{
gtk_widget_destroy (GTK_WIDGET (context->window));
context->window = NULL;
gtk_widget_destroy (GTK_WIDGET (self->window));
self->window = NULL;
}
static void
make_widget (ServerContextService *context)
make_widget (ServerContextService *self)
{
if (context->widget) {
gtk_widget_destroy(context->widget);
context->widget = NULL;
if (self->widget) {
gtk_widget_destroy(self->widget);
self->widget = NULL;
}
self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->layout);
LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (context->state);
context->widget = eek_gtk_keyboard_new (keyboard, context->state, context->submission);
gtk_widget_set_has_tooltip (context->widget, TRUE);
gtk_container_add (GTK_CONTAINER(context->window), context->widget);
gtk_widget_show (context->widget);
gtk_widget_set_has_tooltip (self->widget, TRUE);
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
gtk_widget_show_all(self->widget);
}
static gboolean
on_hide (ServerContextService *context)
on_hide (ServerContextService *self)
{
gtk_widget_hide (GTK_WIDGET(context->window));
context->hiding = 0;
gtk_widget_hide (GTK_WIDGET(self->window));
self->hiding = 0;
return G_SOURCE_REMOVE;
}
static void
server_context_service_real_show_keyboard (ServerContextService *context)
server_context_service_real_show_keyboard (ServerContextService *self)
{
if (context->hiding) {
g_source_remove (context->hiding);
context->hiding = 0;
if (!self->enabled)
return;
if (self->hiding) {
g_source_remove (self->hiding);
self->hiding = 0;
}
if (!context->window)
make_window (context);
if (!context->widget)
make_widget (context);
if (!self->window)
make_window (self);
if (!self->widget)
make_widget (self);
context->visible = TRUE;
gtk_widget_show (GTK_WIDGET(context->window));
self->visible = TRUE;
gtk_widget_show (GTK_WIDGET(self->window));
}
static void
server_context_service_real_hide_keyboard (ServerContextService *context)
server_context_service_real_hide_keyboard (ServerContextService *self)
{
if (!context->hiding)
context->hiding = g_timeout_add (200, (GSourceFunc) on_hide, context);
if (!self->hiding)
self->hiding = g_timeout_add (200, (GSourceFunc) on_hide, self);
context->visible = FALSE;
self->visible = FALSE;
}
void
server_context_service_show_keyboard (ServerContextService *context)
server_context_service_show_keyboard (ServerContextService *self)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(context));
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
if (!context->visible) {
server_context_service_real_show_keyboard (context);
if (!self->visible) {
server_context_service_real_show_keyboard (self);
}
}
void
server_context_service_hide_keyboard (ServerContextService *context)
server_context_service_hide_keyboard (ServerContextService *self)
{
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(context));
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE(self));
if (context->visible) {
server_context_service_real_hide_keyboard (context);
if (self->visible) {
server_context_service_real_hide_keyboard (self);
}
}
@ -295,26 +286,15 @@ server_context_service_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
GVariant *variant;
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
switch (prop_id) {
case PROP_SIZE_CONSTRAINT_LANDSCAPE:
variant = g_value_get_variant (value);
g_variant_get (variant, "(dd)",
&context->size_constraint_landscape[0],
&context->size_constraint_landscape[1]);
break;
case PROP_SIZE_CONSTRAINT_PORTRAIT:
variant = g_value_get_variant (value);
g_variant_get (variant, "(dd)",
&context->size_constraint_portrait[0],
&context->size_constraint_portrait[1]);
break;
case PROP_VISIBLE:
context->visible = g_value_get_boolean (value);
self->visible = g_value_get_boolean (value);
break;
case PROP_ENABLED:
server_context_service_set_enabled (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -327,10 +307,10 @@ server_context_service_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
switch (prop_id) {
case PROP_VISIBLE:
g_value_set_boolean (value, context->visible);
g_value_set_boolean (value, self->visible);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -341,10 +321,10 @@ server_context_service_get_property (GObject *object,
static void
server_context_service_dispose (GObject *object)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
ServerContextService *self = SERVER_CONTEXT_SERVICE(object);
destroy_window (context);
context->widget = NULL;
destroy_window (self);
self->widget = NULL;
G_OBJECT_CLASS (server_context_service_parent_class)->dispose (object);
}
@ -359,26 +339,6 @@ server_context_service_class_init (ServerContextServiceClass *klass)
gobject_class->get_property = server_context_service_get_property;
gobject_class->dispose = server_context_service_dispose;
pspec = g_param_spec_variant ("size-constraint-landscape",
"Size constraint landscape",
"Size constraint landscape",
G_VARIANT_TYPE("(dd)"),
NULL,
G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_SIZE_CONSTRAINT_LANDSCAPE,
pspec);
pspec = g_param_spec_variant ("size-constraint-portrait",
"Size constraint portrait",
"Size constraint portrait",
G_VARIANT_TYPE("(dd)"),
NULL,
G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_SIZE_CONSTRAINT_PORTRAIT,
pspec);
/**
* Flag to indicate if keyboard is visible or not.
*/
@ -390,27 +350,67 @@ server_context_service_class_init (ServerContextServiceClass *klass)
g_object_class_install_property (gobject_class,
PROP_VISIBLE,
pspec);
/**
* ServerContextServie:keyboard:
*
* Does the user want the keyboard to show up automatically?
*/
pspec =
g_param_spec_boolean ("enabled",
"Enabled",
"Whether the keyboard is enabled",
TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (gobject_class,
PROP_ENABLED,
pspec);
}
static void
server_context_service_init (ServerContextService *state) {
(void)state;
server_context_service_init (ServerContextService *self) {
const char *schema_name = "org.gnome.desktop.a11y.applications";
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
g_autoptr(GSettingsSchema) schema = NULL;
self->enabled = TRUE;
if (!ssrc) {
g_warning("No gsettings schemas installed.");
return;
}
schema = g_settings_schema_source_lookup(ssrc, schema_name, TRUE);
if (schema) {
g_autoptr(GSettings) settings = g_settings_new (schema_name);
g_settings_bind (settings, "screen-keyboard-enabled",
self, "enabled", G_SETTINGS_BIND_GET);
} else {
g_warning("Gsettings schema %s is not installed on the system. "
"Enabling by default.", schema_name);
}
}
ServerContextService *
server_context_service_new (EekboardContextService *state, struct submission *submission)
server_context_service_new (EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman)
{
ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
ui->submission = submission;
ui->state = state;
g_signal_connect (state,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ui);
ui->state = self;
ui->layout = layout;
ui->manager = uiman;
return ui;
}
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *service)
void
server_context_service_set_enabled (ServerContextService *self, gboolean enabled)
{
return service->last_type;
g_return_if_fail (SERVER_IS_CONTEXT_SERVICE (self));
if (enabled == self->enabled)
return;
self->enabled = enabled;
if (self->enabled)
server_context_service_show_keyboard (self);
else
server_context_service_hide_keyboard (self);
}

View File

@ -20,26 +20,20 @@
#include "src/layout.h"
#include "src/submission.h"
#include "ui_manager.h"
G_BEGIN_DECLS
#define SERVER_TYPE_CONTEXT_SERVICE (server_context_service_get_type())
#define SERVER_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextService))
#define SERVER_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass))
#define SERVER_IS_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SERVER_TYPE_CONTEXT_SERVICE))
#define SERVER_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SERVER_TYPE_CONTEXT_SERVICE))
#define SERVER_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass))
/** Manages the lifecycle of the window displaying layouts. */
typedef struct _ServerContextService ServerContextService;
G_DECLARE_FINAL_TYPE (ServerContextService, server_context_service, SERVER, CONTEXT_SERVICE, GObject)
GType server_context_service_get_type
(void) G_GNUC_CONST;
ServerContextService *server_context_service_new(EekboardContextService *state, struct submission *submission);
ServerContextService *server_context_service_new(EekboardContextService *self, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
void server_context_service_show_keyboard (ServerContextService *context);
void server_context_service_hide_keyboard (ServerContextService *context);
void server_context_service_show_keyboard (ServerContextService *self);
void server_context_service_hide_keyboard (ServerContextService *self);
void server_context_service_set_enabled (ServerContextService *self, gboolean enabled);
G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -28,9 +28,11 @@
#include "eek/eek.h"
#include "eekboard/eekboard-context-service.h"
#include "dbus.h"
#include "layout.h"
#include "outputs.h"
#include "submission.h"
#include "server-context-service.h"
#include "ui_manager.h"
#include "wayland.h"
#include <gdk/gdkwayland.h>
@ -38,11 +40,13 @@
/// Global application state
struct squeekboard {
struct squeek_wayland wayland;
DBusHandler *dbus_handler;
EekboardContextService *settings_context;
ServerContextService *ui_context;
struct submission *submission;
struct squeek_wayland wayland; // Just hooks.
DBusHandler *dbus_handler; // Controls visibility of the OSK.
EekboardContextService *settings_context; // Gsettings hooks.
ServerContextService *ui_context; // mess, includes the entire UI
struct submission *submission; // Wayland text input handling.
struct squeek_layout_state layout_choice; // Currently wanted layout.
struct ui_manager *ui_manager; // UI shape tracker/chooser. TODO: merge with layuot choice
};
@ -199,7 +203,9 @@ main (int argc, char **argv)
g_warning("Wayland input method interface not available");
}
instance.settings_context = eekboard_context_service_new();
instance.ui_manager = squeek_uiman_new();
instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
// set up dbus
@ -222,9 +228,9 @@ main (int argc, char **argv)
error = NULL;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (connection == NULL) {
g_printerr ("Can't connect to the bus: %s\n", error->message);
g_printerr ("Can't connect to the bus: %s. "
"Visibility switching unavailable.", error->message);
g_error_free (error);
exit (1);
}
break;
case G_BUS_TYPE_NONE:
@ -246,25 +252,28 @@ main (int argc, char **argv)
g_assert_not_reached ();
break;
}
guint owner_id = 0;
DBusHandler *service = NULL;
if (connection) {
service = dbus_handler_new(connection, DBUS_SERVICE_PATH);
DBusHandler *service = dbus_handler_new(connection, DBUS_SERVICE_PATH);
if (service == NULL) {
g_printerr ("Can't create dbus server\n");
exit (1);
}
instance.dbus_handler = service;
if (service == NULL) {
g_printerr ("Can't create dbus server\n");
exit (1);
}
instance.dbus_handler = service;
guint owner_id = g_bus_own_name_on_connection (connection,
DBUS_SERVICE_INTERFACE,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired,
on_name_lost,
NULL,
NULL);
if (owner_id == 0) {
g_printerr ("Can't own the name\n");
exit (1);
owner_id = g_bus_own_name_on_connection (connection,
DBUS_SERVICE_INTERFACE,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired,
on_name_lost,
NULL,
NULL);
if (owner_id == 0) {
g_printerr ("Can't own the name\n");
exit (1);
}
}
instance.submission = get_submission(instance.wayland.input_method_manager,
@ -276,7 +285,9 @@ main (int argc, char **argv)
ServerContextService *ui_context = server_context_service_new(
instance.settings_context,
instance.submission);
instance.submission,
&instance.layout_choice,
instance.ui_manager);
if (!ui_context) {
g_error("Could not initialize GUI");
exit(1);
@ -296,9 +307,15 @@ main (int argc, char **argv)
g_main_loop_run (loop);
g_bus_unown_name (owner_id);
g_object_unref (service);
g_object_unref (connection);
if (connection) {
if (service) {
if (owner_id != 0) {
g_bus_unown_name (owner_id);
}
g_object_unref (service);
}
g_object_unref (connection);
}
g_main_loop_unref (loop);
squeek_wayland_deinit (&instance.wayland);

View File

@ -2,6 +2,6 @@
#define __STYLE_H
#include "gtk/gtk.h"
GtkCssProvider *squeek_load_style();
GtkCssProvider *squeek_load_style(void);
#endif

View File

@ -15,5 +15,5 @@ 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);
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard, uint32_t time);
#endif

View File

@ -16,14 +16,20 @@
* The text-input interface may be enabled and disabled at arbitrary times,
* and those events SHOULD NOT cause any lost events.
* */
use ::action::Action;
use std::collections::HashSet;
use std::ffi::CString;
use ::action::Modifier;
use ::imservice;
use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyState, KeyStateId, PressType };
use ::logging;
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
use ::layout::c::LevelKeyboard;
use ::util::vec_remove;
use ::vkeyboard::VirtualKeyboard;
// traits
use std::iter::FromIterator;
/// Gathers stuff defined in C or called by C
pub mod c {
use super::*;
@ -31,7 +37,6 @@ pub mod c {
use std::os::raw::c_void;
use ::imservice::c::InputMethod;
use ::layout::c::LevelKeyboard;
use ::vkeyboard::c::ZwpVirtualKeyboardV1;
// The following defined in C
@ -60,6 +65,7 @@ pub mod c {
Box::<Submission>::into_raw(Box::new(
Submission {
imservice,
modifiers_active: Vec::new(),
virtual_keyboard: VirtualKeyboard(vk),
pressed: Vec::new(),
}
@ -85,18 +91,23 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn submission_set_keyboard(submission: *mut Submission, keyboard: LevelKeyboard) {
fn submission_set_keyboard(
submission: *mut Submission,
keyboard: LevelKeyboard,
time: u32,
) {
if submission.is_null() {
panic!("Null submission pointer");
}
let submission: &mut Submission = unsafe { &mut *submission };
submission.virtual_keyboard.update_keymap(keyboard);
submission.update_keymap(keyboard, Timestamp(time));
}
}
#[derive(Clone, Copy)]
pub struct Timestamp(pub u32);
#[derive(Clone)]
enum SubmittedAction {
/// A collection of keycodes that were pressed
VirtualKeyboard(Vec<KeyCode>),
@ -106,62 +117,72 @@ enum SubmittedAction {
pub struct Submission {
imservice: Option<Box<IMService>>,
virtual_keyboard: VirtualKeyboard,
modifiers_active: Vec<(KeyStateId, Modifier)>,
pressed: Vec<(KeyStateId, SubmittedAction)>,
}
pub enum SubmitData<'a> {
Text(&'a CString),
Erase,
Keycodes,
}
impl Submission {
/// Sends a submit text event if possible;
/// otherwise sends key press and makes a note of it
pub fn handle_press(
&mut self,
key: &KeyState, key_id: KeyStateId,
key_id: KeyStateId,
data: SubmitData,
keycodes: &Vec<KeyCode>,
time: Timestamp,
) {
match &key.action {
Action::Submit { text: _, keys: _ }
| Action::Erase
=> (),
_ => {
log_print!(
logging::Level::Bug,
"Submitted key with action other than Submit or Erase",
);
return;
},
};
let mods_are_on = !self.modifiers_active.is_empty();
let was_committed_as_text = match (&mut self.imservice, &key.action) {
(Some(imservice), Action::Submit { text: Some(text), keys: _ }) => {
let submit_result = imservice.commit_string(text)
.and_then(|_| imservice.commit());
match submit_result {
Ok(()) => true,
Err(imservice::SubmitError::NotActive) => false,
let was_committed_as_text = match (&mut self.imservice, mods_are_on) {
(Some(imservice), false) => {
enum Outcome {
Submitted(Result<(), imservice::SubmitError>),
NotSubmitted,
};
let submit_outcome = match data {
SubmitData::Text(text) => {
Outcome::Submitted(imservice.commit_string(text))
},
SubmitData::Erase => {
/* Delete_surrounding_text takes byte offsets,
* so cannot work without get_surrounding_text.
* This is a bug in the protocol.
*/
// imservice.delete_surrounding_text(1, 0),
Outcome::NotSubmitted
},
SubmitData::Keycodes => Outcome::NotSubmitted,
};
match submit_outcome {
Outcome::Submitted(result) => {
match result.and_then(|()| imservice.commit()) {
Ok(()) => true,
Err(imservice::SubmitError::NotActive) => false,
}
},
Outcome::NotSubmitted => false,
}
},
/* Delete_surrounding_text takes byte offsets,
* so cannot work without get_surrounding_text.
* This is a bug in the protocol.
(Some(imservice), Action::Erase) => {
let submit_result = imservice.delete_surrounding_text(1, 0)
.and_then(|_| imservice.commit());
match submit_result {
Ok(()) => true,
Err(imservice::SubmitError::NotActive) => false,
}
}*/
(_, _) => false,
};
let submit_action = match was_committed_as_text {
true => SubmittedAction::IMService,
false => {
self.virtual_keyboard.switch(
&key.keycodes,
keycodes,
PressType::Pressed,
time,
);
SubmittedAction::VirtualKeyboard(key.keycodes.clone())
SubmittedAction::VirtualKeyboard(keycodes.clone())
},
};
@ -187,4 +208,84 @@ impl Submission {
}
};
}
pub fn handle_add_modifier(
&mut self,
key_id: KeyStateId,
modifier: Modifier, _time: Timestamp,
) {
self.modifiers_active.push((key_id, modifier));
self.update_modifiers();
}
pub fn handle_drop_modifier(
&mut self,
key_id: KeyStateId,
_time: Timestamp,
) {
vec_remove(&mut self.modifiers_active, |(id, _)| *id == key_id);
self.update_modifiers();
}
fn update_modifiers(&mut self) {
let raw_modifiers = self.modifiers_active.iter()
.map(|(_id, m)| match m {
Modifier::Control => Modifiers::CONTROL,
Modifier::Alt => Modifiers::MOD1,
})
.fold(Modifiers::empty(), |m, n| m | n);
self.virtual_keyboard.set_modifiers_state(raw_modifiers);
}
pub fn is_modifier_active(&self, modifier: Modifier) -> bool {
self.modifiers_active.iter()
.position(|(_id, m)| *m == modifier)
.is_some()
}
pub fn get_active_modifiers(&self) -> HashSet<Modifier> {
HashSet::from_iter(
self.modifiers_active.iter().map(|(_id, m)| m.clone())
)
}
fn clear_all_modifiers(&mut self) {
// Looks like an optimization,
// but preemptive cleaning is needed before setting a new keymap,
// so removing this check would break keymap setting.
if self.modifiers_active.is_empty() {
return;
}
self.modifiers_active = Vec::new();
self.virtual_keyboard.set_modifiers_state(Modifiers::empty())
}
fn release_all_virtual_keys(&mut self, time: Timestamp) {
let virtual_pressed = self.pressed
.clone().into_iter()
.filter_map(|(id, action)| {
match action {
SubmittedAction::VirtualKeyboard(_) => Some(id),
_ => None,
}
});
for id in virtual_pressed {
self.handle_release(id, time);
}
}
/// Changes keymap and clears pressed keys and modifiers.
///
/// It's not obvious if clearing is the right thing to do,
/// but keymap update may (or may not) do that,
/// possibly putting self.modifiers_active and self.pressed out of sync,
/// so a consistent stance is adopted to avoid that.
/// Alternatively, modifiers could be restored on the new keymap.
/// That approach might be difficult
/// due to modifiers meaning different things in different keymaps.
pub fn update_keymap(&mut self, keyboard: LevelKeyboard, time: Timestamp) {
self.clear_all_modifiers();
self.release_all_virtual_keys(time);
self.virtual_keyboard.update_keymap(keyboard);
}
}

View File

@ -26,17 +26,45 @@ impl CountAndPrint {
}
}
pub fn check_builtin_layout(name: &str) {
check_layout(Layout::from_resource(name).expect("Invalid layout data"))
pub fn check_builtin_layout(name: &str, missing_return: bool) {
check_layout(
Layout::from_resource(name).expect("Invalid layout data"),
missing_return,
)
}
pub fn check_layout_file(path: &str) {
check_layout(Layout::from_file(path.into()).expect("Invalid layout file"))
check_layout(
Layout::from_file(path.into()).expect("Invalid layout file"),
false,
)
}
fn check_layout(layout: Layout) {
fn check_sym_presence(
state: &xkb::State,
sym_name: &str,
handler: &mut dyn logging::Handler,
) {
let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
if sym == xkb::KEY_NoSymbol {
panic!(format!("Entered invalid keysym: {}", sym_name));
}
let map = state.get_keymap();
let range = map.min_keycode()..=map.max_keycode();
let found = range.flat_map(|code| state.key_get_syms(code))
.find(|s| **s == sym)
.is_some();
if !found {
handler.handle(
logging::Level::Surprise,
&format!("There's no way to input the keysym {} on this layout", sym_name),
)
}
}
fn check_layout(layout: Layout, allow_missing_return: bool) {
let handler = CountAndPrint::new();
let (layout, handler) = layout.build(handler);
let (layout, mut handler) = layout.build(handler);
if handler.0 > 0 {
println!("{} problems while parsing layout", handler.0)
@ -58,11 +86,20 @@ fn check_layout(layout: Layout) {
).expect("Failed to create keymap");
let state = xkb::State::new(&keymap);
check_sym_presence(&state, "BackSpace", &mut handler);
let mut printer = logging::Print;
check_sym_presence(
&state,
"Return",
if allow_missing_return { &mut printer }
else { &mut handler },
);
// "Press" each button with keysyms
for (_pos, view) in layout.views.values() {
for (_y, row) in &view.get_rows() {
for (_x, button) in &row.buttons {
for (_y, row) in view.get_rows() {
for (_x, button) in row.get_buttons() {
let keystate = button.state.borrow();
for keycode in &keystate.keycodes {
match state.key_get_one_sym(*keycode) {

14
src/ui_manager.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef UI_MANAGER__
#define UI_MANAGER__
#include <inttypes.h>
#include "outputs.h"
struct ui_manager;
struct ui_manager *squeek_uiman_new(void);
void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output);
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
#endif

81
src/ui_manager.rs Normal file
View File

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

View File

@ -98,7 +98,8 @@ pub mod c {
Rc::from_raw(self.0)
}
/// Creates a new Rc reference to the same data
/// Creates a new Rc reference to the same data.
/// Use for accessing the underlying data as a reference.
pub fn clone_ref(&self) -> Rc<RefCell<T>> {
// A bit dangerous: the Rc may be in use elsewhere
let used_rc = unsafe { Rc::from_raw(self.0) };
@ -130,6 +131,7 @@ pub mod c {
impl<T> COpaquePtr for Wrapped<T> {}
}
/// Clones the underlying data structure, like ToOwned.
pub trait CloneOwned {
type Owned;
fn clone_owned(&self) -> Self::Owned;
@ -195,6 +197,12 @@ pub trait WarningHandler {
fn handle(&mut self, warning: &str);
}
/// Removes the first matcing item
pub fn vec_remove<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, pred: F) -> Option<T> {
let idx = v.iter().position(pred);
idx.map(|idx| v.remove(idx))
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,6 +1,6 @@
/*! Managing the events belonging to virtual-keyboard interface. */
use ::keyboard::{ KeyCode, PressType };
use ::keyboard::{ KeyCode, Modifiers, PressType };
use ::layout::c::LevelKeyboard;
use ::submission::Timestamp;
@ -26,6 +26,11 @@ pub mod c {
virtual_keyboard: ZwpVirtualKeyboardV1,
keyboard: LevelKeyboard,
);
pub fn eek_virtual_keyboard_set_modifiers(
virtual_keyboard: ZwpVirtualKeyboardV1,
modifiers: u32,
);
}
}
@ -33,10 +38,10 @@ pub mod c {
pub struct VirtualKeyboard(pub c::ZwpVirtualKeyboardV1);
impl VirtualKeyboard {
// TODO: split out keyboard state management
// TODO: error out if keymap not set
pub fn switch(
&self,
keycodes: &Vec<KeyCode>,
keycodes: &[KeyCode],
action: PressType,
timestamp: Timestamp,
) {
@ -68,12 +73,16 @@ impl VirtualKeyboard {
}
}
pub fn set_modifiers_state(&self, modifiers: Modifiers) {
let modifiers = modifiers.bits() as u32;
unsafe {
c::eek_virtual_keyboard_set_modifiers(self.0, modifiers);
}
}
pub fn update_keymap(&self, keyboard: LevelKeyboard) {
unsafe {
c::eek_virtual_keyboard_update_keymap(
self.0,
keyboard,
);
c::eek_virtual_keyboard_update_keymap(self.0, keyboard);
}
}
}

View File

@ -17,7 +17,13 @@ eek_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard
void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, const LevelKeyboard *keyboard) {
zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1,
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keyboard->keymap_fd, keyboard->keymap_len);
keyboard->keymap.fd, keyboard->keymap.fd_len);
}
void
eek_virtual_keyboard_set_modifiers(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t mods_depressed) {
zwp_virtual_keyboard_v1_modifiers(zwp_virtual_keyboard_v1,
mods_depressed, 0, 0, 0);
}
int squeek_output_add_listener(struct wl_output *wl_output,

10
tests/layout_erase.yaml Normal file
View File

@ -0,0 +1,10 @@
---
# Erase only
views:
base:
- "BackSpace"
outlines:
default: { width: 0, height: 0 }
buttons:
BackSpace:
action: erase

View File

@ -49,24 +49,38 @@ endforeach
# and the need to call it manually
foreach layout : [
'us', 'us_wide',
'br',
'be', 'be_wide',
'de', 'de_wide',
'dk',
'es',
'fi',
'fr', 'fr_wide',
'gr',
'it',
'jp+kana','jp+kana_wide',
'no',
'number',
'pl', 'pl_wide',
'ru',
'se',
'terminal',
'ua',
'terminal', 'terminal_wide',
'emoji',
]
extra = []
if layout == 'emoji'
extra += ['allow_missing_return']
endif
test(
'test_layout_' + layout,
cargo_script,
args: ['run', '--example', 'test_layout', layout]
args: ['run'] + cargo_build_flags
+ ['--example', 'test_layout', '--', layout]
+ extra,
workdir: meson.build_root(),
)
endforeach

View File

@ -12,8 +12,9 @@ test_layout = custom_target('squeekboard-test-layout',
build_always_stale: true,
output: ['squeekboard-test-layout'],
console: true,
command: [cargo_build] + cargo_build_flags
+ ['--rename', 'test_layout', '@OUTPUT@', '--bin', 'test_layout'],
command: [cargo_build, '--rename', 'test_layout', '@OUTPUT@', '--bin', 'test_layout']
+ cargo_build_flags,
install: true,
install_dir: bindir,
depends: cargo_toml,
)