Compare commits
10 Commits
Fixed_im_s
...
x11kb
| Author | SHA1 | Date | |
|---|---|---|---|
| 523caa99c5 | |||
| 20c44476a3 | |||
| e6c136918c | |||
| c686cf7e81 | |||
| 2959d27ea3 | |||
| 88d3a45083 | |||
| edc330d683 | |||
| 44e06bc0dc | |||
| 8e2e8b0f5f | |||
| fea4ea7392 |
@ -19,6 +19,7 @@ path = "@path@/examples/test_layout.rs"
|
|||||||
[features]
|
[features]
|
||||||
gio_v0_5 = []
|
gio_v0_5 = []
|
||||||
gtk_v0_5 = []
|
gtk_v0_5 = []
|
||||||
|
rustc_less_1_36 = []
|
||||||
|
|
||||||
# Dependencies which don't change based on build flags
|
# Dependencies which don't change based on build flags
|
||||||
[dependencies.cairo-sys-rs]
|
[dependencies.cairo-sys-rs]
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
---
|
|
||||||
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"
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
---
|
|
||||||
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"
|
|
||||||
@ -1,211 +0,0 @@
|
|||||||
---
|
|
||||||
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"
|
|
||||||
2
debian/control
vendored
2
debian/control
vendored
@ -38,8 +38,6 @@ Depends:
|
|||||||
gnome-themes-extra-data,
|
gnome-themes-extra-data,
|
||||||
${shlibs:Depends},
|
${shlibs:Depends},
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
Breaks:
|
|
||||||
librem5-base (<< 24),
|
|
||||||
Description: On-screen keyboard for Wayland
|
Description: On-screen keyboard for Wayland
|
||||||
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
|
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
|
||||||
|
|
||||||
|
|||||||
@ -1,87 +1,34 @@
|
|||||||
A guide to creating layouts
|
Kareema's guide to creating layouts
|
||||||
===========================
|
===================================
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
It’s long overdue to write a comprehensive guide how to add a keyboard layout from start. But unfortunately, I don’t 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.
|
It’s long overdue to write a comprehensive guide how to add a keyboard layout from start. But unfortunately, I don’t 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.
|
||||||
|
|
||||||
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/)
|
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.
|
||||||
|
|
||||||
## Creating a new layout
|
**Get one of the existing keyboard layouts**
|
||||||
|
|
||||||
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)
|
* You can get one of the keyboards from the squeekboard git repository : [https://source.puri.sm/Librem5/squeekboard ](https://source.puri.sm/Librem5/squeekboard)
|
||||||
* The keyboard layouts are located in the subdirectory [`data/keyboards/`](https://source.puri.sm/Librem5/squeekboard/-/tree/master/data/keyboards) in the `.yaml` files
|
* The keyboard layouts are located in the subdirectory `data/keyboard/` in the `.yaml` files
|
||||||
* Take a look and try to understand them :slight_smile:
|
* 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).
|
* 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
|
* Clone your fork locally with `git clone` and use the uri of your forked repo there
|
||||||
|
|
||||||
### Edit your keyboard and get it merged
|
**Workflow to edit your keyboard and get it merged**
|
||||||
|
|
||||||
* It may be useful to check out the [generic guide how the workflow to contribute works](https://developer.puri.sm/Librem5/Contact/Contributing.html)
|
* A generic guide how the workflow to contribute works, can be found at https://developer.puri.sm/Librem5/Contact/Contributing.html
|
||||||
* Create a branch: Name it “keyboard-layout-mylanguage” or whatever
|
* Create a branch: Name it “keyboard-layout-mylanguage” or whatever
|
||||||
* Checkout your branch, edit your keyboard layout and commit your changes
|
* 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. **Don’t forget to add it** to `src/resources.rs` and the layout to `tests/meson.build` (that’s 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)
|
* 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
|
* Create a merge request for the branch to get your changes merged to the official squeekboard git repository
|
||||||
|
|
||||||
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.
|
**Compile squeekboard**
|
||||||
|
|
||||||
## Compiling and 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.
|
|
||||||
|
|
||||||
### Compile squeekboard
|
|
||||||
|
|
||||||
* Follow the instructions found in “Building” section of the squeekboard’s 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)
|
* Follow the instructions found in “Building” section of the squeekboard’s 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
|
**Running 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)
|
* 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)
|
* Additionally take a look at the contribution document for [testing info](HACKING.md#testing)
|
||||||
@ -90,3 +37,19 @@ If you want your change to become part of official Squeekboard, or if you want t
|
|||||||
`deb [arch=amd64] http://ci.puri.sm/ scratch librem5`
|
`deb [arch=amd64] http://ci.puri.sm/ scratch librem5`
|
||||||
|
|
||||||
Squeekboard can be installed from there as a Debian package, too (that’s what I often do). But beware - there be dragons! You could bork your system with these packages and you should probably disable this repository again after installing what you need - these packages are not meant for production systems (or so I heard :wink: )
|
Squeekboard can be installed from there as a Debian package, too (that’s 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/`
|
||||||
|
* Don’t forget to add your newly created layout or translation to `src/resources.rs` and the layout to `tests/meson.build` (that’s 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
|
||||||
|
|
||||||
|
|||||||
@ -104,10 +104,10 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Units of virtual pixels size
|
// Units of pixel size
|
||||||
static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
|
static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
|
||||||
(void)height;
|
(void)height;
|
||||||
if (width < 540) {
|
if (width < 1080) {
|
||||||
return ARRANGEMENT_KIND_BASE;
|
return ARRANGEMENT_KIND_BASE;
|
||||||
}
|
}
|
||||||
return ARRANGEMENT_KIND_WIDE;
|
return ARRANGEMENT_KIND_WIDE;
|
||||||
@ -119,10 +119,11 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
|
|||||||
{
|
{
|
||||||
EekGtkKeyboardPrivate *priv =
|
EekGtkKeyboardPrivate *priv =
|
||||||
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
|
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
|
||||||
|
uint32_t scale = (uint32_t)gtk_widget_get_scale_factor(self);
|
||||||
// check if the change would switch types
|
// check if the change would switch types
|
||||||
enum squeek_arrangement_kind new_type = get_type(
|
enum squeek_arrangement_kind new_type = get_type(
|
||||||
(uint32_t)(allocation->width - allocation->x),
|
(uint32_t)(allocation->width - allocation->x) * scale,
|
||||||
(uint32_t)(allocation->height - allocation->y));
|
(uint32_t)(allocation->height - allocation->y) * scale);
|
||||||
if (priv->layout->arrangement != new_type) {
|
if (priv->layout->arrangement != new_type) {
|
||||||
priv->layout->arrangement = new_type;
|
priv->layout->arrangement = new_type;
|
||||||
uint32_t time = gdk_event_get_time(NULL);
|
uint32_t time = gdk_event_get_time(NULL);
|
||||||
|
|||||||
@ -28,19 +28,17 @@
|
|||||||
#include <sys/random.h> // TODO: this is Linux-specific
|
#include <sys/random.h> // TODO: this is Linux-specific
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
|
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
|
|
||||||
|
/// External linkage for Rust.
|
||||||
static void eek_key_map_deinit(struct keymap *self) {
|
/// Don't call multiple times on the same copy, just in Drop.
|
||||||
if (self->fd < 0) {
|
void eek_key_map_deinit(struct KeyMap *self) {
|
||||||
g_error("Deinit called multiple times on KeyMap");
|
|
||||||
} else {
|
|
||||||
close(self->fd);
|
close(self->fd);
|
||||||
}
|
}
|
||||||
self->fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct keymap eek_key_map_from_str(const char *keymap_str) {
|
/// External linkage for Rust.
|
||||||
|
struct KeyMap eek_key_map_from_str(char *keymap_str) {
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
g_error("No context created");
|
g_error("No context created");
|
||||||
@ -83,7 +81,7 @@ static struct keymap eek_key_map_from_str(const char *keymap_str) {
|
|||||||
munmap(ptr, keymap_len);
|
munmap(ptr, keymap_len);
|
||||||
free(xkb_keymap_str);
|
free(xkb_keymap_str);
|
||||||
xkb_keymap_unref(keymap);
|
xkb_keymap_unref(keymap);
|
||||||
struct keymap km = {
|
struct KeyMap km = {
|
||||||
.fd = keymap_fd,
|
.fd = keymap_fd,
|
||||||
.fd_len = keymap_len,
|
.fd_len = keymap_len,
|
||||||
};
|
};
|
||||||
@ -91,7 +89,6 @@ static struct keymap eek_key_map_from_str(const char *keymap_str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void level_keyboard_free(LevelKeyboard *self) {
|
void level_keyboard_free(LevelKeyboard *self) {
|
||||||
eek_key_map_deinit(&self->keymap);
|
|
||||||
squeek_layout_free(self->layout);
|
squeek_layout_free(self->layout);
|
||||||
g_free(self);
|
g_free(self);
|
||||||
}
|
}
|
||||||
@ -104,7 +101,5 @@ level_keyboard_new (struct squeek_layout *layout)
|
|||||||
g_error("Failed to create a keyboard");
|
g_error("Failed to create a keyboard");
|
||||||
}
|
}
|
||||||
keyboard->layout = layout;
|
keyboard->layout = layout;
|
||||||
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
|
|
||||||
keyboard->keymap = eek_key_map_from_str(keymap_str);
|
|
||||||
return keyboard;
|
return keyboard;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,19 +32,20 @@
|
|||||||
|
|
||||||
G_BEGIN_DECLS
|
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
|
/// Keyboard state holder
|
||||||
struct _LevelKeyboard {
|
struct _LevelKeyboard {
|
||||||
struct squeek_layout *layout; // owned
|
struct squeek_layout *layout; // owned
|
||||||
struct keymap keymap; // owned
|
// FIXME: This no longer needs to exist, keymap was folded into layout.
|
||||||
|
guint id; // as a key to layout choices
|
||||||
};
|
};
|
||||||
typedef struct _LevelKeyboard LevelKeyboard;
|
typedef struct _LevelKeyboard LevelKeyboard;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
};
|
||||||
|
|
||||||
gchar *eek_keyboard_get_keymap(LevelKeyboard *keyboard);
|
gchar *eek_keyboard_get_keymap(LevelKeyboard *keyboard);
|
||||||
|
|
||||||
LevelKeyboard*
|
LevelKeyboard*
|
||||||
|
|||||||
@ -159,7 +159,7 @@ eekboard_context_service_use_layout(EekboardContextService *context, struct sque
|
|||||||
// Update the keymap if necessary.
|
// Update the keymap if necessary.
|
||||||
// TODO: Update submission on change event
|
// TODO: Update submission on change event
|
||||||
if (context->submission) {
|
if (context->submission) {
|
||||||
submission_set_keyboard(context->submission, keyboard, timestamp);
|
submission_use_layout(context->submission, keyboard->layout, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
@ -345,7 +345,7 @@ void eekboard_context_service_set_submission(EekboardContextService *context, st
|
|||||||
context->submission = submission;
|
context->submission = submission;
|
||||||
if (context->submission) {
|
if (context->submission) {
|
||||||
uint32_t time = gdk_event_get_time(NULL);
|
uint32_t time = gdk_event_get_time(NULL);
|
||||||
submission_set_keyboard(context->submission, context->keyboard, time);
|
submission_use_layout(context->submission, context->keyboard->layout, time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@ add_project_arguments(
|
|||||||
'-Werror=missing-field-initializers',
|
'-Werror=missing-field-initializers',
|
||||||
'-Werror=incompatible-pointer-types',
|
'-Werror=incompatible-pointer-types',
|
||||||
'-Werror=int-conversion',
|
'-Werror=int-conversion',
|
||||||
'-Werror=redundant-decls',
|
|
||||||
'-Wformat-nonliteral',
|
'-Wformat-nonliteral',
|
||||||
'-Wformat-security',
|
'-Wformat-security',
|
||||||
'-Winit-self',
|
'-Winit-self',
|
||||||
@ -84,7 +83,7 @@ cargo_toml_base = configure_file(
|
|||||||
cargo_deps = files('Cargo.deps')
|
cargo_deps = files('Cargo.deps')
|
||||||
|
|
||||||
if get_option('legacy') == true
|
if get_option('legacy') == true
|
||||||
cargo_build_flags += ['--features', 'gtk_v0_5,gio_v0_5']
|
cargo_build_flags += ['--features', 'gtk_v0_5,gio_v0_5,rustc_less_1_36']
|
||||||
cargo_deps = files('Cargo.deps.legacy')
|
cargo_deps = files('Cargo.deps.legacy')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
45
src/data.rs
45
src/data.rs
@ -18,7 +18,7 @@ use xkbcommon::xkb;
|
|||||||
use ::action;
|
use ::action;
|
||||||
use ::keyboard::{
|
use ::keyboard::{
|
||||||
KeyState, PressType,
|
KeyState, PressType,
|
||||||
generate_keymap, generate_keycodes, FormattingError
|
generate_keymaps, generate_keycodes, KeyCode, FormattingError
|
||||||
};
|
};
|
||||||
use ::layout;
|
use ::layout;
|
||||||
use ::layout::ArrangementKind;
|
use ::layout::ArrangementKind;
|
||||||
@ -382,7 +382,7 @@ impl Layout {
|
|||||||
)
|
)
|
||||||
)}).collect();
|
)}).collect();
|
||||||
|
|
||||||
let symbolmap: HashMap<String, u32> = generate_keycodes(
|
let symbolmap: HashMap<String, KeyCode> = generate_keycodes(
|
||||||
extract_symbol_names(&button_actions)
|
extract_symbol_names(&button_actions)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -391,7 +391,7 @@ impl Layout {
|
|||||||
let keycodes = match &action {
|
let keycodes = match &action {
|
||||||
::action::Action::Submit { text: _, keys } => {
|
::action::Action::Submit { text: _, keys } => {
|
||||||
keys.iter().map(|named_keysym| {
|
keys.iter().map(|named_keysym| {
|
||||||
*symbolmap.get(named_keysym.0.as_str())
|
symbolmap.get(named_keysym.0.as_str())
|
||||||
.expect(
|
.expect(
|
||||||
format!(
|
format!(
|
||||||
"keysym {} in key {} missing from symbol map",
|
"keysym {} in key {} missing from symbol map",
|
||||||
@ -399,11 +399,13 @@ impl Layout {
|
|||||||
name
|
name
|
||||||
).as_str()
|
).as_str()
|
||||||
)
|
)
|
||||||
|
.clone()
|
||||||
}).collect()
|
}).collect()
|
||||||
},
|
},
|
||||||
action::Action::Erase => vec![
|
action::Action::Erase => vec![
|
||||||
*symbolmap.get("BackSpace")
|
symbolmap.get("BackSpace")
|
||||||
.expect(&format!("BackSpace missing from symbol map")),
|
.expect(&format!("BackSpace missing from symbol map"))
|
||||||
|
.clone(),
|
||||||
],
|
],
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
};
|
};
|
||||||
@ -418,7 +420,7 @@ impl Layout {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let keymap_str = match generate_keymap(symbolmap) {
|
let keymaps = match generate_keymaps(symbolmap) {
|
||||||
Err(e) => { return (Err(e), warning_handler) },
|
Err(e) => { return (Err(e), warning_handler) },
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
};
|
};
|
||||||
@ -446,14 +448,14 @@ impl Layout {
|
|||||||
&mut warning_handler,
|
&mut warning_handler,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
layout::Row::new(
|
layout::Row {
|
||||||
add_offsets(
|
buttons: add_offsets(
|
||||||
buttons,
|
buttons,
|
||||||
|button| button.size.width,
|
|button| button.size.width,
|
||||||
).collect()
|
).collect()
|
||||||
)
|
}
|
||||||
});
|
});
|
||||||
let rows = add_offsets(rows, |row| row.get_size().height)
|
let rows = add_offsets(rows, |row| row.get_height())
|
||||||
.collect();
|
.collect();
|
||||||
(
|
(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
@ -471,8 +473,8 @@ impl Layout {
|
|||||||
name,
|
name,
|
||||||
(
|
(
|
||||||
layout::c::Point {
|
layout::c::Point {
|
||||||
x: (total_size.width - view.get_size().width) / 2.0,
|
x: (total_size.width - view.get_width()) / 2.0,
|
||||||
y: (total_size.height - view.get_size().height) / 2.0,
|
y: (total_size.height - view.get_height()) / 2.0,
|
||||||
},
|
},
|
||||||
view,
|
view,
|
||||||
),
|
),
|
||||||
@ -482,10 +484,10 @@ impl Layout {
|
|||||||
(
|
(
|
||||||
Ok(::layout::LayoutData {
|
Ok(::layout::LayoutData {
|
||||||
views: views,
|
views: views,
|
||||||
keymap_str: {
|
keymaps: keymaps.into_iter().map(|keymap_str|
|
||||||
CString::new(keymap_str)
|
CString::new(keymap_str)
|
||||||
.expect("Invalid keymap string generated")
|
.expect("Invalid keymap string generated")
|
||||||
},
|
).collect(),
|
||||||
// FIXME: use a dedicated field
|
// FIXME: use a dedicated field
|
||||||
margins: layout::Margins {
|
margins: layout::Margins {
|
||||||
top: self.margins.top,
|
top: self.margins.top,
|
||||||
@ -722,19 +724,20 @@ fn create_button<H: logging::Handler>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extract_symbol_names<'a>(actions: &'a [(&str, action::Action)])
|
fn extract_symbol_names<'a>(actions: &'a [(&str, action::Action)])
|
||||||
-> impl Iterator<Item=&'a str>
|
-> impl Iterator<Item=String> + 'a
|
||||||
{
|
{
|
||||||
actions.iter()
|
actions.iter()
|
||||||
.filter_map(|(_name, act)| {
|
.filter_map(|(_name, act)| {
|
||||||
match act {
|
match act {
|
||||||
action::Action::Submit {
|
action::Action::Submit {
|
||||||
text: _, keys,
|
text: _, keys,
|
||||||
} => Some(keys),
|
} => Some(keys.clone()),
|
||||||
|
action::Action::Erase => Some(vec!(action::KeySym("BackSpace".into()))),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|named_keysym| named_keysym.0.as_str())
|
.map(|named_keysym| named_keysym.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -827,7 +830,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
out.views["base"].1
|
out.views["base"].1
|
||||||
.get_rows()[0].1
|
.get_rows()[0].1
|
||||||
.get_buttons()[0].1
|
.buttons[0].1
|
||||||
.label,
|
.label,
|
||||||
::layout::Label::Text(CString::new("test").unwrap())
|
::layout::Label::Text(CString::new("test").unwrap())
|
||||||
);
|
);
|
||||||
@ -842,7 +845,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
out.views["base"].1
|
out.views["base"].1
|
||||||
.get_rows()[0].1
|
.get_rows()[0].1
|
||||||
.get_buttons()[0].1
|
.buttons[0].1
|
||||||
.label,
|
.label,
|
||||||
::layout::Label::Text(CString::new("test").unwrap())
|
::layout::Label::Text(CString::new("test").unwrap())
|
||||||
);
|
);
|
||||||
@ -858,7 +861,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
out.views["base"].1
|
out.views["base"].1
|
||||||
.get_rows()[0].1
|
.get_rows()[0].1
|
||||||
.get_buttons()[0].1
|
.buttons[0].1
|
||||||
.state.borrow()
|
.state.borrow()
|
||||||
.keycodes.len(),
|
.keycodes.len(),
|
||||||
2
|
2
|
||||||
@ -986,7 +989,7 @@ mod tests {
|
|||||||
)];
|
)];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extract_symbol_names(&actions[..]).collect::<Vec<_>>(),
|
extract_symbol_names(&actions[..]).collect::<Vec<_>>(),
|
||||||
Vec::<&str>::new(), //"BackSpace"], // TODO: centralize handling of BackSpace
|
vec!["BackSpace"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,7 @@ pub mod c {
|
|||||||
im: *const InputMethod)
|
im: *const InputMethod)
|
||||||
{
|
{
|
||||||
let imservice = check_imservice(imservice, im).unwrap();
|
let imservice = check_imservice(imservice, im).unwrap();
|
||||||
imservice.preedit_string = String::new(); // I don't think this is ever used. Remove it?
|
imservice.preedit_string = String::new();
|
||||||
imservice.pending = IMProtocolState {
|
imservice.pending = IMProtocolState {
|
||||||
active: true,
|
active: true,
|
||||||
..IMProtocolState::default()
|
..IMProtocolState::default()
|
||||||
@ -84,7 +84,7 @@ pub mod c {
|
|||||||
surrounding_text: into_cstring(text)
|
surrounding_text: into_cstring(text)
|
||||||
.expect("Received invalid string")
|
.expect("Received invalid string")
|
||||||
.expect("Received null string"),
|
.expect("Received null string"),
|
||||||
cursor,
|
surrounding_cursor: cursor,
|
||||||
..imservice.pending.clone()
|
..imservice.pending.clone()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -146,6 +146,10 @@ pub mod c {
|
|||||||
let active_changed = imservice.current.active ^ imservice.pending.active;
|
let active_changed = imservice.current.active ^ imservice.pending.active;
|
||||||
|
|
||||||
imservice.current = imservice.pending.clone();
|
imservice.current = imservice.pending.clone();
|
||||||
|
imservice.pending = IMProtocolState {
|
||||||
|
active: imservice.current.active,
|
||||||
|
..IMProtocolState::default()
|
||||||
|
};
|
||||||
|
|
||||||
if active_changed {
|
if active_changed {
|
||||||
if imservice.current.active {
|
if imservice.current.active {
|
||||||
@ -310,7 +314,7 @@ impl TryFrom<u32> for ChangeCause {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct IMProtocolState {
|
struct IMProtocolState {
|
||||||
surrounding_text: CString,
|
surrounding_text: CString,
|
||||||
cursor: u32,
|
surrounding_cursor: u32,
|
||||||
content_purpose: ContentPurpose,
|
content_purpose: ContentPurpose,
|
||||||
content_hint: ContentHint,
|
content_hint: ContentHint,
|
||||||
text_change_cause: ChangeCause,
|
text_change_cause: ChangeCause,
|
||||||
@ -321,7 +325,7 @@ impl Default for IMProtocolState {
|
|||||||
fn default() -> IMProtocolState {
|
fn default() -> IMProtocolState {
|
||||||
IMProtocolState {
|
IMProtocolState {
|
||||||
surrounding_text: CString::default(),
|
surrounding_text: CString::default(),
|
||||||
cursor: 0, // TODO: mark that there's no cursor
|
surrounding_cursor: 0, // TODO: mark that there's no cursor
|
||||||
content_hint: ContentHint::NONE,
|
content_hint: ContentHint::NONE,
|
||||||
content_purpose: ContentPurpose::Normal,
|
content_purpose: ContentPurpose::Normal,
|
||||||
text_change_cause: ChangeCause::InputMethod,
|
text_change_cause: ChangeCause::InputMethod,
|
||||||
@ -340,7 +344,7 @@ pub struct IMService {
|
|||||||
|
|
||||||
pending: IMProtocolState,
|
pending: IMProtocolState,
|
||||||
current: IMProtocolState, // turn current into an idiomatic representation?
|
current: IMProtocolState, // turn current into an idiomatic representation?
|
||||||
preedit_string: String, // I don't think this is ever used. Remove it?
|
preedit_string: String,
|
||||||
serial: Wrapping<u32>,
|
serial: Wrapping<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +366,7 @@ impl IMService {
|
|||||||
state_manager,
|
state_manager,
|
||||||
pending: IMProtocolState::default(),
|
pending: IMProtocolState::default(),
|
||||||
current: IMProtocolState::default(),
|
current: IMProtocolState::default(),
|
||||||
preedit_string: String::new(), // I don't think this is ever used. Remove it?
|
preedit_string: String::new(),
|
||||||
serial: Wrapping(0u32),
|
serial: Wrapping(0u32),
|
||||||
});
|
});
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -374,14 +378,9 @@ impl IMService {
|
|||||||
imservice
|
imservice
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit_string(&mut self, text: &CString) -> Result<(), SubmitError> {
|
pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
|
||||||
match self.current.active {
|
match self.current.active {
|
||||||
true => {
|
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 {
|
unsafe {
|
||||||
c::eek_input_method_commit_string(self.im, text.as_ptr())
|
c::eek_input_method_commit_string(self.im, text.as_ptr())
|
||||||
}
|
}
|
||||||
@ -392,16 +391,11 @@ impl IMService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_surrounding_text(
|
pub fn delete_surrounding_text(
|
||||||
&mut self,
|
&self,
|
||||||
before: u32, after: u32,
|
before: u32, after: u32,
|
||||||
) -> Result<(), SubmitError> {
|
) -> Result<(), SubmitError> {
|
||||||
match self.current.active {
|
match self.current.active {
|
||||||
true => {
|
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 {
|
unsafe {
|
||||||
c::eek_input_method_delete_surrounding_text(
|
c::eek_input_method_delete_surrounding_text(
|
||||||
self.im,
|
self.im,
|
||||||
@ -421,7 +415,6 @@ impl IMService {
|
|||||||
c::eek_input_method_commit(self.im, self.serial.0)
|
c::eek_input_method_commit(self.im, self.serial.0)
|
||||||
}
|
}
|
||||||
self.serial += Wrapping(1u32);
|
self.serial += Wrapping(1u32);
|
||||||
imservice_handle_done(self);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
false => Err(SubmitError::NotActive),
|
false => Err(SubmitError::NotActive),
|
||||||
|
|||||||
140
src/keyboard.rs
140
src/keyboard.rs
@ -5,10 +5,13 @@ use std::cell::RefCell;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
use ::action::Action;
|
use ::action::Action;
|
||||||
|
use ::util;
|
||||||
|
|
||||||
// Traits
|
// Traits
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@ -20,7 +23,12 @@ pub enum PressType {
|
|||||||
Pressed = 1,
|
Pressed = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type KeyCode = u32;
|
/// The extended, unambiguous layout-keycode
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct KeyCode {
|
||||||
|
pub code: u32,
|
||||||
|
pub keymap_idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
bitflags!{
|
bitflags!{
|
||||||
/// Map to `virtual_keyboard.modifiers` modifiers values
|
/// Map to `virtual_keyboard.modifiers` modifiers values
|
||||||
@ -79,10 +87,10 @@ impl KeyState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sorts an iterator by converting it to a Vector and back
|
/// Sorts an iterator by converting it to a Vector and back
|
||||||
fn sorted<'a, I: Iterator<Item=&'a str>>(
|
fn sorted<'a, I: Iterator<Item=String>>(
|
||||||
iter: I
|
iter: I
|
||||||
) -> impl Iterator<Item=&'a str> {
|
) -> impl Iterator<Item=String> {
|
||||||
let mut v: Vec<&'a str> = iter.collect();
|
let mut v: Vec<String> = iter.collect();
|
||||||
v.sort();
|
v.sort();
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
}
|
}
|
||||||
@ -90,15 +98,17 @@ fn sorted<'a, I: Iterator<Item=&'a str>>(
|
|||||||
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
||||||
/// HACK: starting from 9, because 8 results in keycode 0,
|
/// HACK: starting from 9, because 8 results in keycode 0,
|
||||||
/// which the compositor likes to discard
|
/// which the compositor likes to discard
|
||||||
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
|
pub fn generate_keycodes<'a, C: IntoIterator<Item=String>>(
|
||||||
key_names: C,
|
key_names: C,
|
||||||
) -> HashMap<String, u32> {
|
) -> HashMap<String, KeyCode> {
|
||||||
let special_keysyms = ["BackSpace", "Return"].iter().map(|&s| s);
|
|
||||||
HashMap::from_iter(
|
HashMap::from_iter(
|
||||||
// sort to remove a source of indeterminism in keycode assignment
|
// Sort to remove a source of indeterminism in keycode assignment.
|
||||||
sorted(key_names.into_iter().chain(special_keysyms))
|
sorted(key_names.into_iter())
|
||||||
.map(|name| String::from(name))
|
.zip(util::cycle_count(9..255))
|
||||||
.zip(9..)
|
.map(|(name, (code, keymap_idx))| (
|
||||||
|
String::from(name),
|
||||||
|
KeyCode { code, keymap_idx },
|
||||||
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,10 +133,54 @@ impl From<io::Error> for FormattingError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Index is the key code, String is the occupant.
|
||||||
|
/// Starts all empty.
|
||||||
|
/// https://gitlab.freedesktop.org/xorg/xserver/-/issues/260
|
||||||
|
type SingleKeyMap = [Option<String>; 256];
|
||||||
|
|
||||||
|
fn single_key_map_new() -> SingleKeyMap {
|
||||||
|
// Why can't we just initialize arrays without tricks -_- ?
|
||||||
|
unsafe {
|
||||||
|
// Inspired by
|
||||||
|
// https://www.reddit.com/r/rust/comments/5n7bh1/how_to_create_an_array_of_a_type_with_clone_but/
|
||||||
|
#[cfg(feature = "rustc_less_1_36")]
|
||||||
|
let mut array: SingleKeyMap = mem::uninitialized();
|
||||||
|
#[cfg(not(feature = "rustc_less_1_36"))]
|
||||||
|
let mut array: SingleKeyMap = mem::MaybeUninit::uninit().assume_init();
|
||||||
|
|
||||||
|
for element in array.iter_mut() {
|
||||||
|
ptr::write(element, None);
|
||||||
|
}
|
||||||
|
array
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_keymaps(symbolmap: HashMap::<String, KeyCode>)
|
||||||
|
-> Result<Vec<String>, FormattingError>
|
||||||
|
{
|
||||||
|
let mut bins: Vec<SingleKeyMap> = Vec::new();
|
||||||
|
|
||||||
|
for (name, KeyCode { code, keymap_idx }) in symbolmap.into_iter() {
|
||||||
|
if keymap_idx >= bins.len() {
|
||||||
|
bins.resize_with(
|
||||||
|
keymap_idx + 1,
|
||||||
|
|| single_key_map_new(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
bins[keymap_idx][code as usize] = Some(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for bin in bins {
|
||||||
|
out.push(generate_keymap(&bin)?);
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a de-facto single level keymap.
|
/// Generates a de-facto single level keymap.
|
||||||
/// Key codes must not repeat and should remain between 9 and 255.
|
/// Key codes must not repeat and must remain between 9 and 255.
|
||||||
pub fn generate_keymap(
|
fn generate_keymap(
|
||||||
symbolmap: HashMap::<String, KeyCode>,
|
symbolmap: &SingleKeyMap,
|
||||||
) -> Result<String, FormattingError> {
|
) -> Result<String, FormattingError> {
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
writeln!(
|
writeln!(
|
||||||
@ -135,14 +189,21 @@ pub fn generate_keymap(
|
|||||||
|
|
||||||
xkb_keycodes \"squeekboard\" {{
|
xkb_keycodes \"squeekboard\" {{
|
||||||
minimum = 8;
|
minimum = 8;
|
||||||
maximum = 999;"
|
maximum = 255;"
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let pairs: Vec<(&String, usize)> = symbolmap.iter()
|
||||||
|
// Attach a key code to each cell.
|
||||||
|
.enumerate()
|
||||||
|
// Get rid of empty keycodes.
|
||||||
|
.filter_map(|(code, name)| name.as_ref().map(|n| (n, code)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Xorg can only consume up to 255 keys, so this may not work in Xwayland.
|
// Xorg can only consume up to 255 keys, so this may not work in Xwayland.
|
||||||
// Two possible solutions:
|
// Two possible solutions:
|
||||||
// - use levels to cram multiple characters into one key
|
// - use levels to cram multiple characters into one key
|
||||||
// - swap layouts on key presses
|
// - swap layouts on key presses
|
||||||
for keycode in symbolmap.values() {
|
for (_name, keycode) in &pairs {
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"
|
"
|
||||||
@ -161,7 +222,7 @@ pub fn generate_keymap(
|
|||||||
"
|
"
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
for (name, keycode) in symbolmap.iter() {
|
for (name, keycode) in pairs {
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"
|
"
|
||||||
@ -219,13 +280,14 @@ mod tests {
|
|||||||
use xkbcommon::xkb;
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keymap_multi() {
|
fn test_keymap_single_resolve() {
|
||||||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
let mut key_map = single_key_map_new();
|
||||||
|
key_map[9] = Some("a".into());
|
||||||
|
key_map[10] = Some("c".into());
|
||||||
|
|
||||||
let keymap_str = generate_keymap(hashmap!{
|
let keymap_str = generate_keymap(&key_map).unwrap();
|
||||||
"a".into() => 9,
|
|
||||||
"c".into() => 10,
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
}).unwrap();
|
|
||||||
|
|
||||||
let keymap = xkb::Keymap::new_from_string(
|
let keymap = xkb::Keymap::new_from_string(
|
||||||
&context,
|
&context,
|
||||||
@ -239,4 +301,36 @@ mod tests {
|
|||||||
assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
|
assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
|
||||||
assert_eq!(state.key_get_one_sym(10), xkb::KEY_c);
|
assert_eq!(state.key_get_one_sym(10), xkb::KEY_c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keymap_second_resolve() {
|
||||||
|
let keymaps = generate_keymaps(hashmap!(
|
||||||
|
"a".into() => KeyCode { keymap_idx: 1, code: 9 },
|
||||||
|
)).unwrap();
|
||||||
|
|
||||||
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
|
|
||||||
|
let keymap = xkb::Keymap::new_from_string(
|
||||||
|
&context,
|
||||||
|
keymaps[1].clone(), // this index is part of the test
|
||||||
|
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||||
|
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||||
|
).expect("Failed to create keymap");
|
||||||
|
|
||||||
|
let state = xkb::State::new(&keymap);
|
||||||
|
|
||||||
|
assert_eq!(state.key_get_one_sym(9), xkb::KEY_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_symbolmap_overflow() {
|
||||||
|
// The 257th key (U1101) is interesting.
|
||||||
|
// Use Unicode encoding for being able to use in xkb keymaps.
|
||||||
|
let keynames = (0..258).map(|num| format!("U{:04X}", 0x1000 + num));
|
||||||
|
let keycodes = generate_keycodes(keynames);
|
||||||
|
|
||||||
|
// test now
|
||||||
|
let code = keycodes.get("U1101").expect("Did not find the tested keysym");
|
||||||
|
assert_eq!(code.keymap_idx, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,6 @@ struct transformation squeek_layout_calculate_transformation(
|
|||||||
double allocation_width, double allocation_size);
|
double allocation_width, double allocation_size);
|
||||||
|
|
||||||
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
|
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
|
||||||
const char *squeek_layout_get_keymap(const struct squeek_layout*);
|
|
||||||
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
|
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
|
||||||
void squeek_layout_free(struct squeek_layout*);
|
void squeek_layout_free(struct squeek_layout*);
|
||||||
|
|
||||||
|
|||||||
246
src/layout.rs
246
src/layout.rs
@ -237,13 +237,6 @@ pub mod c {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C"
|
|
||||||
fn squeek_layout_get_keymap(layout: *const Layout) -> *const c_char {
|
|
||||||
let layout = unsafe { &*layout };
|
|
||||||
layout.keymap_str.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_layout_get_kind(layout: *const Layout) -> u32 {
|
fn squeek_layout_get_kind(layout: *const Layout) -> u32 {
|
||||||
@ -493,63 +486,38 @@ pub struct Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The graphical representation of a row of buttons
|
/// The graphical representation of a row of buttons
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
/// Buttons together with their offset from the left relative to the row.
|
/// Buttons together with their offset from the left
|
||||||
/// ie. the first button always start at 0.
|
pub buttons: Vec<(f64, Box<Button>)>,
|
||||||
buttons: Vec<(f64, Box<Button>)>,
|
|
||||||
|
|
||||||
/// Total size of the row
|
|
||||||
size: Size,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Row {
|
impl Row {
|
||||||
pub fn new(buttons: Vec<(f64, Box<Button>)>) -> Row {
|
pub fn get_height(&self) -> f64 {
|
||||||
// Make sure buttons are sorted by offset.
|
find_max_double(
|
||||||
debug_assert!({
|
self.buttons.iter(),
|
||||||
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,
|
|(_offset, button)| button.size.height,
|
||||||
);
|
)
|
||||||
|
|
||||||
Row { buttons, size: Size { width, height } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size(&self) -> Size {
|
fn get_width(&self) -> f64 {
|
||||||
self.size.clone()
|
self.buttons.iter().next_back()
|
||||||
}
|
.map(|(x_offset, button)| button.size.width + x_offset)
|
||||||
|
.unwrap_or(0.0)
|
||||||
pub fn get_buttons(&self) -> &Vec<(f64, Box<Button>)> {
|
|
||||||
&self.buttons
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the first button that covers the specified point
|
/// Finds the first button that covers the specified point
|
||||||
/// relative to row's position's origin
|
/// relative to row's position's origin
|
||||||
fn find_button_by_position(&self, x: f64) -> &(f64, Box<Button>)
|
fn find_button_by_position(&self, point: c::Point)
|
||||||
|
-> Option<(f64, &Box<Button>)>
|
||||||
{
|
{
|
||||||
// Buttons are sorted so we can use a binary search to find the clicked
|
self.buttons.iter().find(|(x_offset, button)| {
|
||||||
// button. Note this doesn't check whether the point is actually within
|
c::Bounds {
|
||||||
// a button. This is on purpose as we want a click past the left edge of
|
x: *x_offset, y: 0.0,
|
||||||
// the left-most button to register as a click.
|
width: button.size.width,
|
||||||
let result = self.buttons.binary_search_by(
|
height: button.size.height,
|
||||||
|&(f, _)| f.partial_cmp(&x).unwrap()
|
}.contains(&point)
|
||||||
);
|
})
|
||||||
|
.map(|(x_offset, button)| (*x_offset, button))
|
||||||
let index = result.unwrap_or_else(|r| r);
|
|
||||||
let index = if index > 0 { index - 1 } else { 0 };
|
|
||||||
|
|
||||||
&self.buttons[index]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,85 +528,56 @@ pub struct Spacing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct View {
|
pub struct View {
|
||||||
/// Rows together with their offsets from the top left
|
/// Rows together with their offsets from the top
|
||||||
rows: Vec<(c::Point, Row)>,
|
rows: Vec<(f64, Row)>,
|
||||||
|
|
||||||
/// Total size of the view
|
|
||||||
size: Size,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
pub fn new(rows: Vec<(f64, Row)>) -> View {
|
pub fn new(rows: Vec<(f64, Row)>) -> View {
|
||||||
// Make sure rows are sorted by offset.
|
View { rows }
|
||||||
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
|
/// Finds the first button that covers the specified point
|
||||||
/// relative to view's position's origin
|
/// relative to view's position's origin
|
||||||
fn find_button_by_position(&self, point: c::Point)
|
fn find_button_by_position(&self, point: c::Point)
|
||||||
-> Option<ButtonPlace>
|
-> Option<ButtonPlace>
|
||||||
{
|
{
|
||||||
// Only test bounds of the view here, letting rows/column search extend
|
self.get_rows().iter().find_map(|(row_offset, row)| {
|
||||||
// to the edges of these bounds.
|
// make point relative to the inside of the row
|
||||||
let bounds = c::Bounds {
|
row.find_button_by_position({
|
||||||
x: 0.0,
|
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,
|
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_size(&self) -> Size {
|
pub fn get_width(&self) -> f64 {
|
||||||
self.size.clone()
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns positioned rows, with appropriate x offsets (centered)
|
/// Returns positioned rows, with appropriate x offsets (centered)
|
||||||
pub fn get_rows(&self) -> &Vec<(c::Point, Row)> {
|
pub fn get_rows(&self) -> Vec<(c::Point, &Row)> {
|
||||||
&self.rows
|
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a size which contains all the views
|
/// Returns a size which contains all the views
|
||||||
@ -647,11 +586,11 @@ impl View {
|
|||||||
Size {
|
Size {
|
||||||
height: find_max_double(
|
height: find_max_double(
|
||||||
views.iter(),
|
views.iter(),
|
||||||
|view| view.size.height,
|
|view| view.get_height(),
|
||||||
),
|
),
|
||||||
width: find_max_double(
|
width: find_max_double(
|
||||||
views.iter(),
|
views.iter(),
|
||||||
|view| view.size.width,
|
|view| view.get_width(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -686,8 +625,8 @@ pub struct Layout {
|
|||||||
pub views: HashMap<String, (c::Point, View)>,
|
pub views: HashMap<String, (c::Point, View)>,
|
||||||
|
|
||||||
// Non-UI stuff
|
// Non-UI stuff
|
||||||
/// xkb keymap applicable to the contained keys. Unchangeable
|
/// xkb keymaps applicable to the contained keys. Unchangeable
|
||||||
pub keymap_str: CString,
|
pub keymaps: Vec<CString>,
|
||||||
// Changeable state
|
// Changeable state
|
||||||
// a Vec would be enough, but who cares, this will be small & fast enough
|
// a Vec would be enough, but who cares, this will be small & fast enough
|
||||||
// TODO: turn those into per-input point *_buttons to track dragging.
|
// TODO: turn those into per-input point *_buttons to track dragging.
|
||||||
@ -703,7 +642,7 @@ pub struct Layout {
|
|||||||
pub struct LayoutData {
|
pub struct LayoutData {
|
||||||
/// Point is the offset within layout
|
/// Point is the offset within layout
|
||||||
pub views: HashMap<String, (c::Point, View)>,
|
pub views: HashMap<String, (c::Point, View)>,
|
||||||
pub keymap_str: CString,
|
pub keymaps: Vec<CString>,
|
||||||
pub margins: Margins,
|
pub margins: Margins,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +665,7 @@ impl Layout {
|
|||||||
kind,
|
kind,
|
||||||
current_view: "base".to_owned(),
|
current_view: "base".to_owned(),
|
||||||
views: data.views,
|
views: data.views,
|
||||||
keymap_str: data.keymap_str,
|
keymaps: data.keymaps,
|
||||||
pressed_keys: HashSet::new(),
|
pressed_keys: HashSet::new(),
|
||||||
margins: data.margins,
|
margins: data.margins,
|
||||||
}
|
}
|
||||||
@ -799,7 +738,7 @@ impl Layout {
|
|||||||
where F: FnMut(c::Point, &Box<Button>)
|
where F: FnMut(c::Point, &Box<Button>)
|
||||||
{
|
{
|
||||||
let (view_offset, view) = self.get_current_view_position();
|
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 {
|
for (x_offset, button) in &row.buttons {
|
||||||
let offset = view_offset
|
let offset = view_offset
|
||||||
+ row_offset.clone()
|
+ row_offset.clone()
|
||||||
@ -812,7 +751,7 @@ impl Layout {
|
|||||||
pub fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
|
pub fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
let view = self.get_current_view();
|
let view = self.get_current_view();
|
||||||
for (_, row) in view.get_rows() {
|
for (_, row) in &view.get_rows() {
|
||||||
for (_, button) in &row.buttons {
|
for (_, button) in &row.buttons {
|
||||||
let locked = {
|
let locked = {
|
||||||
let state = RefCell::borrow(&button.state).clone();
|
let state = RefCell::borrow(&button.state).clone();
|
||||||
@ -874,9 +813,13 @@ mod procedures {
|
|||||||
let button = make_button_with_state("1".into(), state);
|
let button = make_button_with_state("1".into(), state);
|
||||||
let button_ptr = as_ptr(&button);
|
let button_ptr = as_ptr(&button);
|
||||||
|
|
||||||
let row = Row::new(vec!((0.1, button)));
|
let row = Row {
|
||||||
|
buttons: vec!((0.1, button)),
|
||||||
|
};
|
||||||
|
|
||||||
let view = View::new(vec!((1.2, row)));
|
let view = View {
|
||||||
|
rows: vec!((1.2, row)),
|
||||||
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
find_key_places(&view, &state_clone.clone()).into_iter()
|
find_key_places(&view, &state_clone.clone()).into_iter()
|
||||||
@ -887,7 +830,9 @@ mod procedures {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let view = View::new(vec![]);
|
let view = View {
|
||||||
|
rows: Vec::new(),
|
||||||
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
find_key_places(&view, &state_clone.clone()).is_empty(),
|
find_key_places(&view, &state_clone.clone()).is_empty(),
|
||||||
true
|
true
|
||||||
@ -1130,56 +1075,37 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_centering() {
|
fn check_centering() {
|
||||||
// A B
|
// foo
|
||||||
// ---bar---
|
// ---bar---
|
||||||
let view = View::new(vec![
|
let view = View::new(vec![
|
||||||
(
|
(
|
||||||
0.0,
|
0.0,
|
||||||
Row::new(vec![
|
Row {
|
||||||
(
|
buttons: vec![(
|
||||||
0.0,
|
0.0,
|
||||||
Box::new(Button {
|
Box::new(Button {
|
||||||
size: Size { width: 5.0, height: 10.0 },
|
size: Size { width: 10.0, height: 10.0 },
|
||||||
..*make_button_with_state("A".into(), make_state())
|
..*make_button_with_state("foo".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,
|
10.0,
|
||||||
Row::new(vec![
|
Row {
|
||||||
(
|
buttons: vec![(
|
||||||
0.0,
|
0.0,
|
||||||
Box::new(Button {
|
Box::new(Button {
|
||||||
size: Size { width: 30.0, height: 10.0 },
|
size: Size { width: 30.0, height: 10.0 },
|
||||||
..*make_button_with_state("bar".into(), make_state())
|
..*make_button_with_state("bar".into(), make_state())
|
||||||
}),
|
}),
|
||||||
),
|
)]
|
||||||
]),
|
},
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
assert!(
|
assert!(
|
||||||
view.find_button_by_position(c::Point { x: 5.0, y: 5.0 })
|
view.find_button_by_position(c::Point { x: 5.0, y: 5.0 })
|
||||||
.unwrap().button.name.to_str().unwrap() == "A"
|
.is_none()
|
||||||
);
|
|
||||||
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"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1189,18 +1115,20 @@ mod test {
|
|||||||
let view = View::new(vec![
|
let view = View::new(vec![
|
||||||
(
|
(
|
||||||
0.0,
|
0.0,
|
||||||
Row::new(vec![(
|
Row {
|
||||||
|
buttons: vec![(
|
||||||
0.0,
|
0.0,
|
||||||
Box::new(Button {
|
Box::new(Button {
|
||||||
size: Size { width: 1.0, height: 1.0 },
|
size: Size { width: 1.0, height: 1.0 },
|
||||||
..*make_button_with_state("foo".into(), make_state())
|
..*make_button_with_state("foo".into(), make_state())
|
||||||
}),
|
}),
|
||||||
)]),
|
)]
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
let layout = Layout {
|
let layout = Layout {
|
||||||
current_view: String::new(),
|
current_view: String::new(),
|
||||||
keymap_str: CString::new("").unwrap(),
|
keymaps: Vec::new(),
|
||||||
kind: ArrangementKind::Base,
|
kind: ArrangementKind::Base,
|
||||||
pressed_keys: HashSet::new(),
|
pressed_keys: HashSet::new(),
|
||||||
// Lots of bottom margin
|
// Lots of bottom margin
|
||||||
|
|||||||
@ -14,7 +14,6 @@ sources = [
|
|||||||
config_h,
|
config_h,
|
||||||
'dbus.c',
|
'dbus.c',
|
||||||
'imservice.c',
|
'imservice.c',
|
||||||
'popover.c',
|
|
||||||
'server-context-service.c',
|
'server-context-service.c',
|
||||||
'wayland.c',
|
'wayland.c',
|
||||||
'../eek/eek.c',
|
'../eek/eek.c',
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
#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));
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -3,7 +3,6 @@
|
|||||||
use gio;
|
use gio;
|
||||||
use gtk;
|
use gtk;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::cmp::Ordering;
|
|
||||||
use ::layout::c::{ Bounds, EekGtkKeyboard };
|
use ::layout::c::{ Bounds, EekGtkKeyboard };
|
||||||
use ::locale;
|
use ::locale;
|
||||||
use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
|
use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
|
||||||
@ -26,15 +25,6 @@ use gtk::WidgetExt;
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use ::logging::Warn;
|
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 {
|
mod variants {
|
||||||
use glib;
|
use glib;
|
||||||
use glib::Variant;
|
use glib::Variant;
|
||||||
@ -138,12 +128,6 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
|
|||||||
xml,
|
xml,
|
||||||
"
|
"
|
||||||
</section>
|
</section>
|
||||||
<section>
|
|
||||||
<item>
|
|
||||||
<attribute name=\"label\" translatable=\"yes\">Keyboard Settings</attribute>
|
|
||||||
<attribute name=\"action\">settings</attribute>
|
|
||||||
</item>
|
|
||||||
</section>
|
|
||||||
</menu>
|
</menu>
|
||||||
</interface>"
|
</interface>"
|
||||||
).unwrap();
|
).unwrap();
|
||||||
@ -350,13 +334,8 @@ pub fn show(
|
|||||||
.zip(all_layouts.clone().into_iter())
|
.zip(all_layouts.clone().into_iter())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
human_names.sort_unstable_by(|(tr_a, layout_a), (tr_b, layout_b)| {
|
human_names.sort_unstable_by(|(tr_a, _), (tr_b, _)| {
|
||||||
// Sort first by layout then name
|
compare_current_locale(&tr_a.0, &tr_b.0)
|
||||||
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,
|
// GVariant doesn't natively support `enum`s,
|
||||||
@ -435,14 +414,8 @@ pub fn show(
|
|||||||
menu_inner.popdown();
|
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();
|
let action_group = gio::SimpleActionGroup::new();
|
||||||
action_group.add_action(&layout_action);
|
action_group.add_action(&layout_action);
|
||||||
action_group.add_action(&settings_action);
|
|
||||||
|
|
||||||
menu.insert_action_group("popup", Some(&action_group));
|
menu.insert_action_group("popup", Some(&action_group));
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,13 +18,11 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
|
|||||||
("br", include_str!("../data/keyboards/br.yaml")),
|
("br", include_str!("../data/keyboards/br.yaml")),
|
||||||
("de", include_str!("../data/keyboards/de.yaml")),
|
("de", include_str!("../data/keyboards/de.yaml")),
|
||||||
("be", include_str!("../data/keyboards/be.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")),
|
("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
|
||||||
("dk", include_str!("../data/keyboards/dk.yaml")),
|
("dk", include_str!("../data/keyboards/dk.yaml")),
|
||||||
("es", include_str!("../data/keyboards/es.yaml")),
|
("es", include_str!("../data/keyboards/es.yaml")),
|
||||||
("fi", include_str!("../data/keyboards/fi.yaml")),
|
("fi", include_str!("../data/keyboards/fi.yaml")),
|
||||||
("fr", include_str!("../data/keyboards/fr.yaml")),
|
("fr", include_str!("../data/keyboards/fr.yaml")),
|
||||||
("fr_wide", include_str!("../data/keyboards/fr_wide.yaml")),
|
|
||||||
("gr", include_str!("../data/keyboards/gr.yaml")),
|
("gr", include_str!("../data/keyboards/gr.yaml")),
|
||||||
("it", include_str!("../data/keyboards/it.yaml")),
|
("it", include_str!("../data/keyboards/it.yaml")),
|
||||||
("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
|
("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
|
||||||
@ -38,7 +36,6 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
|
|||||||
("ua", include_str!("../data/keyboards/ua.yaml")),
|
("ua", include_str!("../data/keyboards/ua.yaml")),
|
||||||
// layout+overlay
|
// layout+overlay
|
||||||
("terminal", include_str!("../data/keyboards/terminal.yaml")),
|
("terminal", include_str!("../data/keyboards/terminal.yaml")),
|
||||||
("terminal_wide", include_str!("../data/keyboards/terminal_wide.yaml")),
|
|
||||||
// Overlays
|
// Overlays
|
||||||
("emoji", include_str!("../data/keyboards/emoji.yaml")),
|
("emoji", include_str!("../data/keyboards/emoji.yaml")),
|
||||||
];
|
];
|
||||||
|
|||||||
@ -85,20 +85,13 @@ on_notify_unmap (ServerContextService *self, GtkWidget *widget)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
calculate_height(int32_t width, GdkRectangle *geometry)
|
calculate_height(int32_t width)
|
||||||
{
|
{
|
||||||
uint32_t height;
|
uint32_t height = 180;
|
||||||
if (geometry->width > geometry->height) {
|
if (width < 360 && width > 0) {
|
||||||
// 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
|
height = ((unsigned)width * 7 / 12); // to match 360×210
|
||||||
} else {
|
} else if (width < 540) {
|
||||||
// Here we switch to wide layout, less height needed
|
height = 180 + (540 - (unsigned)width) * 30 / 180; // smooth transition
|
||||||
height = ((unsigned)width * 7 / 22);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
@ -106,10 +99,6 @@ calculate_height(int32_t width, GdkRectangle *geometry)
|
|||||||
static void
|
static void
|
||||||
on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
|
on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
|
||||||
{
|
{
|
||||||
GdkDisplay *display = NULL;
|
|
||||||
GdkWindow *window = NULL;
|
|
||||||
GdkMonitor *monitor = NULL;
|
|
||||||
GdkRectangle geometry;
|
|
||||||
gint width;
|
gint width;
|
||||||
gint height;
|
gint height;
|
||||||
|
|
||||||
@ -121,24 +110,11 @@ on_surface_configure(ServerContextService *self, PhoshLayerSurface *surface)
|
|||||||
"configured-height", &height,
|
"configured-height", &height,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
// In order to improve height calculation, we need the monitor geometry so
|
|
||||||
// we can use different algorithms for portrait and landscape mode.
|
|
||||||
// Note: this is a temporary fix until the size manager is complete.
|
|
||||||
display = gdk_display_get_default ();
|
|
||||||
if (display)
|
|
||||||
window = gtk_widget_get_window (GTK_WIDGET (surface));
|
|
||||||
if (window)
|
|
||||||
monitor = gdk_display_get_monitor_at_window (display, window);
|
|
||||||
if (monitor)
|
|
||||||
gdk_monitor_get_geometry (monitor, &geometry);
|
|
||||||
else
|
|
||||||
geometry.width = geometry.height = 0;
|
|
||||||
|
|
||||||
// When the geometry event comes after surface.configure,
|
// When the geometry event comes after surface.configure,
|
||||||
// this entire height calculation does nothing.
|
// this entire height calculation does nothing.
|
||||||
// guint desired_height = squeek_uiman_get_perceptual_height(context->manager);
|
// guint desired_height = squeek_uiman_get_perceptual_height(context->manager);
|
||||||
// Temporarily use old method, until the size manager is complete.
|
// Temporarily use old method, until the size manager is complete.
|
||||||
guint desired_height = calculate_height(width, &geometry);
|
guint desired_height = calculate_height(width);
|
||||||
|
|
||||||
guint configured_height = (guint)height;
|
guint configured_height = (guint)height;
|
||||||
// if height was already requested once but a different one was given
|
// if height was already requested once but a different one was given
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "eek/eek-types.h"
|
#include "eek/eek-types.h"
|
||||||
|
|
||||||
struct submission;
|
struct submission;
|
||||||
|
struct squeek_layout;
|
||||||
|
|
||||||
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
|
struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
|
||||||
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
|
struct zwp_virtual_keyboard_manager_v1 *vkmanager,
|
||||||
@ -15,5 +16,5 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
|
|||||||
// Defined in Rust
|
// Defined in Rust
|
||||||
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state);
|
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_ui(struct submission *self, ServerContextService *ui_context);
|
||||||
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard, uint32_t time);
|
void submission_use_layout(struct submission *self, struct squeek_layout *layout, uint32_t time);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -23,8 +23,9 @@ use ::action::Modifier;
|
|||||||
use ::imservice;
|
use ::imservice;
|
||||||
use ::imservice::IMService;
|
use ::imservice::IMService;
|
||||||
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
|
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
|
||||||
use ::layout::c::LevelKeyboard;
|
use ::layout;
|
||||||
use ::util::vec_remove;
|
use ::util::vec_remove;
|
||||||
|
use ::vkeyboard;
|
||||||
use ::vkeyboard::VirtualKeyboard;
|
use ::vkeyboard::VirtualKeyboard;
|
||||||
|
|
||||||
// traits
|
// traits
|
||||||
@ -68,6 +69,8 @@ pub mod c {
|
|||||||
modifiers_active: Vec::new(),
|
modifiers_active: Vec::new(),
|
||||||
virtual_keyboard: VirtualKeyboard(vk),
|
virtual_keyboard: VirtualKeyboard(vk),
|
||||||
pressed: Vec::new(),
|
pressed: Vec::new(),
|
||||||
|
keymap_fds: Vec::new(),
|
||||||
|
keymap_idx: None,
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -91,16 +94,17 @@ pub mod c {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn submission_set_keyboard(
|
fn submission_use_layout(
|
||||||
submission: *mut Submission,
|
submission: *mut Submission,
|
||||||
keyboard: LevelKeyboard,
|
layout: *const layout::Layout,
|
||||||
time: u32,
|
time: u32,
|
||||||
) {
|
) {
|
||||||
if submission.is_null() {
|
if submission.is_null() {
|
||||||
panic!("Null submission pointer");
|
panic!("Null submission pointer");
|
||||||
}
|
}
|
||||||
let submission: &mut Submission = unsafe { &mut *submission };
|
let submission: &mut Submission = unsafe { &mut *submission };
|
||||||
submission.update_keymap(keyboard, Timestamp(time));
|
let layout = unsafe { &*layout };
|
||||||
|
submission.use_layout(layout, Timestamp(time));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +123,8 @@ pub struct Submission {
|
|||||||
virtual_keyboard: VirtualKeyboard,
|
virtual_keyboard: VirtualKeyboard,
|
||||||
modifiers_active: Vec<(KeyStateId, Modifier)>,
|
modifiers_active: Vec<(KeyStateId, Modifier)>,
|
||||||
pressed: Vec<(KeyStateId, SubmittedAction)>,
|
pressed: Vec<(KeyStateId, SubmittedAction)>,
|
||||||
|
keymap_fds: Vec<vkeyboard::c::KeyMap>,
|
||||||
|
keymap_idx: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SubmitData<'a> {
|
pub enum SubmitData<'a> {
|
||||||
@ -177,11 +183,34 @@ impl Submission {
|
|||||||
let submit_action = match was_committed_as_text {
|
let submit_action = match was_committed_as_text {
|
||||||
true => SubmittedAction::IMService,
|
true => SubmittedAction::IMService,
|
||||||
false => {
|
false => {
|
||||||
|
let keycodes_count = keycodes.len();
|
||||||
|
for keycode in keycodes.iter() {
|
||||||
|
self.select_keymap(keycode.keymap_idx, time);
|
||||||
|
let keycode = keycode.code;
|
||||||
|
match keycodes_count {
|
||||||
|
// Pressing a key made out of a single keycode is simple:
|
||||||
|
// press on press, release on release.
|
||||||
|
1 => self.virtual_keyboard.switch(
|
||||||
|
keycode,
|
||||||
|
PressType::Pressed,
|
||||||
|
time,
|
||||||
|
),
|
||||||
|
// A key made of multiple keycodes
|
||||||
|
// has to submit them one after the other.
|
||||||
|
_ => {
|
||||||
self.virtual_keyboard.switch(
|
self.virtual_keyboard.switch(
|
||||||
keycodes,
|
keycode.clone(),
|
||||||
PressType::Pressed,
|
PressType::Pressed,
|
||||||
time,
|
time,
|
||||||
);
|
);
|
||||||
|
self.virtual_keyboard.switch(
|
||||||
|
keycode.clone(),
|
||||||
|
PressType::Released,
|
||||||
|
time,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
SubmittedAction::VirtualKeyboard(keycodes.clone())
|
SubmittedAction::VirtualKeyboard(keycodes.clone())
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -199,11 +228,21 @@ impl Submission {
|
|||||||
// no matter if the imservice got activated,
|
// no matter if the imservice got activated,
|
||||||
// keys must be released
|
// keys must be released
|
||||||
SubmittedAction::VirtualKeyboard(keycodes) => {
|
SubmittedAction::VirtualKeyboard(keycodes) => {
|
||||||
|
let keycodes_count = keycodes.len();
|
||||||
|
match keycodes_count {
|
||||||
|
1 => {
|
||||||
|
let keycode = &keycodes[0];
|
||||||
|
self.select_keymap(keycode.keymap_idx, time);
|
||||||
self.virtual_keyboard.switch(
|
self.virtual_keyboard.switch(
|
||||||
&keycodes,
|
keycode.code,
|
||||||
PressType::Released,
|
PressType::Released,
|
||||||
time,
|
time,
|
||||||
)
|
);
|
||||||
|
},
|
||||||
|
// Design choice here: submit multiple all at press time
|
||||||
|
// and do nothing at release time.
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -274,6 +313,7 @@ impl Submission {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Changes keymap and clears pressed keys and modifiers.
|
/// Changes keymap and clears pressed keys and modifiers.
|
||||||
///
|
///
|
||||||
/// It's not obvious if clearing is the right thing to do,
|
/// It's not obvious if clearing is the right thing to do,
|
||||||
@ -283,9 +323,28 @@ impl Submission {
|
|||||||
/// Alternatively, modifiers could be restored on the new keymap.
|
/// Alternatively, modifiers could be restored on the new keymap.
|
||||||
/// That approach might be difficult
|
/// That approach might be difficult
|
||||||
/// due to modifiers meaning different things in different keymaps.
|
/// due to modifiers meaning different things in different keymaps.
|
||||||
pub fn update_keymap(&mut self, keyboard: LevelKeyboard, time: Timestamp) {
|
fn select_keymap(&mut self, idx: usize, time: Timestamp) {
|
||||||
|
if self.keymap_idx != Some(idx) {
|
||||||
|
self.keymap_idx = Some(idx);
|
||||||
self.clear_all_modifiers();
|
self.clear_all_modifiers();
|
||||||
self.release_all_virtual_keys(time);
|
self.release_all_virtual_keys(time);
|
||||||
self.virtual_keyboard.update_keymap(keyboard);
|
let keymap = &self.keymap_fds[idx];
|
||||||
|
self.virtual_keyboard.update_keymap(keymap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn use_layout(&mut self, layout: &layout::Layout, time: Timestamp) {
|
||||||
|
self.keymap_fds = layout.keymaps.iter()
|
||||||
|
.map(|keymap_str| vkeyboard::c::KeyMap::from_cstr(
|
||||||
|
keymap_str.as_c_str()
|
||||||
|
))
|
||||||
|
.collect();
|
||||||
|
self.keymap_idx = None;
|
||||||
|
|
||||||
|
// This can probably be eliminated,
|
||||||
|
// because key presses can trigger an update anyway.
|
||||||
|
// However, self.keymap_idx needs to become Option<>
|
||||||
|
// in order to force update on new layouts.
|
||||||
|
self.select_keymap(0, time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/tests.rs
58
src/tests.rs
@ -40,21 +40,29 @@ pub fn check_layout_file(path: &str) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_sym_presence(
|
fn check_sym_in_keymap(state: &xkb::State, sym_name: &str) -> bool {
|
||||||
state: &xkb::State,
|
|
||||||
sym_name: &str,
|
|
||||||
handler: &mut dyn logging::Handler,
|
|
||||||
) {
|
|
||||||
let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
|
let sym = xkb::keysym_from_name(sym_name, xkb::KEYSYM_NO_FLAGS);
|
||||||
if sym == xkb::KEY_NoSymbol {
|
if sym == xkb::KEY_NoSymbol {
|
||||||
panic!(format!("Entered invalid keysym: {}", sym_name));
|
panic!(format!("Entered invalid keysym: {}", sym_name));
|
||||||
}
|
}
|
||||||
let map = state.get_keymap();
|
let map = state.get_keymap();
|
||||||
let range = map.min_keycode()..=map.max_keycode();
|
let range = map.min_keycode()..=map.max_keycode();
|
||||||
let found = range.flat_map(|code| state.key_get_syms(code))
|
range.flat_map(|code| state.key_get_syms(code))
|
||||||
.find(|s| **s == sym)
|
.find(|s| **s == sym)
|
||||||
.is_some();
|
.is_some()
|
||||||
if !found {
|
}
|
||||||
|
|
||||||
|
fn check_sym_presence(
|
||||||
|
states: &[xkb::State],
|
||||||
|
sym_name: &str,
|
||||||
|
handler: &mut dyn logging::Handler,
|
||||||
|
) {
|
||||||
|
let found = states.iter()
|
||||||
|
.position(|state| {
|
||||||
|
check_sym_in_keymap(&state, sym_name)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let None = found {
|
||||||
handler.handle(
|
handler.handle(
|
||||||
logging::Level::Surprise,
|
logging::Level::Surprise,
|
||||||
&format!("There's no way to input the keysym {} on this layout", sym_name),
|
&format!("There's no way to input the keysym {} on this layout", sym_name),
|
||||||
@ -72,25 +80,26 @@ fn check_layout(layout: Layout, allow_missing_return: bool) {
|
|||||||
|
|
||||||
let layout = layout.expect("layout broken");
|
let layout = layout.expect("layout broken");
|
||||||
|
|
||||||
|
let xkb_states: Vec<xkb::State> = layout.keymaps.iter()
|
||||||
|
.map(|keymap_str| {
|
||||||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
|
let keymap_str = keymap_str
|
||||||
let keymap_str = layout.keymap_str
|
|
||||||
.clone()
|
.clone()
|
||||||
.into_string().expect("Failed to decode keymap string");
|
.into_string().expect("Failed to decode keymap string");
|
||||||
|
|
||||||
let keymap = xkb::Keymap::new_from_string(
|
let keymap = xkb::Keymap::new_from_string(
|
||||||
&context,
|
&context,
|
||||||
keymap_str.clone(),
|
keymap_str.clone(),
|
||||||
xkb::KEYMAP_FORMAT_TEXT_V1,
|
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||||
).expect("Failed to create keymap");
|
).expect("Failed to create keymap");
|
||||||
|
xkb::State::new(&keymap)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let state = xkb::State::new(&keymap);
|
check_sym_presence(&xkb_states, "BackSpace", &mut handler);
|
||||||
|
|
||||||
check_sym_presence(&state, "BackSpace", &mut handler);
|
|
||||||
let mut printer = logging::Print;
|
let mut printer = logging::Print;
|
||||||
check_sym_presence(
|
check_sym_presence(
|
||||||
&state,
|
&xkb_states,
|
||||||
"Return",
|
"Return",
|
||||||
if allow_missing_return { &mut printer }
|
if allow_missing_return { &mut printer }
|
||||||
else { &mut handler },
|
else { &mut handler },
|
||||||
@ -98,14 +107,23 @@ fn check_layout(layout: Layout, allow_missing_return: bool) {
|
|||||||
|
|
||||||
// "Press" each button with keysyms
|
// "Press" each button with keysyms
|
||||||
for (_pos, view) in layout.views.values() {
|
for (_pos, view) in layout.views.values() {
|
||||||
for (_y, row) in view.get_rows() {
|
for (_y, row) in &view.get_rows() {
|
||||||
for (_x, button) in row.get_buttons() {
|
for (_x, button) in &row.buttons {
|
||||||
let keystate = button.state.borrow();
|
let keystate = button.state.borrow();
|
||||||
for keycode in &keystate.keycodes {
|
for keycode in &keystate.keycodes {
|
||||||
match state.key_get_one_sym(*keycode) {
|
match xkb_states[keycode.keymap_idx].key_get_one_sym(keycode.code) {
|
||||||
xkb::KEY_NoSymbol => {
|
xkb::KEY_NoSymbol => {
|
||||||
eprintln!("{}", keymap_str);
|
eprintln!(
|
||||||
panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name);
|
"keymap {}: {}",
|
||||||
|
keycode.keymap_idx,
|
||||||
|
layout.keymaps[keycode.keymap_idx].to_str().unwrap(),
|
||||||
|
);
|
||||||
|
panic!(
|
||||||
|
"Keysym for code {:?} on key {} ({:?}) can't be resolved",
|
||||||
|
keycode,
|
||||||
|
button.name.to_string_lossy(),
|
||||||
|
button.name,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/util.rs
25
src/util.rs
@ -203,6 +203,23 @@ pub fn vec_remove<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, pred: F) -> Option<T>
|
|||||||
idx.map(|idx| v.remove(idx))
|
idx.map(|idx| v.remove(idx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Repeats all the items of the iterator forever,
|
||||||
|
/// but returns the cycle number alongside.
|
||||||
|
/// Inefficient due to all the vectors, but doesn't have to be fast.
|
||||||
|
pub fn cycle_count<T, I: Clone + Iterator<Item=T>>(iter: I)
|
||||||
|
-> impl Iterator<Item=(T, usize)>
|
||||||
|
{
|
||||||
|
let numbered_copies = vec![iter].into_iter()
|
||||||
|
.cycle()
|
||||||
|
.enumerate();
|
||||||
|
numbered_copies.flat_map(|(idx, cycle)|
|
||||||
|
// Pair each element from the cycle with a copy of the index.
|
||||||
|
cycle.zip(
|
||||||
|
vec![idx].into_iter().cycle() // Repeat the index forever.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -217,4 +234,12 @@ mod tests {
|
|||||||
assert_eq!(s.insert(Pointer(Rc::new(2u32))), true);
|
assert_eq!(s.insert(Pointer(Rc::new(2u32))), true);
|
||||||
assert_eq!(s.remove(&Pointer(first)), true);
|
assert_eq!(s.remove(&Pointer(first)), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_count() {
|
||||||
|
assert_eq!(
|
||||||
|
cycle_count(5..8).take(7).collect::<Vec<_>>(),
|
||||||
|
vec![(5, 0), (6, 0), (7, 0), (5, 1), (6, 1), (7, 1), (5, 2)]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,42 @@
|
|||||||
/*! Managing the events belonging to virtual-keyboard interface. */
|
/*! Managing the events belonging to virtual-keyboard interface. */
|
||||||
|
|
||||||
use ::keyboard::{ KeyCode, Modifiers, PressType };
|
use ::keyboard::{ Modifiers, PressType };
|
||||||
use ::layout::c::LevelKeyboard;
|
|
||||||
use ::submission::Timestamp;
|
use ::submission::Timestamp;
|
||||||
|
|
||||||
|
/// Standard xkb keycode
|
||||||
|
type KeyCode = u32;
|
||||||
|
|
||||||
/// Gathers stuff defined in C or called by C
|
/// Gathers stuff defined in C or called by C
|
||||||
pub mod c {
|
pub mod c {
|
||||||
use super::*;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::{ c_char, c_void };
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct ZwpVirtualKeyboardV1(*const c_void);
|
pub struct ZwpVirtualKeyboardV1(*const c_void);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct KeyMap {
|
||||||
|
fd: u32,
|
||||||
|
fd_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyMap {
|
||||||
|
pub fn from_cstr(s: &CStr) -> KeyMap {
|
||||||
|
unsafe {
|
||||||
|
eek_key_map_from_str(s.as_ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for KeyMap {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
eek_key_map_deinit(self as *mut KeyMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn eek_virtual_keyboard_v1_key(
|
pub fn eek_virtual_keyboard_v1_key(
|
||||||
@ -24,13 +48,16 @@ pub mod c {
|
|||||||
|
|
||||||
pub fn eek_virtual_keyboard_update_keymap(
|
pub fn eek_virtual_keyboard_update_keymap(
|
||||||
virtual_keyboard: ZwpVirtualKeyboardV1,
|
virtual_keyboard: ZwpVirtualKeyboardV1,
|
||||||
keyboard: LevelKeyboard,
|
keymap: *const KeyMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn eek_virtual_keyboard_set_modifiers(
|
pub fn eek_virtual_keyboard_set_modifiers(
|
||||||
virtual_keyboard: ZwpVirtualKeyboardV1,
|
virtual_keyboard: ZwpVirtualKeyboardV1,
|
||||||
modifiers: u32,
|
modifiers: u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pub fn eek_key_map_from_str(keymap_str: *const c_char) -> KeyMap;
|
||||||
|
pub fn eek_key_map_deinit(keymap: *mut KeyMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,35 +68,15 @@ impl VirtualKeyboard {
|
|||||||
// TODO: error out if keymap not set
|
// TODO: error out if keymap not set
|
||||||
pub fn switch(
|
pub fn switch(
|
||||||
&self,
|
&self,
|
||||||
keycodes: &[KeyCode],
|
keycode: KeyCode,
|
||||||
action: PressType,
|
action: PressType,
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
) {
|
) {
|
||||||
let keycodes_count = keycodes.len();
|
|
||||||
for keycode in keycodes.iter() {
|
|
||||||
let keycode = keycode - 8;
|
let keycode = keycode - 8;
|
||||||
match (action, keycodes_count) {
|
unsafe {
|
||||||
// Pressing a key made out of a single keycode is simple:
|
|
||||||
// press on press, release on release.
|
|
||||||
(_, 1) => unsafe {
|
|
||||||
c::eek_virtual_keyboard_v1_key(
|
c::eek_virtual_keyboard_v1_key(
|
||||||
self.0, timestamp.0, keycode, action.clone() as u32
|
self.0, timestamp.0, keycode, action.clone() as u32
|
||||||
);
|
);
|
||||||
},
|
|
||||||
// A key made of multiple keycodes
|
|
||||||
// has to submit them one after the other
|
|
||||||
(PressType::Pressed, _) => unsafe {
|
|
||||||
c::eek_virtual_keyboard_v1_key(
|
|
||||||
self.0, timestamp.0, keycode, PressType::Pressed as u32
|
|
||||||
);
|
|
||||||
c::eek_virtual_keyboard_v1_key(
|
|
||||||
self.0, timestamp.0, keycode, PressType::Released as u32
|
|
||||||
);
|
|
||||||
},
|
|
||||||
// Design choice here: submit multiple all at press time
|
|
||||||
// and do nothing at release time
|
|
||||||
(PressType::Released, _) => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,9 +87,12 @@ impl VirtualKeyboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_keymap(&self, keyboard: LevelKeyboard) {
|
pub fn update_keymap(&self, keymap: &c::KeyMap) {
|
||||||
unsafe {
|
unsafe {
|
||||||
c::eek_virtual_keyboard_update_keymap(self.0, keyboard);
|
c::eek_virtual_keyboard_update_keymap(
|
||||||
|
self.0,
|
||||||
|
keymap as *const c::KeyMap,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,10 +14,10 @@ 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) {
|
void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, struct KeyMap *keymap) {
|
||||||
zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1,
|
zwp_virtual_keyboard_v1_keymap(zwp_virtual_keyboard_v1,
|
||||||
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
||||||
keyboard->keymap.fd, keyboard->keymap.fd_len);
|
keymap->fd, keymap->fd_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@ -50,12 +50,12 @@ endforeach
|
|||||||
foreach layout : [
|
foreach layout : [
|
||||||
'us', 'us_wide',
|
'us', 'us_wide',
|
||||||
'br',
|
'br',
|
||||||
'be', 'be_wide',
|
'be',
|
||||||
'de', 'de_wide',
|
'de', 'de_wide',
|
||||||
'dk',
|
'dk',
|
||||||
'es',
|
'es',
|
||||||
'fi',
|
'fi',
|
||||||
'fr', 'fr_wide',
|
'fr',
|
||||||
'gr',
|
'gr',
|
||||||
'it',
|
'it',
|
||||||
'jp+kana','jp+kana_wide',
|
'jp+kana','jp+kana_wide',
|
||||||
@ -65,7 +65,7 @@ foreach layout : [
|
|||||||
'ru',
|
'ru',
|
||||||
'se',
|
'se',
|
||||||
'ua',
|
'ua',
|
||||||
'terminal', 'terminal_wide',
|
'terminal',
|
||||||
|
|
||||||
'emoji',
|
'emoji',
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user