Compare commits

..

1 Commits

Author SHA1 Message Date
1488aa97f3 Release 1.8.1 "Corona"
- Landscape layout doesn't crash
- CSS font is actually taken into account
- Failed start due to dbus is now communicated
- Better log messages
- Fixed Enter in numbers layout
- More consistent terminal layout
- Proper font sizes in terminal layout
2020-01-31 10:08:59 +00:00
66 changed files with 883 additions and 1955 deletions

View File

@ -84,13 +84,3 @@ test:
script: script:
- apt-get -y build-dep . - apt-get -y build-dep .
- ninja -C _build test - ninja -C _build test
check_release:
<<: *tags
stage: test
only:
refs:
- master
script:
- apt-get -y install git python3
- (head -n 1 ./debian/changelog && git tag) | ./debian/check_release.py

14
Cargo.lock generated
View File

@ -2,10 +2,10 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.8" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -254,7 +254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.3.2" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -317,8 +317,8 @@ name = "regex"
version = "1.1.9" version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -457,7 +457,7 @@ dependencies = [
] ]
[metadata] [metadata]
"checksum aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" "checksum aho-corasick 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5f56c476256dc249def911d6f7580b5fc7e875895b5d7ee88f5d602208035744"
"checksum atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7017e53393e713212aed7aea336b6553be4927f58c37070a56c2fe3d107e489" "checksum atk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7017e53393e713212aed7aea336b6553be4927f58c37070a56c2fe3d107e489"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd940f0d609699e343ef71c4af5f66423afbf30d666f796dabd8fd15229cf5b6" "checksum cairo-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd940f0d609699e343ef71c4af5f66423afbf30d666f796dabd8fd15229cf5b6"
@ -481,7 +481,7 @@ dependencies = [
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" "checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2cb169402a3eb1ba034a7cc7d95b8b1c106e9be5ba4be79a5a93dc1a2795f4" "checksum pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2cb169402a3eb1ba034a7cc7d95b8b1c106e9be5ba4be79a5a93dc1a2795f4"
"checksum pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6eb49268e69dd0c1da5d3001a61aac08e2e9d2bfbe4ae4b19b9963c998f6453" "checksum pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6eb49268e69dd0c1da5d3001a61aac08e2e9d2bfbe4ae4b19b9963c998f6453"

View File

@ -3,42 +3,10 @@ Hacking
This document describes the standards for modifying and maintaining the *squeekboard* project. This document describes the standards for modifying and maintaining the *squeekboard* project.
Principles
----------
The project was built upon some guiding principles, which should be respected primarily by the maintainers, but also by contributors to avoid needlessly rejected changes.
The overarching principle of *squeekboard* is to empower users.
Software is primarily meant to solve problems of its users. Often in the quest to make software better, a hard distinction is made between the developer, who becomes the creator, and the user, who takes the role of the consumer, without direct influence on the software they use.
This project aims to give users the power to make the software work for them by blurring the lines between users and developers.
Nonwithstanding its current state, *squeekboard* must be structured in a way that provides users a gradual way to gain more experience and power to adjust it. It must be easy, in order of importance:
- to use the software,
- to modify its resources,
- to change its behaviour,
- to contribute upstream.
To give an idea of what it means in practice, those are some examples of what has been important for *squeekboard* so far:
- being quick and useable,
- allowing local overrides of resources and config,
- storing resources and config as editable, standard files,
- having complete, up to date documentation of interfaces,
- having an easy process of sending contributions,
- adapting to to user's settings and constrains without overriding them,
- avoiding compiling whenever possible,
- making it easy to build,
- having code that is [simple and obvious](https://www.python.org/dev/peps/pep-0020/),
- having an easy process of testing and accepting contributions.
You may notice that they are ordered roughly from "user-focused" to "maintainer-focused". While good properties are desired, sometimes they conflict, and maintainers should give additional weight to those benefitting the user compared to those benefitting regular contributors.
Sending patches Sending patches
--------------- ---------------
By submitting a change to this project, you agree to license it under the [GPL license version 3](https://source.puri.sm/Librem5/squeekboard/blob/master/COPYING), or any later version. You also certify that your contribution fulfills the [Developer's Certificate of Origin 1.1](https://source.puri.sm/Librem5/squeekboard/blob/master/dco.txt). By submitting a change to this project, you agree to license it under the [GPL license version 3](./COPYING), or any later version. You also certify that your contribution fulfills the [Developer's Certificate of Origin 1.1](./dco.txt).
Development environment Development environment
----------------------- -----------------------
@ -56,7 +24,8 @@ sudo apt-get -y install build-essential
sudo apt-get -y build-dep . sudo apt-get -y build-dep .
``` ```
For an explicit list of dependencies check the `Build-Depends` entry in the [`debian/control`](https://source.puri.sm/Librem5/squeekboard/blob/master/debian/control) file. For an explicit list of dependencies check the `Build-Depends` entry in the
[`debian/control`](./debian/control) file.
Testing Testing
------- -------

View File

@ -56,4 +56,4 @@ $ src/squeekboard
Developing Developing
---------- ----------
See [`docs/hacking.md`](docs/hacking.md) for this copy, or the [official documentation](https://developer.puri.sm/projects/squeekboard/) for the current release. See `HACKING.md`

View File

@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -46,7 +46,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -1,80 +1,16 @@
--- ---
outlines: outlines:
default: { width: 52, height: 52 } default: { width: 52, height: 52 }
altline: { width: 40, height: 52 } altline: { width: 52, height: 52 }
narrow: { width: 22, height: 52 }
views: views:
base: base:
- "😀 😁 😅 😂 😊 😇 🙃" - "😀 😁 😅 😂 😊 😇 🙃"
- "😍 😘 😋 😜 😎 🥳 😔" - "😍 😘 😋 😜 😎 🥳 😔"
- "😢 😭 😡 😱 🤔 😬 🙄" - "😢 😭 😡 😱 🤔 😬 🙄"
- "preferences blank 1 2 3 4 5 6 blank BackSpace" - "preferences 🤨 🤓 😴 🤢 🤮 😈"
two:
- "🤩 🤨 🤓 😴 🤢 🤮 😈"
- "💩 🙌 👏 👍 👎 👌 👋"
- "💪 🖕 🙏 💋 🤦‍♀️ 🤷‍♀️ 💃"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
three:
- "🐶 🐱 🐯 🙈 🐴 🦄 🌳"
- "🍀 🌹 💫 ⭐️ ✨ 💥 🔥"
- "🌈 ☀️ 🌤 🌧 ⛄️ ☂️ 🌊"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
four:
- "🍎 🍓 🍑 🍍 🍆 🥑 🥦"
- "🍕 🎂 🍫 🍿 🍻 🍾 🍽"
- "⚽️ 🏀 🏓 🏆 🎹 🎸 🎯"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
five:
- "🚗 🚌 🚲 🚄 🚂 ✈️ 🛰"
- "🚀 🛸 🚁 🚦 🏝 🏔 ⛺️"
- "🏠 🏢 🏥 🏛 🛤 🌅 🎇"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
six:
- "⌚️ 📱 💻 🖥 🖨 🕹 ✉️"
- "📞 ☎️ ⏰ ⏳ 📈 📉 📌"
- "🎁 ❤️ 💕 💯 ✅ ❎ 📢"
- "preferences blank 1 2 3 4 5 6 blank BackSpace"
buttons: buttons:
1:
action:
set_view: "base"
outline: "altline"
label: "1"
2:
action:
set_view: "two"
outline: "altline"
label: "2"
3:
action:
set_view: "three"
outline: "altline"
label: "3"
4:
action:
set_view: "four"
outline: "altline"
label: "4"
5:
action:
set_view: "five"
outline: "altline"
label: "5"
6:
action:
set_view: "six"
outline: "altline"
label: "6"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
keysym: BackSpace
blank:
outline: "narrow"
text: ""

View File

@ -44,7 +44,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -46,7 +46,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"

View File

@ -195,7 +195,7 @@ buttons:
BackSpace: BackSpace:
outline: "wide" outline: "wide"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"

View File

@ -195,7 +195,7 @@ buttons:
BackSpace: BackSpace:
outline: "wide" outline: "wide"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -16,7 +16,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
space: space:
outline: spaceline outline: spaceline
text: " " text: " "

View File

@ -1,110 +0,0 @@
---
outlines:
default: { width: 35.33, height: 52 }
altline: { width: 52.67, height: 52 }
wide: { width: 59, height: 52 }
spaceline: { width: 140, height: 52 }
special: { width: 44, height: 52 }
views:
base:
- "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_accents Return"
upper:
- "Q W E R T Y U I O P"
- "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space show_upper_accents Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space period Return"
accents:
- "q w ę r t y u i ó p"
- "ą ś d f g h j k ł"
- "accents_show_upper ż ź ć v b ń m BackSpace"
- "show_numbers preferences space show_accents Return"
upper_accents:
- "Q W Ę R T Y U I Ó P"
- "Ą Ś D F G H J K Ł"
- "accents_show_upper Ż Ź Ć V B Ń M BackSpace"
- "show_numbers preferences space show_upper_accents Return"
buttons:
Shift_L:
action:
locking:
lock_view: "upper"
unlock_view: "base"
outline: "altline"
icon: "key-shift"
accents_show_upper:
action:
locking:
lock_view: "upper_accents"
unlock_view: "accents"
outline: "altline"
icon: "key-shift"
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
keysym: "BackSpace"
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_accents:
action:
locking:
lock_view: "accents"
unlock_view: "base"
outline: "altline"
label: "ąę"
show_upper_accents:
action:
locking:
lock_view: "upper_accents"
unlock_view: "upper"
outline: "altline"
label: "ĄĘ"
period:
outline: "altline"
text: "."
space:
outline: "spaceline"
text: " "
Return:
outline: "wide"
icon: "key-enter"
keysym: "Return"
colon:
text: ":"

View File

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

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"

View File

@ -20,13 +20,13 @@ views:
- "show_numbers preferences space show_actions Return" - "show_numbers preferences space show_actions Return"
numbers: numbers:
- "1 2 3 4 5 6 7 8 9 0" - "1 2 3 4 5 6 7 8 9 0"
- "* # $ / & - _ + ( )" - "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace" - "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return" - "show_letters preferences space period Return"
symbols: symbols:
- "~ ` | · √ π τ ÷ × ¶" - "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° @ { }" - "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ % < > = [ ] BackSpace" - "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space period Return" - "show_letters preferences space period Return"
actions: actions:
- "F1 F2 F3 F4 F5 F6" - "F1 F2 F3 F4 F5 F6"
@ -45,7 +45,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"
@ -69,14 +69,14 @@ buttons:
action: action:
set_view: "symbols" set_view: "symbols"
outline: "altline" outline: "altline"
label: "τ=\\" label: "*/="
show_actions: show_actions:
action: action:
set_view: "actions" set_view: "actions"
outline: "altline" outline: "altline"
label: ">_" label: ">_"
period: period:
outline: "altline" outline: "special"
text: "." text: "."
space: space:
outline: "spaceline" outline: "spaceline"

View File

@ -39,9 +39,9 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: erase keysym: "BackSpace"
preferences: preferences:
action: show_prefs action: "show_prefs"
outline: "special" outline: "special"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
show_numbers: show_numbers:

View File

@ -39,7 +39,7 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
action: "erase" keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"

View File

@ -0,0 +1,8 @@
us Englisch (US)
de Deutsch
el Griechisch
es Spanisch
it Italienisch
jp+kana Japanisch (Kana)
no Norwegisch

View File

@ -1,2 +1,10 @@
emoji Emoji us English (US)
terminal Terminal de German
el Greek
es Spanish
fi Finnish
it Italian
jp+kana Japanese (kana)
no Norwegian
se Swedish

View File

@ -0,0 +1,8 @@
us 英語 (US)
de ドイツ語
el ギリシャ語
es スペイン語
it イタリア語
jp+kana 日本語 (かな)
nb ノルウェー語

View File

@ -1,2 +1,9 @@
emoji emoji us angielski (USA)
terminal terminal de niemiecki
el grecki
es hiszpański
fi fiński
it włoski
jp+kana japoński (kana)
no norweski
se szwedzki

37
debian/changelog vendored
View File

@ -1,40 +1,3 @@
squeekboard (1.9.0) amber-phone; urgency=medium
[ Dorota Czaplejewicz ]
* imservice: Add commit_string method
* submission: Handle submitting strings
* input_method: Use for erasing
* logging: Use in merged functions
* translations: Remove redundant ones
* translations: Translate builtin layouts
* greek: Rename to gr which is used by gnome settings
[ Sebastian Krzyszkowiak ]
* layouts: Add Polish layouts
[ Dorota Czaplejewicz ]
* locks: Draw based on current view
* locking: Lock keys statelessly
* layouts: Better accented uppercase in PL
* emoji: Add more choices
* row: Eliminate angle
* layout: Center views relative to each other and the layout bounds
* drawing: Generalized foreach_visible_button
* variant: Fix double-free
* variant: Fix leak
* keyboard_layout: Fix leak
* layout: Improve scoping of locked variable
* terminal: Make */ easier to reach
[ Sebastian Krzyszkowiak ]
* layouts: terminal: Use altline outline for dot key
[ Dorota Czaplejewicz ]
* text input: Disable erasing
* cargo: Update deps
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Wed, 19 Feb 2020 14:32:39 +0000
squeekboard (1.8.1) amber-phone; urgency=medium squeekboard (1.8.1) amber-phone; urgency=medium
[ Dorota Czaplejewicz ] [ Dorota Czaplejewicz ]

View File

@ -1,10 +0,0 @@
#!/usr/bin/env python3
"""Checks tag before release.
Feed it the first changelog line, and then all available tags.
"""
import re, sys
tag = "v" + re.findall("\\((.*)\\)", input())[0]
if tag not in map(str.strip, sys.stdin.readlines()):
raise Exception("Changelog's current version doesn't have a tag. Push the tag!")

View File

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

View File

@ -31,7 +31,7 @@ So at least I will try to start writing a short how-to here and edit this post a
**Running 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 https://source.puri.sm/Librem5/squeekboard/blob/master/HACKING.md#testing
* You can either test it locally on your Linux system or use the [QEMU Librem 5 image ](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html) * You can either test it locally on your Linux system or use the [QEMU Librem 5 image ](https://developer.puri.sm/Librem5/Development_Environment/Boards/emulators.html)
* To test squeekboard locally, you need phoc. Either compile that from the sources as well or use the CI repository ci.puri.sm for Debian based systems: * To test squeekboard locally, you need phoc. Either compile that from the sources as well or use the CI repository ci.puri.sm for Debian based systems:
`deb [arch=amd64] http://ci.puri.sm/ scratch librem5` `deb [arch=amd64] http://ci.puri.sm/ scratch librem5`

View File

@ -41,15 +41,23 @@
#include "src/layout.h" #include "src/layout.h"
#include "src/submission.h" #include "src/submission.h"
enum {
PROP_0,
PROP_LAST
};
/* since 2.91.5 GDK_DRAWABLE was removed and gdk_cairo_create takes
GdkWindow as the argument */
#ifndef GDK_DRAWABLE
#define GDK_DRAWABLE(x) (x)
#endif
typedef struct _EekGtkKeyboardPrivate typedef struct _EekGtkKeyboardPrivate
{ {
EekRenderer *renderer; // owned, nullable EekRenderer *renderer;
LayoutHolder *eekboard_context; // unowned reference EekboardContextService *eekboard_context; // unowned reference
struct submission *submission; // unowned reference struct submission *submission; // unowned reference
LevelKeyboard *keyboard; // unowned reference; it's kept in server-context (FIXME)
struct squeek_layout_state *layout; // unowned
LevelKeyboard *keyboard; // unowned reference; it's kept in server-context
GdkEventSequence *sequence; // unowned reference GdkEventSequence *sequence; // unowned reference
} EekGtkKeyboardPrivate; } EekGtkKeyboardPrivate;
@ -80,59 +88,31 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
GtkAllocation allocation; GtkAllocation allocation;
gtk_widget_get_allocation (self, &allocation); gtk_widget_get_allocation (self, &allocation);
if (!priv->keyboard) {
return FALSE;
}
if (!priv->renderer) { if (!priv->renderer) {
PangoContext *pcontext = gtk_widget_get_pango_context (self); PangoContext *pcontext = gtk_widget_get_pango_context (self);
priv->renderer = eek_renderer_new ( priv->renderer = eek_renderer_new (priv->keyboard, pcontext);
priv->keyboard,
pcontext);
eek_renderer_set_allocation_size (priv->renderer, eek_renderer_set_allocation_size (priv->renderer,
priv->keyboard->layout,
allocation.width, allocation.width,
allocation.height); allocation.height);
eek_renderer_set_scale_factor (priv->renderer, eek_renderer_set_scale_factor (priv->renderer,
gtk_widget_get_scale_factor (self)); gtk_widget_get_scale_factor (self));
} }
eek_renderer_render_keyboard (priv->renderer, priv->submission, cr, priv->keyboard); eek_renderer_render_keyboard (priv->renderer, cr);
return FALSE; return FALSE;
} }
// Units of pixel size
static enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 1080) {
return ARRANGEMENT_KIND_BASE;
}
return ARRANGEMENT_KIND_WIDE;
}
static void static void
eek_gtk_keyboard_real_size_allocate (GtkWidget *self, eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
GtkAllocation *allocation) GtkAllocation *allocation)
{ {
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
enum squeek_arrangement_kind new_type = get_type(
(uint32_t)(allocation->width - allocation->x) * scale,
(uint32_t)(allocation->height - allocation->y) * scale);
if (priv->layout->arrangement != new_type) {
struct squeek_layout_state layout = *priv->layout;
layout.arrangement = new_type;
eek_layout_holder_use_layout(priv->eekboard_context, &layout);
}
if (priv->renderer) if (priv->renderer)
eek_renderer_set_allocation_size (priv->renderer, eek_renderer_set_allocation_size (priv->renderer,
priv->keyboard->layout,
allocation->width, allocation->width,
allocation->height); allocation->height);
@ -144,9 +124,7 @@ static void depress(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time) gdouble x, gdouble y, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (!priv->keyboard) {
return;
}
squeek_layout_depress(priv->keyboard->layout, squeek_layout_depress(priv->keyboard->layout,
priv->submission, priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, self); x, y, eek_renderer_get_transformation(priv->renderer), time, self);
@ -156,10 +134,7 @@ static void drag(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time) gdouble x, gdouble y, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (!priv->keyboard) { squeek_layout_drag(priv->keyboard->layout,
return;
}
squeek_layout_drag(eek_layout_holder_get_keyboard(priv->eekboard_context)->layout,
priv->submission, priv->submission,
x, y, eek_renderer_get_transformation(priv->renderer), time, x, y, eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, self); priv->eekboard_context, self);
@ -168,10 +143,8 @@ static void drag(EekGtkKeyboard *self,
static void release(EekGtkKeyboard *self, guint32 time) static void release(EekGtkKeyboard *self, guint32 time)
{ {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (!priv->keyboard) {
return; squeek_layout_release(priv->keyboard->layout,
}
squeek_layout_release(eek_layout_holder_get_keyboard(priv->eekboard_context)->layout,
priv->submission, priv->submission,
eek_renderer_get_transformation(priv->renderer), time, eek_renderer_get_transformation(priv->renderer), time,
priv->eekboard_context, self); priv->eekboard_context, self);
@ -291,7 +264,7 @@ eek_gtk_keyboard_dispose (GObject *object)
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self); EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
if (priv->renderer) { if (priv->renderer) {
eek_renderer_free(priv->renderer); g_object_unref (priv->renderer);
priv->renderer = NULL; priv->renderer = NULL;
priv->renderer = NULL; priv->renderer = NULL;
} }
@ -338,46 +311,26 @@ eek_gtk_keyboard_init (EekGtkKeyboard *self)
(void)self; (void)self;
} }
static void
on_notify_keyboard (GObject *object,
GParamSpec *spec,
EekGtkKeyboard *self) {
(void)spec;
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (self);
priv->keyboard = eek_layout_holder_get_keyboard(LAYOUT_HOLDER(object));
if (priv->renderer) {
eek_renderer_free(priv->renderer);
}
priv->renderer = NULL;
gtk_widget_queue_draw(GTK_WIDGET(self));
}
/** /**
* eek_gtk_keyboard_new:
* @keyboard: an #EekKeyboard
*
* Create a new #GtkWidget displaying @keyboard. * Create a new #GtkWidget displaying @keyboard.
* Returns: a #GtkWidget * Returns: a #GtkWidget
*/ */
GtkWidget * GtkWidget *
eek_gtk_keyboard_new (LayoutHolder *eekservice, eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice,
struct submission *submission, struct submission *submission)
struct squeek_layout_state *layout)
{ {
EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL)); EekGtkKeyboard *ret = EEK_GTK_KEYBOARD(g_object_new (EEK_TYPE_GTK_KEYBOARD, NULL));
EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret); EekGtkKeyboardPrivate *priv = (EekGtkKeyboardPrivate*)eek_gtk_keyboard_get_instance_private (ret);
priv->keyboard = keyboard;
priv->eekboard_context = eekservice; priv->eekboard_context = eekservice;
priv->submission = submission; priv->submission = submission;
priv->layout = layout;
priv->renderer = NULL;
g_signal_connect (eekservice,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ret);
on_notify_keyboard(G_OBJECT(eekservice), NULL, ret);
/* TODO: this is how a compound keyboard
* made out of a layout and a suggestion bar could start.
* GtkBox *box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
GtkEntry *fill = GTK_ENTRY(gtk_entry_new());
gtk_box_pack_start(box, GTK_WIDGET(fill), FALSE, FALSE, 0);
gtk_box_pack_start(box, GTK_WIDGET(ret), TRUE, TRUE, 0);
return GTK_WIDGET(box);*/
return GTK_WIDGET(ret); return GTK_WIDGET(ret);
} }
EekRenderer *eek_gtk_keyboard_get_renderer(EekGtkKeyboard *self) {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
return priv->renderer;
}

View File

@ -31,7 +31,7 @@
#include "eek/eek-types.h" #include "eek/eek-types.h"
struct submission; struct submission;
struct squeek_layout_state; typedef struct _LevelKeyboard LevelKeyboard; // including causes weird bugs
G_BEGIN_DECLS G_BEGIN_DECLS
#define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type()) #define EEK_TYPE_GTK_KEYBOARD (eek_gtk_keyboard_get_type())
@ -48,7 +48,7 @@ struct _EekGtkKeyboardClass
}; };
GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST; GType eek_gtk_keyboard_get_type (void) G_GNUC_CONST;
GtkWidget *eek_gtk_keyboard_new (LayoutHolder *eekservice, struct submission *submission, struct squeek_layout_state *layout); GtkWidget *eek_gtk_keyboard_new (LevelKeyboard *keyboard, EekboardContextService *eekservice, struct submission *submission);
G_END_DECLS G_END_DECLS
#endif /* EEK_GTK_KEYBOARD_H */ #endif /* EEK_GTK_KEYBOARD_H */

View File

@ -38,8 +38,10 @@ void level_keyboard_free(LevelKeyboard *self) {
} }
LevelKeyboard* LevelKeyboard*
level_keyboard_new (struct squeek_layout *layout) level_keyboard_new (const gchar *keyboard_type,
enum squeek_arrangement_kind t)
{ {
struct squeek_layout *layout = squeek_load_layout(keyboard_type, t);
LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1); LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
if (!keyboard) { if (!keyboard) {

View File

@ -47,7 +47,8 @@ gchar * eek_keyboard_get_keymap
(LevelKeyboard *keyboard); (LevelKeyboard *keyboard);
LevelKeyboard* LevelKeyboard*
level_keyboard_new (struct squeek_layout *layout); level_keyboard_new (const gchar *keyboard_type,
enum squeek_arrangement_kind t);
void level_keyboard_free(LevelKeyboard *self); void level_keyboard_free(LevelKeyboard *self);
G_END_DECLS G_END_DECLS

View File

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

View File

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

View File

@ -37,7 +37,7 @@ G_BEGIN_DECLS
typedef struct _EekBounds EekBounds; typedef struct _EekBounds EekBounds;
typedef struct _LayoutHolder LayoutHolder; typedef struct _EekboardContextService EekboardContextService;
typedef struct _ServerContextService ServerContextService; typedef struct _ServerContextService ServerContextService;
typedef struct _LevelKeyboard LevelKeyboard; typedef struct _LevelKeyboard LevelKeyboard;

View File

@ -35,17 +35,34 @@ enum {
PROP_LAST PROP_LAST
}; };
#define LAYOUT_HOLDER_GET_PRIVATE(obj) \ enum {
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_LAYOUT_HOLDER, LayoutHolderPrivate)) DESTROYED,
LAST_SIGNAL
};
struct _LayoutHolderPrivate { static guint signals[LAST_SIGNAL] = { 0, };
#define EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServicePrivate))
struct _EekboardContextServicePrivate {
LevelKeyboard *keyboard; // currently used keyboard LevelKeyboard *keyboard; // currently used keyboard
GHashTable *keyboard_hash; // a table of available keyboards, per layout
char *overlay;
GSettings *settings; // Owned reference
uint32_t hint;
uint32_t purpose;
// Maybe TODO: it's used only for fetching layout type.
// Maybe let UI push the type to this structure?
ServerContextService *ui; // unowned reference
/// Needed for keymap changes after keyboard updates /// Needed for keymap changes after keyboard updates
struct submission *submission; // unowned struct submission *submission; // unowned
}; };
G_DEFINE_TYPE_WITH_PRIVATE (LayoutHolder, layout_holder, G_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE (EekboardContextService, eekboard_context_service, G_TYPE_OBJECT);
static void static void
eekboard_context_service_set_property (GObject *object, eekboard_context_service_set_property (GObject *object,
@ -62,12 +79,12 @@ eekboard_context_service_set_property (GObject *object,
} }
static void static void
layout_holder_get_property (GObject *object, eekboard_context_service_get_property (GObject *object,
guint prop_id, guint prop_id,
GValue *value, GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
LayoutHolder *context = LAYOUT_HOLDER(object); EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
switch (prop_id) { switch (prop_id) {
case PROP_KEYBOARD: case PROP_KEYBOARD:
@ -79,52 +96,60 @@ layout_holder_get_property (GObject *object,
} }
} }
static void
eekboard_context_service_dispose (GObject *object)
{
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
if (context->priv->keyboard_hash) {
g_hash_table_destroy (context->priv->keyboard_hash);
context->priv->keyboard_hash = NULL;
}
G_OBJECT_CLASS (eekboard_context_service_parent_class)->
dispose (object);
}
static void static void
settings_get_layout(GSettings *settings, char **type, char **layout) settings_get_layout(GSettings *settings, char **type, char **layout)
{ {
if (!settings) {
return;
}
GVariant *inputs = g_settings_get_value(settings, "sources"); GVariant *inputs = g_settings_get_value(settings, "sources");
if (g_variant_n_children(inputs) == 0) { // current layout is always first
g_warning("No system layout present"); g_variant_get_child(inputs, 0, "(ss)", type, layout);
*type = NULL;
*layout = NULL;
} else {
// current layout is always first
g_variant_get_child(inputs, 0, "(ss)", type, layout);
}
g_variant_unref(inputs);
} }
void void
eek_layout_holder_use_layout(LayoutHolder *context, struct squeek_layout_state *state) { eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t)
*context->layout = *state; {
gchar *layout_name = state->overlay_name; g_autofree gchar *keyboard_layout = NULL;
if (context->priv->overlay) {
keyboard_layout = g_strdup(context->priv->overlay);
} else {
g_autofree gchar *keyboard_type = NULL;
settings_get_layout(context->priv->settings,
&keyboard_type, &keyboard_layout);
}
if (layout_name == NULL) { if (!keyboard_layout) {
layout_name = state->layout_name; keyboard_layout = g_strdup("us");
}
switch (state->purpose) { EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
layout_name = "number";
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
layout_name = "terminal";
break;
default:
;
}
if (layout_name == NULL) { switch (priv->purpose) {
layout_name = "us"; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
} case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE:
keyboard_layout = g_strdup("number");
break;
case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL:
keyboard_layout = g_strdup("terminal");
break;
default:
;
} }
// generic part follows // generic part follows
struct squeek_layout *layout = squeek_load_layout(layout_name, state->arrangement); LevelKeyboard *keyboard = level_keyboard_new(keyboard_layout, t);
LevelKeyboard *keyboard = level_keyboard_new(layout);
// set as current // set as current
LevelKeyboard *previous_keyboard = context->priv->keyboard; LevelKeyboard *previous_keyboard = context->priv->keyboard;
context->priv->keyboard = keyboard; context->priv->keyboard = keyboard;
@ -134,7 +159,6 @@ eek_layout_holder_use_layout(LayoutHolder *context, struct squeek_layout_state *
submission_set_keyboard(context->priv->submission, keyboard); submission_set_keyboard(context->priv->submission, keyboard);
} }
// Update UI
g_object_notify (G_OBJECT(context), "keyboard"); g_object_notify (G_OBJECT(context), "keyboard");
// replacing the keyboard above will cause the previous keyboard to get destroyed from the UI side (eek_gtk_keyboard_dispose) // replacing the keyboard above will cause the previous keyboard to get destroyed from the UI side (eek_gtk_keyboard_dispose)
@ -143,22 +167,67 @@ eek_layout_holder_use_layout(LayoutHolder *context, struct squeek_layout_state *
} }
} }
static void static void update_layout_and_type(EekboardContextService *context) {
layout_holder_init (LayoutHolder *self) { EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
self->priv = LAYOUT_HOLDER_GET_PRIVATE(self); enum squeek_arrangement_kind layout_kind = ARRANGEMENT_KIND_BASE;
if (priv->ui) {
layout_kind = server_context_service_get_layout_type(priv->ui);
}
eekboard_context_service_update_layout(context, layout_kind);
}
static gboolean
settings_handle_layout_changed(GSettings *s,
gpointer keys, gint n_keys,
gpointer user_data) {
(void)s;
(void)keys;
(void)n_keys;
EekboardContextService *context = user_data;
g_free(context->priv->overlay);
context->priv->overlay = NULL;
update_layout_and_type(context);
return TRUE;
} }
static void static void
layout_holder_class_init (LayoutHolderClass *klass) eekboard_context_service_constructed (GObject *object)
{
EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE (object);
update_layout_and_type(context);
}
static void
eekboard_context_service_class_init (EekboardContextServiceClass *klass)
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec; GParamSpec *pspec;
gobject_class->constructed = eekboard_context_service_constructed;
gobject_class->set_property = eekboard_context_service_set_property; gobject_class->set_property = eekboard_context_service_set_property;
gobject_class->get_property = layout_holder_get_property; gobject_class->get_property = eekboard_context_service_get_property;
gobject_class->dispose = eekboard_context_service_dispose;
/**
* EekboardContextService::destroyed:
* @context: an #EekboardContextService
*
* Emitted when @context is destroyed.
*/
signals[DESTROYED] =
g_signal_new (I_("destroyed"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekboardContextServiceClass, destroyed),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
/** /**
* An #LevelKeyboard currently active in this context. * EekboardContextService:keyboard:
*
* An #EekKeyboard currently active in this context.
*/ */
pspec = g_param_spec_pointer("keyboard", pspec = g_param_spec_pointer("keyboard",
"Keyboard", "Keyboard",
@ -169,115 +238,92 @@ layout_holder_class_init (LayoutHolderClass *klass)
pspec); pspec);
} }
static void
eekboard_context_service_init (EekboardContextService *self)
{
self->priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(self);
self->priv->keyboard_hash =
g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify)g_object_unref);
self->priv->settings = g_settings_new ("org.gnome.desktop.input-sources");
gulong conn_id = g_signal_connect(self->priv->settings, "change-event",
G_CALLBACK(settings_handle_layout_changed),
self);
if (conn_id == 0) {
g_warning ("Could not connect to gsettings updates, layout"
" changing unavailable");
}
self->priv->overlay = NULL;
}
/** /**
* eekboard_context_service_destroy:
* @context: an #EekboardContextService
*
* Destroy @context.
*/
void
eekboard_context_service_destroy (EekboardContextService *context)
{
g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
g_free(context->priv->overlay);
g_signal_emit (context, signals[DESTROYED], 0);
}
/**
* eekboard_context_service_get_keyboard:
* @context: an #EekboardContextService
*
* Get keyboard currently active in @context. * Get keyboard currently active in @context.
* Returns: (transfer none): a LevelKeyboard * Returns: (transfer none): an #EekKeyboard
*/ */
LevelKeyboard * LevelKeyboard *
eek_layout_holder_get_keyboard (LayoutHolder *context) eekboard_context_service_get_keyboard (EekboardContextService *context)
{ {
return context->priv->keyboard; return context->priv->keyboard;
} }
void eekboard_context_service_set_hint_purpose(LayoutHolder *context, void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint, uint32_t purpose) uint32_t hint, uint32_t purpose)
{ {
if (context->layout->hint != hint || context->layout->purpose != purpose) { EekboardContextServicePrivate *priv = EEKBOARD_CONTEXT_SERVICE_GET_PRIVATE(context);
context->layout->hint = hint;
context->layout->purpose = purpose; if (priv->hint != hint || priv->purpose != purpose) {
eek_layout_holder_use_layout(context, context->layout); priv->hint = hint;
priv->purpose = purpose;
update_layout_and_type(context);
} }
} }
void void
eekboard_context_service_set_overlay(LayoutHolder *context, const char* name) { eekboard_context_service_set_overlay(EekboardContextService *context, const char* name) {
if (g_strcmp0(context->layout->overlay_name, name)) { context->priv->overlay = g_strdup(name);
g_free(context->layout->overlay_name); update_layout_and_type(context);
context->layout->overlay_name = g_strdup(name);
eek_layout_holder_use_layout(context, context->layout);
}
} }
const char* const char*
eekboard_context_service_get_overlay(LayoutHolder *context) { eekboard_context_service_get_overlay(EekboardContextService *context) {
return context->layout->overlay_name; return context->priv->overlay;
} }
LayoutHolder *eek_layout_holder_new(struct squeek_layout_state *state) EekboardContextService *eekboard_context_service_new(void)
{ {
LayoutHolder *context = g_object_new (EEKBOARD_TYPE_LAYOUT_HOLDER, NULL); return g_object_new (EEKBOARD_TYPE_CONTEXT_SERVICE, NULL);
context->layout = state;
eek_layout_holder_use_layout(context, context->layout);
return context;
} }
void eek_layout_holder_set_submission(LayoutHolder *context, struct submission *submission) { void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission) {
context->priv->submission = submission; context->priv->submission = submission;
if (context->priv->submission) { if (context->priv->submission) {
submission_set_keyboard(context->priv->submission, context->priv->keyboard); submission_set_keyboard(context->priv->submission, context->priv->keyboard);
} }
} }
static void settings_update_layout(struct gsettings_tracker *self) { void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui) {
// The layout in the param must be the same layout as held by context. context->priv->ui = ui;
g_autofree gchar *keyboard_layout = NULL;
g_autofree gchar *keyboard_type = NULL;
settings_get_layout(self->gsettings,
&keyboard_type, &keyboard_layout);
if (g_strcmp0(self->layout->layout_name, keyboard_layout) != 0 || self->layout->overlay_name) {
g_free(self->layout->overlay_name);
self->layout->overlay_name = NULL;
if (keyboard_layout) {
g_free(self->layout->layout_name);
self->layout->layout_name = g_strdup(keyboard_layout);
}
// This must actually update the UI.
eek_layout_holder_use_layout(self->context, self->layout);
}
}
static gboolean
handle_layout_changed(GSettings *s,
gpointer keys, gint n_keys,
gpointer user_data) {
(void)s;
(void)keys;
(void)n_keys;
struct gsettings_tracker *self = user_data;
settings_update_layout(self);
return TRUE;
}
void eek_gsettings_tracker_init(struct gsettings_tracker *tracker, LayoutHolder *context, struct squeek_layout_state *layout)
{
tracker->layout = layout;
tracker->context = context;
const char *schema_name = "org.gnome.desktop.input-sources";
GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default();
if (ssrc) {
GSettingsSchema *schema = g_settings_schema_source_lookup(ssrc,
schema_name,
TRUE);
if (schema) {
// Not referencing the found schema directly,
// because it's not clear how...
tracker->gsettings = g_settings_new (schema_name);
gulong conn_id = g_signal_connect(tracker->gsettings, "change-event",
G_CALLBACK(handle_layout_changed),
tracker);
if (conn_id == 0) {
g_warning ("Could not connect to gsettings updates, "
"automatic layout changing unavailable");
}
} else {
g_warning("Gsettings schema %s is not installed on the system. "
"Layout switching unavailable", schema_name);
}
} else {
g_warning("No gsettings schemas installed. Layout switching unavailable.");
}
settings_update_layout(tracker);
} }

View File

@ -30,60 +30,74 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#define EEKBOARD_TYPE_LAYOUT_HOLDER (layout_holder_get_type()) #define EEKBOARD_CONTEXT_SERVICE_PATH "/org/fedorahosted/Eekboard/Context_%d"
#define LAYOUT_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_LAYOUT_HOLDER, LayoutHolder)) #define EEKBOARD_CONTEXT_SERVICE_INTERFACE "org.fedorahosted.Eekboard.Context"
#define EEKBOARD_TYPE_CONTEXT_SERVICE (eekboard_context_service_get_type())
#define EEKBOARD_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextService))
#define EEKBOARD_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass)) #define EEKBOARD_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
#define EEKBOARD_IS_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE)) #define EEKBOARD_IS_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE))
#define EEKBOARD_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE)) #define EEKBOARD_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE))
#define EEKBOARD_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass)) #define EEKBOARD_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
typedef struct _LayoutHolderClass LayoutHolderClass; typedef struct _EekboardContextServiceClass EekboardContextServiceClass;
typedef struct _LayoutHolderPrivate LayoutHolderPrivate; typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
/** /**
* Handles layout state, and virtual-keyboard. * EekboardContextService:
*
* Handles layout state, gsettings, and virtual-keyboard.
* *
* TODO: Restrict to managing keyboard layouts, and maybe button repeats, * TODO: Restrict to managing keyboard layouts, and maybe button repeats,
* and the virtual keyboard protocol. * and the virtual keyboard protocol.
*
* The #EekboardContextService structure contains only private data
* and should only be accessed using the provided API.
*/ */
struct _LayoutHolder { struct _EekboardContextService {
GObject parent; GObject parent;
LayoutHolderPrivate *priv;
struct squeek_layout_state *layout; // Unowned EekboardContextServicePrivate *priv;
}; };
struct _LayoutHolderClass { /**
* EekboardContextServiceClass:
* @create_keyboard: virtual function for create a keyboard from string
* @enabled: class handler for #EekboardContextService::enabled signal
* @disabled: class handler for #EekboardContextService::disabled signal
*/
struct _EekboardContextServiceClass {
/*< private >*/ /*< private >*/
GObjectClass parent_class; GObjectClass parent_class;
/*< public >*/
struct squeek_view *(*create_keyboard) (EekboardContextService *self,
const gchar *keyboard_type);
/* signals */
void (*destroyed) (EekboardContextService *self);
/*< private >*/ /*< private >*/
/* padding */ /* padding */
gpointer pdummy[24]; gpointer pdummy[24];
}; };
GType layout_holder_get_type(void) G_GNUC_CONST; GType eekboard_context_service_get_type
(void) G_GNUC_CONST;
EekboardContextService *eekboard_context_service_new(void);
void eekboard_context_service_set_submission(EekboardContextService *context, struct submission *submission);
void eekboard_context_service_set_ui(EekboardContextService *context, ServerContextService *ui);
void eekboard_context_service_destroy (EekboardContextService *context);
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
/// Handles gsettings os-level keyboard layout switches. void eekboard_context_service_set_keymap(EekboardContextService *context,
struct gsettings_tracker {
GSettings *gsettings; // Owned reference
LayoutHolder *context; // Unowned
struct squeek_layout_state *layout; // Unowned
};
void eek_gsettings_tracker_init(struct gsettings_tracker* tracker, LayoutHolder *context, struct squeek_layout_state *layout);
LayoutHolder *eek_layout_holder_new(struct squeek_layout_state *state);
void eek_layout_holder_set_submission(LayoutHolder *context, struct submission *submission);
LevelKeyboard *eek_layout_holder_get_keyboard(LayoutHolder *context);
void eekboard_context_service_set_keymap(LayoutHolder *context,
const LevelKeyboard *keyboard); const LevelKeyboard *keyboard);
void eekboard_context_service_set_hint_purpose(LayoutHolder *context, void eekboard_context_service_set_hint_purpose(EekboardContextService *context,
uint32_t hint, uint32_t hint,
uint32_t purpose); uint32_t purpose);
void void
eek_layout_holder_use_layout(LayoutHolder *context, struct squeek_layout_state *layout); eekboard_context_service_update_layout(EekboardContextService *context, enum squeek_arrangement_kind t);
G_END_DECLS G_END_DECLS
#endif /* EEKBOARD_CONTEXT_SERVICE_H */ #endif /* EEKBOARD_CONTEXT_SERVICE_H */

View File

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

View File

@ -10,11 +10,8 @@ pub struct KeySym(pub String);
type View = String; type View = String;
/// Use to send modified keypresses /// Use to send modified keypresses
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq)]
pub enum Modifier { pub enum Modifier {
/// Control and Alt are the only modifiers
/// which doesn't interfere with levels,
/// so it's simple to implement as levels are deprecated in squeekboard.
Control, Control,
Alt, Alt,
} }
@ -30,33 +27,14 @@ pub enum Action {
/// When unlocked by pressing it or emitting a key /// When unlocked by pressing it or emitting a key
unlock: View, unlock: View,
}, },
/// Hold this modifier for as long as the button is pressed /// Set this modifier TODO: release?
ApplyModifier(Modifier), SetModifier(Modifier),
/// Submit some text /// Submit some text
Submit { Submit {
/// Text to submit with input-method. /// Text to submit with input-method
/// If None, then keys are to be submitted instead.
text: Option<CString>, text: Option<CString>,
/// The key events this symbol submits when submitting text is not possible /// The key events this symbol submits when submitting text is not possible
keys: Vec<KeySym>, keys: Vec<KeySym>,
}, },
/// Erase a position behind the cursor
Erase,
ShowPreferences, ShowPreferences,
} }
impl Action {
pub fn is_locked(&self, view_name: &str) -> bool {
match self {
Action::LockView { lock, unlock: _ } => lock == view_name,
_ => false,
}
}
pub fn is_active(&self, view_name: &str) -> bool {
match self {
Action::SetView(view) => view == view_name,
Action::LockView { lock, unlock: _ } => lock == view_name,
_ => false,
}
}
}

View File

@ -15,7 +15,6 @@ use std::vec::Vec;
use xkbcommon::xkb; use xkbcommon::xkb;
use ::action;
use ::keyboard::{ use ::keyboard::{
KeyState, PressType, KeyState, PressType,
generate_keymap, generate_keycodes, FormattingError generate_keymap, generate_keycodes, FormattingError
@ -240,20 +239,14 @@ type ButtonIds = String;
#[derive(Debug, Default, Deserialize, PartialEq)] #[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
struct ButtonMeta { struct ButtonMeta {
// TODO: structure (action, keysym, text, modifier) as an enum /// Special action to perform on activation. Conflicts with keysym, text.
// to detect conflicts and missing values at compile time
/// Special action to perform on activation.
/// Conflicts with keysym, text, modifier.
action: Option<Action>, action: Option<Action>,
/// The name of the XKB keysym to emit on activation. /// The name of the XKB keysym to emit on activation.
/// Conflicts with action, text, modifier. /// Conflicts with action, text
keysym: Option<String>, keysym: Option<String>,
/// The text to submit on activation. Will be derived from ID if not present /// The text to submit on activation. Will be derived from ID if not present
/// Conflicts with action, keysym, modifier. /// Conflicts with action, keysym
text: Option<String>, text: Option<String>,
/// The modifier to apply while the key is locked
/// Conflicts with action, keysym, text
modifier: Option<Modifier>,
/// If not present, will be derived from text or the button ID /// If not present, will be derived from text or the button ID
label: Option<String>, label: Option<String>,
/// Conflicts with label /// Conflicts with label
@ -271,23 +264,6 @@ enum Action {
SetView(String), SetView(String),
#[serde(rename="show_prefs")] #[serde(rename="show_prefs")]
ShowPrefs, ShowPrefs,
/// Remove last character
#[serde(rename="erase")]
Erase,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
enum Modifier {
Control,
Shift,
Lock,
#[serde(alias="Mod1")]
Alt,
Mod2,
Mod3,
Mod4,
Mod5,
} }
#[derive(Debug, Clone, Deserialize, PartialEq)] #[derive(Debug, Clone, Deserialize, PartialEq)]
@ -410,16 +386,13 @@ impl Layout {
) )
}).collect() }).collect()
}, },
action::Action::Erase => vec![
*keymap.get("BackSpace")
.expect(&format!("BackSpace missing from keymap")),
],
_ => Vec::new(), _ => Vec::new(),
}; };
( (
name.into(), name.into(),
KeyState { KeyState {
pressed: PressType::Released, pressed: PressType::Released,
locked: false,
keycodes, keycodes,
action, action,
} }
@ -444,8 +417,8 @@ impl Layout {
)} )}
); );
let views: Vec<_> = self.views.iter() let views = HashMap::from_iter(
.map(|(name, view)| { self.views.iter().map(|(name, view)| {
let rows = view.iter().map(|row| { let rows = view.iter().map(|row| {
let buttons = row.split_ascii_whitespace() let buttons = row.split_ascii_whitespace()
.map(|name| { .map(|name| {
@ -459,7 +432,8 @@ impl Layout {
&mut warning_handler, &mut warning_handler,
)) ))
}); });
layout::Row { ::layout::Row {
angle: 0,
buttons: add_offsets( buttons: add_offsets(
buttons, buttons,
|button| button.size.width, |button| button.size.width,
@ -472,25 +446,8 @@ impl Layout {
name.clone(), name.clone(),
layout::View::new(rows) layout::View::new(rows)
) )
}).collect(); })
);
// Center views on the same point.
let views = {
let total_size = layout::View::calculate_super_size(
views.iter().map(|(_name, view)| view).collect()
);
HashMap::from_iter(views.into_iter().map(|(name, view)| (
name,
(
layout::c::Point {
x: (total_size.width - view.get_width()) / 2.0,
y: (total_size.height - view.get_height()) / 2.0,
},
view,
),
)))
};
( (
Ok(::layout::LayoutData { Ok(::layout::LayoutData {
@ -530,27 +487,22 @@ fn create_action<H: logging::Handler>(
Action(Action), Action(Action),
Text(String), Text(String),
Keysym(String), Keysym(String),
Modifier(Modifier),
}; };
let submission = match ( let submission = match (
&symbol_meta.action, &symbol_meta.action,
&symbol_meta.keysym, &symbol_meta.keysym,
&symbol_meta.text, &symbol_meta.text
&symbol_meta.modifier,
) { ) {
(Some(action), None, None, None) => SubmitData::Action(action.clone()), (Some(action), None, None) => SubmitData::Action(action.clone()),
(None, Some(keysym), None, None) => SubmitData::Keysym(keysym.clone()), (None, Some(keysym), None) => SubmitData::Keysym(keysym.clone()),
(None, None, Some(text), None) => SubmitData::Text(text.clone()), (None, None, Some(text)) => SubmitData::Text(text.clone()),
(None, None, None, Some(modifier)) => { (None, None, None) => SubmitData::Text(name.into()),
SubmitData::Modifier(modifier.clone())
},
(None, None, None, None) => SubmitData::Text(name.into()),
_ => { _ => {
warning_handler.handle( warning_handler.handle(
logging::Level::Warning, logging::Level::Warning,
&format!( &format!(
"Button {} has more than one of (action, keysym, text, modifier)", "Button {} has more than one of (action, keysym, text)",
name, name,
), ),
); );
@ -606,7 +558,6 @@ fn create_action<H: logging::Handler>(
SubmitData::Action( SubmitData::Action(
Action::ShowPrefs Action::ShowPrefs
) => ::action::Action::ShowPreferences, ) => ::action::Action::ShowPreferences,
SubmitData::Action(Action::Erase) => action::Action::Erase,
SubmitData::Keysym(keysym) => ::action::Action::Submit { SubmitData::Keysym(keysym) => ::action::Action::Submit {
text: None, text: None,
keys: vec!(::action::KeySym( keys: vec!(::action::KeySym(
@ -638,27 +589,7 @@ fn create_action<H: logging::Handler>(
false => format!("U{:04X}", codepoint as u32), false => format!("U{:04X}", codepoint as u32),
}) })
}).collect(), }).collect(),
}, }
SubmitData::Modifier(modifier) => match modifier {
Modifier::Control => action::Action::ApplyModifier(
action::Modifier::Control,
),
Modifier::Alt => action::Action::ApplyModifier(
action::Modifier::Alt,
),
unsupported_modifier => {
warning_handler.handle(
logging::Level::Bug,
&format!(
"Modifier {:?} unsupported", unsupported_modifier,
),
);
action::Action::Submit {
text: None,
keys: Vec::new(),
}
},
},
} }
} }
@ -756,7 +687,6 @@ mod tests {
keysym: None, keysym: None,
action: None, action: None,
text: None, text: None,
modifier: None,
label: Some("test".into()), label: Some("test".into()),
outline: None, outline: None,
} }
@ -813,7 +743,7 @@ mod tests {
.build(ProblemPanic).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.buttons[0].1 .buttons[0].1
.label, .label,
@ -828,7 +758,7 @@ mod tests {
.build(ProblemPanic).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.buttons[0].1 .buttons[0].1
.label, .label,
@ -844,7 +774,7 @@ mod tests {
.build(ProblemPanic).0 .build(ProblemPanic).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"].1 out.views["base"]
.get_rows()[0].1 .get_rows()[0].1
.buttons[0].1 .buttons[0].1
.state.borrow() .state.borrow()
@ -898,7 +828,6 @@ mod tests {
keysym: None, keysym: None,
text: None, text: None,
action: None, action: None,
modifier: None,
label: Some("test".into()), label: Some("test".into()),
outline: None, outline: None,
} }

View File

@ -3,11 +3,9 @@
use cairo; use cairo;
use std::cell::RefCell; use std::cell::RefCell;
use ::action::Action;
use ::keyboard; use ::keyboard;
use ::layout::{ Button, Layout }; use ::layout::{ Button, Layout };
use ::layout::c::{ EekGtkKeyboard, Point }; use ::layout::c::{ EekGtkKeyboard, Point };
use ::submission::Submission;
use glib::translate::FromGlibPtrNone; use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt; use gtk::WidgetExt;
@ -39,37 +37,30 @@ mod c {
); );
} }
/// Draws all buttons that are not in the base state
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_layout_draw_all_changed( fn squeek_layout_draw_all_changed(
layout: *mut Layout, layout: *mut Layout,
renderer: EekRenderer, renderer: EekRenderer,
cr: *mut cairo_sys::cairo_t, cr: *mut cairo_sys::cairo_t,
submission: *const Submission,
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let submission = unsafe { &*submission };
let cr = unsafe { cairo::Context::from_raw_none(cr) }; let cr = unsafe { cairo::Context::from_raw_none(cr) };
let active_modifiers = submission.get_active_modifiers();
layout.foreach_visible_button(|offset, button| { let view = layout.get_current_view();
let state = RefCell::borrow(&button.state).clone(); for (row_offset, row) in &view.get_rows() {
let active_mod = match &state.action { for (x_offset, button) in &row.buttons {
Action::ApplyModifier(m) => active_modifiers.contains(m), let state = RefCell::borrow(&button.state).clone();
_ => false, if state.pressed == keyboard::PressType::Pressed || state.locked {
}; render_button_at_position(
let locked = state.action.is_active(&layout.current_view) renderer, &cr,
| active_mod; row_offset + Point { x: *x_offset, y: 0.0 },
if state.pressed == keyboard::PressType::Pressed || locked { button.as_ref(),
render_button_at_position( state.pressed, state.locked,
renderer, &cr, );
offset, }
button.as_ref(),
state.pressed, locked,
);
} }
}) }
} }
#[no_mangle] #[no_mangle]
@ -81,15 +72,17 @@ mod c {
) { ) {
let layout = unsafe { &mut *layout }; let layout = unsafe { &mut *layout };
let cr = unsafe { cairo::Context::from_raw_none(cr) }; let cr = unsafe { cairo::Context::from_raw_none(cr) };
let view = layout.get_current_view();
layout.foreach_visible_button(|offset, button| { for (row_offset, row) in &view.get_rows() {
render_button_at_position( for (x_offset, button) in &row.buttons {
renderer, &cr, render_button_at_position(
offset, renderer, &cr,
button.as_ref(), row_offset + Point { x: *x_offset, y: 0.0 },
keyboard::PressType::Released, false, button.as_ref(),
); keyboard::PressType::Released, false,
}) );
}
}
} }
} }

View File

@ -26,7 +26,7 @@ static const struct zwp_input_method_v2_listener input_method_listener = {
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,
struct wl_seat *seat, struct wl_seat *seat,
LayoutHolder *state) { EekboardContextService *state) {
struct zwp_input_method_v2 *im = NULL; struct zwp_input_method_v2 *im = NULL;
if (immanager) { if (immanager) {
im = zwp_input_method_manager_v2_get_input_method(immanager, seat); im = zwp_input_method_manager_v2_get_input_method(immanager, seat);
@ -49,23 +49,6 @@ void imservice_connect_listeners(struct zwp_input_method_v2 *im, struct imservic
zwp_input_method_v2_add_listener(im, &input_method_listener, imservice); zwp_input_method_v2_add_listener(im, &input_method_listener, imservice);
} }
void
eek_input_method_commit_string(struct zwp_input_method_v2 *zwp_input_method_v2, const char *text)
{
zwp_input_method_v2_commit_string(zwp_input_method_v2, text);
}
void
eek_input_method_delete_surrounding_text(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t before_length, uint32_t after_length) {
zwp_input_method_v2_delete_surrounding_text(zwp_input_method_v2, before_length, after_length);
};
void
eek_input_method_commit(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t serial)
{
zwp_input_method_v2_commit(zwp_input_method_v2, serial);
}
/// Declared explicitly because _destroy is inline, /// Declared explicitly because _destroy is inline,
/// making it unavailable in Rust /// making it unavailable in Rust
void imservice_destroy_im(struct zwp_input_method_v2 *im) { void imservice_destroy_im(struct zwp_input_method_v2 *im) {

View File

@ -1,8 +1,3 @@
/*! Manages zwp_input_method_v2 protocol.
*
* Library module.
*/
use std::boxed::Box; use std::boxed::Box;
use std::ffi::CString; use std::ffi::CString;
use std::fmt; use std::fmt;
@ -37,9 +32,6 @@ pub mod c {
fn imservice_destroy_im(im: *mut c::InputMethod); fn imservice_destroy_im(im: *mut c::InputMethod);
#[allow(improper_ctypes)] // IMService will never be dereferenced in C #[allow(improper_ctypes)] // IMService will never be dereferenced in C
pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService); pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService);
pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char);
pub fn eek_input_method_delete_surrounding_text(im: *mut InputMethod, before: u32, after: u32);
pub fn eek_input_method_commit(im: *mut InputMethod, serial: u32);
fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32); fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32);
fn server_context_service_show_keyboard(imservice: *const UIManager); fn server_context_service_show_keyboard(imservice: *const UIManager);
fn server_context_service_hide_keyboard(imservice: *const UIManager); fn server_context_service_hide_keyboard(imservice: *const UIManager);
@ -336,7 +328,7 @@ impl Default for IMProtocolState {
pub struct IMService { pub struct IMService {
/// Owned reference (still created and destroyed in C) /// Owned reference (still created and destroyed in C)
pub im: *mut c::InputMethod, pub im: *const c::InputMethod,
/// Unowned reference. Be careful, it's shared with C at large /// Unowned reference. Be careful, it's shared with C at large
state_manager: *const c::StateManager, state_manager: *const c::StateManager,
/// Unowned reference. Be careful, it's shared with C at large /// Unowned reference. Be careful, it's shared with C at large
@ -348,11 +340,6 @@ pub struct IMService {
serial: Wrapping<u32>, serial: Wrapping<u32>,
} }
pub enum SubmitError {
/// The input method had not been activated
NotActive,
}
impl IMService { impl IMService {
pub fn new( pub fn new(
im: *mut c::InputMethod, im: *mut c::InputMethod,
@ -377,51 +364,4 @@ impl IMService {
} }
imservice imservice
} }
pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_commit_string(self.im, text.as_ptr())
}
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn delete_surrounding_text(
&self,
before: u32, after: u32,
) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_delete_surrounding_text(
self.im,
before, after,
)
}
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn commit(&mut self) -> Result<(), SubmitError> {
match self.current.active {
true => {
unsafe {
c::eek_input_method_commit(self.im, self.serial.0)
}
self.serial += Wrapping(1u32);
Ok(())
},
false => Err(SubmitError::NotActive),
}
}
pub fn is_active(&self) -> bool {
self.current.active
}
} }

View File

@ -1,17 +1,14 @@
/*! State of the emulated keyboard and keys. /*! State of the emulated keyboard and keys.
* Regards the keyboard as if it was composed of switches. */ * Regards the keyboard as if it was composed of switches. */
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::rc::Rc;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action; use ::action::Action;
use ::logging; use ::logging;
// Traits
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
@ -23,39 +20,28 @@ pub enum PressType {
pub type KeyCode = u32; pub type KeyCode = u32;
bitflags!{
/// Map to `virtual_keyboard.modifiers` modifiers values
/// From https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Keyboard_State
pub struct Modifiers: u8 {
const SHIFT = 0x1;
const LOCK = 0x2;
const CONTROL = 0x4;
/// Alt
const MOD1 = 0x8;
const MOD2 = 0x10;
const MOD3 = 0x20;
/// Meta
const MOD4 = 0x40;
/// AltGr
const MOD5 = 0x80;
}
}
/// When the submitted actions of keys need to be tracked,
/// they need a stable, comparable ID
#[derive(PartialEq)]
pub struct KeyStateId(*const KeyState);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct KeyState { pub struct KeyState {
pub pressed: PressType, pub pressed: PressType,
/// A cache of raw keycodes derived from Action::Submit given a keymap pub locked: bool,
/// A cache of raw keycodes derived from Action::Sumbit given a keymap
pub keycodes: Vec<KeyCode>, pub keycodes: Vec<KeyCode>,
/// Static description of what the key does when pressed or released /// Static description of what the key does when pressed or released
pub action: Action, pub action: Action,
} }
impl KeyState { impl KeyState {
#[must_use]
pub fn into_activated(self) -> KeyState {
match self.action {
Action::LockView { lock: _, unlock: _ } => KeyState {
locked: self.locked ^ true,
..self
},
_ => self,
}
}
#[must_use] #[must_use]
pub fn into_released(self) -> KeyState { pub fn into_released(self) -> KeyState {
KeyState { KeyState {
@ -63,20 +49,6 @@ impl KeyState {
..self ..self
} }
} }
#[must_use]
pub fn into_pressed(self) -> KeyState {
KeyState {
pressed: PressType::Pressed,
..self
}
}
/// KeyStates instances are the unique identifiers of pressed keys,
/// and the actions submitted with them.
pub fn get_id(keystate: &Rc<RefCell<KeyState>>) -> KeyStateId {
KeyStateId(keystate.as_ptr() as *const KeyState)
}
} }
/// Sorts an iterator by converting it to a Vector and back /// Sorts an iterator by converting it to a Vector and back
@ -94,10 +66,9 @@ fn sorted<'a, I: Iterator<Item=&'a str>>(
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>( pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C key_names: C
) -> HashMap<String, u32> { ) -> HashMap<String, u32> {
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)) .map(|name| String::from(name))
.zip(9..) .zip(9..)
) )
@ -124,10 +95,7 @@ impl From<io::Error> for FormattingError {
} }
} }
/// Generates a de-facto single level keymap. /// Generates a de-facto single level keymap. TODO: actually drop second level
// TODO: don't rely on keys and their order,
// but rather on what keysyms and keycodes are in use.
// Iterating actions makes it hard to deduplicate keysyms.
pub fn generate_keymap( pub fn generate_keymap(
keystates: &HashMap::<String, KeyState> keystates: &HashMap::<String, KeyState>
) -> Result<String, FormattingError> { ) -> Result<String, FormattingError> {
@ -142,40 +110,22 @@ pub fn generate_keymap(
)?; )?;
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
match &state.action { if let Action::Submit { text: _, keys } = &state.action {
Action::Submit { text: _, keys } => { if let 0 = keys.len() {
if let 0 = keys.len() { log_print!(
log_print!( logging::Level::Warning,
logging::Level::Warning, "Key {} has no keysyms", name,
"Key {} has no keysyms", name, );
); };
}; for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
write!(
buf,
"
<{}> = {};",
named_keysym.0,
keycode,
)?;
}
},
Action::Erase => {
let mut keycodes = state.keycodes.iter();
write!( write!(
buf, buf,
" "
<BackSpace> = {};", <{}> = {};",
keycodes.next().expect("Erase key has no keycode"), named_keysym.0,
keycode,
)?; )?;
if let Some(_) = keycodes.next() { }
log_print!(
logging::Level::Bug,
"Erase key has multiple keycodes",
);
}
},
_ => {},
} }
} }
@ -187,9 +137,7 @@ pub fn generate_keymap(
xkb_symbols \"squeekboard\" {{ xkb_symbols \"squeekboard\" {{
name[Group1] = \"Letters\"; name[Group1] = \"Letters\";
name[Group2] = \"Numbers/Symbols\"; name[Group2] = \"Numbers/Symbols\";"
key <BackSpace> {{ [ BackSpace ] }};"
)?; )?;
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
@ -247,6 +195,7 @@ mod tests {
keys: vec!(KeySym("a".into()), KeySym("c".into())), keys: vec!(KeySym("a".into()), KeySym("c".into())),
}, },
keycodes: vec!(9, 10), keycodes: vec!(9, 10),
locked: false,
pressed: PressType::Released, pressed: PressType::Released,
}, },
}).unwrap(); }).unwrap();

View File

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

View File

@ -26,10 +26,10 @@ use std::vec::Vec;
use ::action::Action; use ::action::Action;
use ::drawing; use ::drawing;
use ::keyboard::KeyState; use ::keyboard::{ KeyState, PressType };
use ::logging; use ::logging;
use ::manager; use ::manager;
use ::submission::{ Submission, SubmitData, Timestamp }; use ::submission::{ Submission, Timestamp };
use ::util::find_max_double; use ::util::find_max_double;
// Traits // Traits
@ -249,7 +249,7 @@ pub mod c {
unsafe { Box::from_raw(layout) }; unsafe { Box::from_raw(layout) };
} }
/// Entry points for more complex procedures and algorithms which span multiple modules /// Entry points for more complex procedures and algoithms which span multiple modules
pub mod procedures { pub mod procedures {
use super::*; use super::*;
@ -329,8 +329,11 @@ pub mod c {
Point { x: x_widget, y: y_widget } Point { x: x_widget, y: y_widget }
); );
let state = layout.find_button_by_position(point) let state = {
.map(|place| place.button.state.clone()); let view = layout.get_current_view();
view.find_button_by_position(point)
.map(|place| place.button.state.clone())
};
if let Some(state) = state { if let Some(state) = state {
seat::handle_press_key( seat::handle_press_key(
@ -371,7 +374,8 @@ pub mod c {
let pressed = layout.pressed_keys.clone(); let pressed = layout.pressed_keys.clone();
let button_info = { let button_info = {
let place = layout.find_button_by_position(point); let view = layout.get_current_view();
let place = view.find_button_by_position(point);
place.map(|place| {( place.map(|place| {(
place.button.state.clone(), place.button.state.clone(),
place.button.clone(), place.button.clone(),
@ -482,6 +486,8 @@ pub struct Button {
pub struct Row { pub struct Row {
/// Buttons together with their offset from the left /// Buttons together with their offset from the left
pub buttons: Vec<(f64, Box<Button>)>, pub buttons: Vec<(f64, Box<Button>)>,
/// Angle is not really used anywhere...
pub angle: i32,
} }
impl Row { impl Row {
@ -548,14 +554,14 @@ impl View {
}) })
} }
pub fn get_width(&self) -> f64 { fn get_width(&self) -> f64 {
// No need to call `get_rows()`, // No need to call `get_rows()`,
// as the biggest row is the most far reaching in both directions // as the biggest row is the most far reaching in both directions
// because they are all centered. // because they are all centered.
find_max_double(self.rows.iter(), |(_offset, row)| row.get_width()) find_max_double(self.rows.iter(), |(_offset, row)| row.get_width())
} }
pub fn get_height(&self) -> f64 { fn get_height(&self) -> f64 {
self.rows.iter().next_back() self.rows.iter().next_back()
.map(|(y_offset, row)| row.get_height() + y_offset) .map(|(y_offset, row)| row.get_height() + y_offset)
.unwrap_or(0.0) .unwrap_or(0.0)
@ -572,21 +578,6 @@ impl View {
row, row,
)}).collect() )}).collect()
} }
/// Returns a size which contains all the views
/// if they are all centered on the same point.
pub fn calculate_super_size(views: Vec<&View>) -> Size {
Size {
height: find_max_double(
views.iter(),
|view| view.get_height(),
),
width: find_max_double(
views.iter(),
|view| view.get_width(),
),
}
}
} }
/// The physical characteristic of layout for the purpose of styling /// The physical characteristic of layout for the purpose of styling
@ -614,8 +605,7 @@ pub struct Layout {
// Views own the actual buttons which have state // Views own the actual buttons which have state
// Maybe they should own UI only, // Maybe they should own UI only,
// and keys should be owned by a dedicated non-UI-State? // and keys should be owned by a dedicated non-UI-State?
/// Point is the offset within the layout pub views: HashMap<String, View>,
pub views: HashMap<String, (c::Point, View)>,
// Non-UI stuff // Non-UI stuff
/// xkb keymap applicable to the contained keys. Unchangeable /// xkb keymap applicable to the contained keys. Unchangeable
@ -629,12 +619,12 @@ pub struct Layout {
// When the list tracks actual location, // When the list tracks actual location,
// it becomes possible to place popovers and other UI accurately. // it becomes possible to place popovers and other UI accurately.
pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>, pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
pub locked_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
} }
/// A builder structure for picking up layout data from storage /// A builder structure for picking up layout data from storage
pub struct LayoutData { pub struct LayoutData {
/// Point is the offset within layout pub views: HashMap<String, View>,
pub views: HashMap<String, (c::Point, View)>,
pub keymap_str: CString, pub keymap_str: CString,
pub margins: Margins, pub margins: Margins,
} }
@ -660,17 +650,13 @@ impl Layout {
views: data.views, views: data.views,
keymap_str: data.keymap_str, keymap_str: data.keymap_str,
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
locked_keys: HashSet::new(),
margins: data.margins, margins: data.margins,
} }
} }
pub fn get_current_view_position(&self) -> &(c::Point, View) {
&self.views
.get(&self.current_view).expect("Selected nonexistent view")
}
pub fn get_current_view(&self) -> &View { pub fn get_current_view(&self) -> &View {
&self.views.get(&self.current_view).expect("Selected nonexistent view").1 self.views.get(&self.current_view).expect("Selected nonexistent view")
} }
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> { fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
@ -684,9 +670,16 @@ impl Layout {
/// Calculates size without margins /// Calculates size without margins
fn calculate_inner_size(&self) -> Size { fn calculate_inner_size(&self) -> Size {
View::calculate_super_size( Size {
self.views.iter().map(|(_, (_offset, v))| v).collect() height: find_max_double(
) self.views.iter(),
|(_name, view)| view.get_height(),
),
width: find_max_double(
self.views.iter(),
|(_name, view)| view.get_width(),
),
}
} }
/// Size including margins /// Size including margins
@ -721,42 +714,6 @@ impl Layout {
scale: 1.0, scale: 1.0,
}) })
} }
fn find_button_by_position(&self, point: c::Point) -> Option<ButtonPlace> {
let (offset, layout) = self.get_current_view_position();
layout.find_button_by_position(point - offset)
}
pub fn foreach_visible_button<F>(&self, mut f: F)
where F: FnMut(c::Point, &Box<Button>)
{
let (view_offset, view) = self.get_current_view_position();
for (row_offset, row) in &view.get_rows() {
for (x_offset, button) in &row.buttons {
let offset = view_offset
+ row_offset.clone()
+ c::Point { x: *x_offset, y: 0.0 };
f(offset, button);
}
}
}
pub fn get_locked_keys(&self) -> Vec<Rc<RefCell<KeyState>>> {
let mut out = Vec::new();
let view = self.get_current_view();
for (_, row) in &view.get_rows() {
for (_, button) in &row.buttons {
let locked = {
let state = RefCell::borrow(&button.state).clone();
state.action.is_locked(&self.current_view)
};
if locked {
out.push(button.state.clone());
}
}
}
out
}
} }
mod procedures { mod procedures {
@ -808,6 +765,7 @@ mod procedures {
let row = Row { let row = Row {
buttons: vec!((0.1, button)), buttons: vec!((0.1, button)),
angle: 0,
}; };
let view = View { let view = View {
@ -883,9 +841,9 @@ mod seat {
#[must_use] #[must_use]
fn unstick_locks(layout: &mut Layout) -> ViewChange { fn unstick_locks(layout: &mut Layout) -> ViewChange {
let mut new_view = None; let mut new_view = None;
for key in layout.get_locked_keys().clone() { for key in layout.locked_keys.clone() {
let key: &Rc<RefCell<KeyState>> = key.borrow(); let key: &Rc<RefCell<KeyState>> = key.borrow();
let key = RefCell::borrow(key); let mut key = RefCell::borrow_mut(key);
match &key.action { match &key.action {
Action::LockView { lock: _, unlock: view } => { Action::LockView { lock: _, unlock: view } => {
new_view = Some(view.clone()); new_view = Some(view.clone());
@ -896,6 +854,7 @@ mod seat {
a, a,
), ),
}; };
key.locked = false;
} }
ViewChange { ViewChange {
@ -916,38 +875,13 @@ mod seat {
"Key {:?} was already pressed", rckey, "Key {:?} was already pressed", rckey,
); );
} }
let key: KeyState = { let mut key = rckey.borrow_mut();
RefCell::borrow(rckey).clone() submission.virtual_keyboard.switch(
}; &key.keycodes,
let action = key.action.clone(); PressType::Pressed,
match action { time,
Action::Submit { );
text: Some(text), key.pressed = PressType::Pressed;
keys: _,
} => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Text(&text),
&key.keycodes,
time,
),
Action::Submit {
text: None,
keys: _,
} => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Keycodes,
&key.keycodes,
time,
),
Action::Erase => submission.handle_press(
KeyState::get_id(rckey),
SubmitData::Erase,
&key.keycodes,
time,
),
_ => {},
};
RefCell::replace(rckey, key.into_pressed());
} }
pub fn handle_release_key( pub fn handle_release_key(
@ -965,43 +899,38 @@ mod seat {
// update // update
let key = key.into_released(); let key = key.into_released();
let key = match action {
Action::LockView { lock: _, unlock: _ } => key.into_activated(),
_ => key,
};
// process changes // process changes
match action { match action {
Action::Submit { text: _, keys: _ } Action::Submit { text: _, keys: _ } => {
| Action::Erase
=> {
unstick_locks(layout).apply(); unstick_locks(layout).apply();
submission.handle_release(KeyState::get_id(rckey), time); submission.virtual_keyboard.switch(
&key.keycodes,
PressType::Released,
time,
);
}, },
Action::SetView(view) => { Action::SetView(view) => {
try_set_view(layout, view) try_set_view(layout, view)
}, },
Action::LockView { lock, unlock } => { Action::LockView { lock, unlock } => {
let gets_locked = !key.action.is_locked(&layout.current_view); // The button that triggered this will be in the right state
// due to commit at the end.
unstick_locks(layout) unstick_locks(layout)
// It doesn't matter what the resulting view should be, // It doesn't matter what the resulting view should be,
// it's getting changed anyway. // it's getting changed anyway.
.choose_view( .choose_view(
match gets_locked { match key.locked {
true => lock.clone(), true => lock.clone(),
false => unlock.clone(), false => unlock.clone(),
} }
) )
.apply() .apply()
}, },
Action::ApplyModifier(modifier) => {
// FIXME: key id is unneeded with stateless locks
let key_id = KeyState::get_id(rckey);
let gets_locked = !submission.is_modifier_active(modifier.clone());
match gets_locked {
true => submission.handle_add_modifier(
key_id,
modifier, time,
),
false => submission.handle_drop_modifier(key_id, time),
}
}
// only show when UI is present // only show when UI is present
Action::ShowPreferences => if let Some(ui) = &ui { Action::ShowPreferences => if let Some(ui) = &ui {
// only show when layout manager is available // only show when layout manager is available
@ -1028,11 +957,20 @@ mod seat {
} }
} }
}, },
Action::SetModifier(_) => log_print!(
logging::Level::Bug,
"Modifiers unsupported",
),
}; };
let pointer = ::util::Pointer(rckey.clone()); let pointer = ::util::Pointer(rckey.clone());
// Apply state changes // Apply state changes
layout.pressed_keys.remove(&pointer); layout.pressed_keys.remove(&pointer);
if key.locked {
layout.locked_keys.insert(pointer);
} else {
layout.locked_keys.remove(&pointer);
}
// Commit activated button state changes // Commit activated button state changes
RefCell::replace(rckey, key); RefCell::replace(rckey, key);
} }
@ -1043,11 +981,11 @@ mod test {
use super::*; use super::*;
use std::ffi::CString; use std::ffi::CString;
use ::keyboard::PressType;
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> { pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
Rc::new(RefCell::new(::keyboard::KeyState { Rc::new(RefCell::new(::keyboard::KeyState {
pressed: PressType::Released, pressed: PressType::Released,
locked: false,
keycodes: Vec::new(), keycodes: Vec::new(),
action: Action::SetView("default".into()), action: Action::SetView("default".into()),
})) }))
@ -1074,6 +1012,7 @@ mod test {
( (
0.0, 0.0,
Row { Row {
angle: 0,
buttons: vec![( buttons: vec![(
0.0, 0.0,
Box::new(Button { Box::new(Button {
@ -1086,6 +1025,7 @@ mod test {
( (
10.0, 10.0,
Row { Row {
angle: 0,
buttons: vec![( buttons: vec![(
0.0, 0.0,
Box::new(Button { Box::new(Button {
@ -1109,6 +1049,7 @@ mod test {
( (
0.0, 0.0,
Row { Row {
angle: 0,
buttons: vec![( buttons: vec![(
0.0, 0.0,
Box::new(Button { Box::new(Button {
@ -1123,6 +1064,7 @@ mod test {
current_view: String::new(), current_view: String::new(),
keymap_str: CString::new("").unwrap(), keymap_str: CString::new("").unwrap(),
kind: ArrangementKind::Base, kind: ArrangementKind::Base,
locked_keys: HashSet::new(),
pressed_keys: HashSet::new(), pressed_keys: HashSet::new(),
// Lots of bottom margin // Lots of bottom margin
margins: Margins { margins: Margins {
@ -1132,7 +1074,7 @@ mod test {
bottom: 1.0, bottom: 1.0,
}, },
views: hashmap! { views: hashmap! {
String::new() => (c::Point { x: 0.0, y: 0.0 }, view), String::new() => view,
}, },
}; };
assert_eq!( assert_eq!(

View File

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

View File

@ -15,7 +15,6 @@ sources = [
'dbus.c', 'dbus.c',
'imservice.c', 'imservice.c',
'server-context-service.c', 'server-context-service.c',
'ui_manager.c',
'wayland.c', 'wayland.c',
'../eek/eek.c', '../eek/eek.c',
'../eek/eek-element.c', '../eek/eek-element.c',

View File

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

View File

@ -1,10 +1,5 @@
/* Copyright (C) 2019-2020 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Managing Wayland outputs */ /*! Managing Wayland outputs */
use std::cell::RefCell;
use std::vec::Vec; use std::vec::Vec;
use ::logging; use ::logging;
@ -22,7 +17,7 @@ pub mod c {
// Defined in C // Defined in C
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, PartialEq, Copy, Hash)] #[derive(Clone, PartialEq)]
pub struct WlOutput(*const c_void); pub struct WlOutput(*const c_void);
#[repr(C)] #[repr(C)]
@ -68,7 +63,7 @@ pub mod c {
} }
/// Map to `wl_output.transform` values /// Map to `wl_output.transform` values
#[derive(Clone, PartialEq)] #[derive(Clone)]
pub enum Transform { pub enum Transform {
Normal = 0, Normal = 0,
Rotated90 = 1, Rotated90 = 1,
@ -108,29 +103,7 @@ pub mod c {
) -> i32; ) -> i32;
} }
pub type COutputs = ::util::c::Wrapped<Outputs>; type COutputs = ::util::c::Wrapped<Outputs>;
/// A stable reference to an output.
#[derive(Clone)]
#[repr(C)]
pub struct OutputHandle {
wl_output: WlOutput,
outputs: COutputs,
}
impl OutputHandle {
// Cannot return an Output reference
// because COutputs is too deeply wrapped
pub fn get_state(&self) -> Option<OutputState> {
let outputs = self.outputs.clone_ref();
let outputs = outputs.borrow();
find_output(&outputs, self.wl_output.clone()).map(|o| o.current.clone())
}
pub fn get_id(&self) -> OutputId {
OutputId { wl_output: self.wl_output }
}
}
// Defined in Rust // Defined in Rust
@ -138,7 +111,7 @@ pub mod c {
outputs: COutputs, outputs: COutputs,
wl_output: WlOutput, wl_output: WlOutput,
_x: i32, _y: i32, _x: i32, _y: i32,
phys_width: i32, phys_height: i32, _phys_width: i32, _phys_height: i32,
_subpixel: i32, _subpixel: i32,
_make: *const c_char, _model: *const c_char, _make: *const c_char, _model: *const c_char,
transform: i32, transform: i32,
@ -154,23 +127,8 @@ pub mod c {
let output_state: Option<&mut OutputState> let output_state: Option<&mut OutputState>
= find_output_mut(&mut collection, wl_output) = find_output_mut(&mut collection, wl_output)
.map(|o| &mut o.pending); .map(|o| &mut o.pending);
match output_state { match output_state {
Some(state) => { Some(state) => { state.transform = Some(transform) },
state.transform = Some(transform);
state.phys_size = {
if (phys_width > 0) & (phys_height > 0) {
Some(SizeMM { width: phys_width, height: phys_height })
} else {
log_print!(
logging::Level::Surprise,
"Impossible physical dimensions: {}mm × {}mm",
phys_width, phys_height,
);
None
}
}
},
None => log_print!( None => log_print!(
logging::Level::Warning, logging::Level::Warning,
"Got geometry on unknown output", "Got geometry on unknown output",
@ -211,27 +169,19 @@ pub mod c {
} }
extern fn outputs_handle_done( extern fn outputs_handle_done(
outputs_raw: COutputs, outputs: COutputs,
wl_output: WlOutput, wl_output: WlOutput,
) { ) {
let outputs = outputs_raw.clone_ref(); let outputs = outputs.clone_ref();
{ let mut collection = outputs.borrow_mut();
let mut collection = RefCell::borrow_mut(&outputs); let output = find_output_mut(&mut collection, wl_output);
let output = find_output_mut(&mut collection, wl_output); match output {
match output { Some(output) => { output.current = output.pending.clone(); }
Some(output) => { output.current = output.pending.clone(); } None => log_print!(
None => log_print!( logging::Level::Warning,
logging::Level::Warning, "Got done on unknown output",
"Got done on unknown output", ),
), };
};
}
let collection = RefCell::borrow(&outputs);
if let Some(ref cb) = &collection.update_cb {
let mut cb = RefCell::borrow_mut(cb);
let cb = Box::as_mut(&mut cb);
cb(OutputHandle { wl_output, outputs: outputs_raw });
}
} }
extern fn outputs_handle_scale( extern fn outputs_handle_scale(
@ -256,10 +206,7 @@ pub mod c {
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_outputs_new() -> COutputs { fn squeek_outputs_new() -> COutputs {
COutputs::new(Outputs { COutputs::new(Outputs { outputs: Vec::new() })
outputs: Vec::new(),
update_cb: None,
})
} }
#[no_mangle] #[no_mangle]
@ -293,15 +240,46 @@ pub mod c {
#[no_mangle] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_outputs_get_current(raw_collection: COutputs) -> OutputHandle { fn squeek_outputs_get_current(raw_collection: COutputs) -> WlOutput {
let collection = raw_collection.clone_ref(); let collection = raw_collection.clone_ref();
let collection = collection.borrow(); let collection = collection.borrow();
OutputHandle { collection.outputs[0].output.clone()
wl_output: collection.outputs[0].output.clone(),
outputs: raw_collection.clone(),
}
} }
#[no_mangle]
pub extern "C"
fn squeek_outputs_get_perceptual_width(
raw_collection: COutputs,
wl_output: WlOutput,
) -> i32 {
let collection = raw_collection.clone_ref();
let collection = collection.borrow();
let output_state = find_output(&collection, wl_output)
.map(|o| &o.current);
match output_state {
Some(OutputState {
current_mode: Some(super::Mode { width, height } ),
transform: Some(transform),
scale,
}) => {
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => width / scale,
_ => height / scale,
}
},
_ => {
log_print!(
logging::Level::Surprise,
"Not enough info received on output",
);
0
},
}
}
// TODO: handle unregistration // TODO: handle unregistration
fn find_output( fn find_output(
@ -327,112 +305,27 @@ pub mod c {
} }
} }
/// Generic size #[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Size {
pub width: u32,
pub height: u32,
}
#[derive(Clone, PartialEq)]
pub struct SizeMM {
pub width: i32,
pub height: i32,
}
/// wl_output mode
#[derive(Clone, PartialEq)]
struct Mode { struct Mode {
width: i32, width: i32,
height: i32, height: i32,
} }
#[derive(Clone, PartialEq)] #[derive(Clone)]
pub struct OutputState { pub struct OutputState {
current_mode: Option<Mode>, current_mode: Option<Mode>,
phys_size: Option<SizeMM>,
transform: Option<c::Transform>, transform: Option<c::Transform>,
pub scale: i32, scale: i32,
} }
impl OutputState { impl OutputState {
// More properly, this would have been a builder kind of struct,
// with wl_output gradually adding properties to it
// before it reached a fully initialized state,
// when it would transform into a struct without all (some?) of the Options.
// However, it's not clear which state is fully initialized,
// and whether it would make things easier at all anyway.
fn uninitialized() -> OutputState { fn uninitialized() -> OutputState {
OutputState { OutputState {
current_mode: None, current_mode: None,
phys_size: None,
transform: None, transform: None,
scale: 1, scale: 1,
} }
} }
pub fn get_pixel_size(&self) -> Option<Size> {
use self::c::Transform;
match self {
OutputState {
current_mode: Some(Mode { width, height } ),
transform: Some(transform),
phys_size: _,
scale: _,
} => Some(
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => Size {
width: *width as u32,
height: *height as u32,
},
_ => Size {
width: *height as u32,
height: *width as u32,
},
}
),
_ => None,
}
}
/// Returns transformed dimensions
pub fn get_phys_size(&self) -> Option<Size> {
use self::c::Transform;
match self {
OutputState {
current_mode: _,
transform: Some(transform),
phys_size: Some(SizeMM { width, height }),
scale: _,
} => Some(
match transform {
Transform::Normal
| Transform::Rotated180
| Transform::Flipped
| Transform::FlippedRotated180 => Size {
width: *width as u32,
height: *height as u32,
},
_ => Size {
width: *height as u32,
height: *width as u32,
},
}
),
_ => None,
}
}
}
/// A comparable ID of an output
#[derive(Clone, PartialEq, Hash)]
pub struct OutputId {
// WlOutput is a unique pointer, so it will not repeat
// even if there are multiple output managers.
wl_output: c::WlOutput,
} }
pub struct Output { pub struct Output {
@ -441,38 +334,6 @@ pub struct Output {
current: OutputState, current: OutputState,
} }
/// The manager of all outputs.
// This is the target of several callbacks,
// so it should only be used with a stable place in memory, like `Rc<RefCell>`.
// It should not be instantiated externally or copied,
// or it will not receive those callbacks and be somewhat of an empty shell.
// It should be safe to use as long as the fields are not `pub`,
// and there's no `Clone`, and this module's API only ever gives out
// references wrapped in `Rc<RefCell>`.
// For perfectness, it would only ever give out immutable opaque references,
// but that's not practical at the moment.
// `mem::swap` could replace the value inside,
// but as long as the swap is atomic,
// that should not cause an inconsistent state.
pub struct Outputs { pub struct Outputs {
outputs: Vec<Output>, outputs: Vec<Output>,
// The RefCell is here to let the function be called
// while holding only a read-reference to `Outputs`.
// Otherwise anything trying to get useful data from OutputHandle
// will fail to acquire reference to Outputs.
// TODO: Maybe pass only current state along with Outputs and Output hash.
// The only reason a full OutputHandle is here
// is to be able to track the right Output.
update_cb: Option<RefCell<Box<dyn FnMut(c::OutputHandle)>>>,
}
impl Outputs {
/// The function will get called whenever
/// any output changes or is removed or created.
/// If output handle doesn't return state, the output just went down.
/// It cannot modify anything in Outputs.
// FIXME: handle output destruction
pub fn set_update_cb(&mut self, callback: Box<dyn FnMut(c::OutputHandle)>) {
self.update_cb = Some(RefCell::new(callback));
}
} }

View File

@ -29,7 +29,6 @@ mod variants {
use glib::ToVariant; use glib::ToVariant;
use glib::translate::FromGlibPtrFull; use glib::translate::FromGlibPtrFull;
use glib::translate::FromGlibPtrNone;
use glib::translate::ToGlibPtr; use glib::translate::ToGlibPtr;
/// Unpacks tuple & array variants /// Unpacks tuple & array variants
@ -92,7 +91,7 @@ mod variants {
unsafe { unsafe {
let ret = glib_sys::g_variant_builder_end(builder); let ret = glib_sys::g_variant_builder_end(builder);
glib_sys::g_variant_builder_unref(builder); glib_sys::g_variant_builder_unref(builder);
glib::Variant::from_glib_none(ret) glib::Variant::from_glib_full(ret)
} }
} }
} }
@ -132,40 +131,19 @@ fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
) )
} }
fn get_settings(schema_name: &str) -> Option<gio::Settings> {
let mut error_handler = logging::Print{};
gio::SettingsSchemaSource::get_default()
.or_warn(
&mut error_handler,
logging::Problem::Surprise,
"No gsettings schemas installed.",
)
.and_then(|sss|
sss.lookup(schema_name, true)
.or_warn(
&mut error_handler,
logging::Problem::Surprise,
&format!("Gsettings schema {} not installed", schema_name),
)
)
.map(|_sschema| gio::Settings::new(schema_name))
}
fn set_layout(kind: String, name: String) { fn set_layout(kind: String, name: String) {
let settings = get_settings("org.gnome.desktop.input-sources"); let settings = gio::Settings::new("org.gnome.desktop.input-sources");
if let Some(settings) = settings { let inputs = settings.get_value("sources").unwrap();
let inputs = settings.get_value("sources").unwrap(); let current = (kind.clone(), name.clone());
let current = (kind.clone(), name.clone()); let inputs = variants::get_tuples(inputs).into_iter()
let inputs = variants::get_tuples(inputs).into_iter() .filter(|t| t != &current);
.filter(|t| t != &current); let inputs = vec![(kind, name)].into_iter()
let inputs = vec![(kind, name)].into_iter() .chain(inputs).collect();
.chain(inputs).collect(); settings.set_value(
settings.set_value( "sources",
"sources", &variants::ArrayPairString(inputs).to_variant()
&variants::ArrayPairString(inputs).to_variant(), );
); settings.apply();
settings.apply();
}
} }
/// A reference to what the user wants to see /// A reference to what the user wants to see
@ -305,13 +283,9 @@ pub fn show(
let overlay_layouts = resources::get_overlays().into_iter() let overlay_layouts = resources::get_overlays().into_iter()
.map(|name| LayoutId::Local(name.to_string())); .map(|name| LayoutId::Local(name.to_string()));
let settings = get_settings("org.gnome.desktop.input-sources"); let settings = gio::Settings::new("org.gnome.desktop.input-sources");
let inputs = settings let inputs = settings.get_value("sources").unwrap();
.map(|settings| { let inputs = variants::get_tuples(inputs);
let inputs = settings.get_value("sources").unwrap();
variants::get_tuples(inputs)
})
.unwrap_or_else(|| Vec::new());
let system_layouts: Vec<LayoutId> = inputs.into_iter() let system_layouts: Vec<LayoutId> = inputs.into_iter()
.map(|(kind, name)| LayoutId::System { kind, name }) .map(|(kind, name)| LayoutId::System { kind, name })

View File

@ -16,16 +16,14 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
("us_wide", include_str!("../data/keyboards/us_wide.yaml")), ("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
("de", include_str!("../data/keyboards/de.yaml")), ("de", include_str!("../data/keyboards/de.yaml")),
("de_wide", include_str!("../data/keyboards/de_wide.yaml")), ("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
("el", include_str!("../data/keyboards/el.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")),
("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")),
("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")), ("jp+kana_wide", include_str!("../data/keyboards/jp+kana_wide.yaml")),
("no", include_str!("../data/keyboards/no.yaml")), ("no", include_str!("../data/keyboards/no.yaml")),
("number", include_str!("../data/keyboards/number.yaml")), ("number", include_str!("../data/keyboards/number.yaml")),
("pl", include_str!("../data/keyboards/pl.yaml")),
("pl_wide", include_str!("../data/keyboards/pl_wide.yaml")),
("se", include_str!("../data/keyboards/se.yaml")), ("se", include_str!("../data/keyboards/se.yaml")),
// layout+overlay // layout+overlay
("terminal", include_str!("../data/keyboards/terminal.yaml")), ("terminal", include_str!("../data/keyboards/terminal.yaml")),

View File

@ -30,6 +30,8 @@
enum { enum {
PROP_0, PROP_0,
PROP_SIZE_CONSTRAINT_LANDSCAPE,
PROP_SIZE_CONSTRAINT_PORTRAIT,
PROP_VISIBLE, PROP_VISIBLE,
PROP_LAST PROP_LAST
}; };
@ -39,17 +41,19 @@ typedef struct _ServerContextServiceClass ServerContextServiceClass;
struct _ServerContextService { struct _ServerContextService {
GObject parent; GObject parent;
LayoutHolder *state; // unowned EekboardContextService *state; // unowned
/// Needed for instantiating the widget /// Needed for instantiating the widget
struct submission *submission; // unowned struct submission *submission; // unowned
struct squeek_layout_state *layout;
struct ui_manager *manager; // unowned
gboolean visible; gboolean visible;
PhoshLayerSurface *window; PhoshLayerSurface *window;
GtkWidget *widget; // nullable GtkWidget *widget; // nullable
guint hiding; guint hiding;
guint last_requested_height; guint last_requested_height;
enum squeek_arrangement_kind last_type;
gdouble size_constraint_landscape[2];
gdouble size_constraint_portrait[2];
}; };
struct _ServerContextServiceClass { struct _ServerContextServiceClass {
@ -71,6 +75,27 @@ on_destroy (GtkWidget *widget, gpointer user_data)
//eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context)); //eekboard_context_service_destroy (EEKBOARD_CONTEXT_SERVICE (context));
} }
static void
make_widget (ServerContextService *context);
static void
on_notify_keyboard (GObject *object,
GParamSpec *spec,
ServerContextService *context)
{
/* Recreate the keyboard widget to keep in sync with the keymap. */
if (context->window)
make_widget(context);
gboolean visible;
g_object_get (context, "visible", &visible, NULL);
if (visible) {
server_context_service_hide_keyboard(context);
server_context_service_show_keyboard(context);
}
}
static void static void
on_notify_map (GObject *object, on_notify_map (GObject *object,
ServerContextService *context) ServerContextService *context)
@ -87,6 +112,26 @@ on_notify_unmap (GObject *object,
g_object_set (context, "visible", FALSE, NULL); g_object_set (context, "visible", FALSE, NULL);
} }
static uint32_t
calculate_height(int32_t width)
{
uint32_t height = 180;
if (width < 360 && width > 0) {
height = ((unsigned)width * 7 / 12); // to match 360×210
} else if (width < 540) {
height = 180 + (540 - (unsigned)width) * 30 / 180; // smooth transition
}
return height;
}
enum squeek_arrangement_kind get_type(uint32_t width, uint32_t height) {
(void)height;
if (width < 540) {
return ARRANGEMENT_KIND_BASE;
}
return ARRANGEMENT_KIND_WIDE;
}
static void static void
on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context) on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context)
{ {
@ -96,8 +141,14 @@ on_surface_configure(PhoshLayerSurface *surface, ServerContextService *context)
"configured-width", &width, "configured-width", &width,
"configured-height", &height, "configured-height", &height,
NULL); NULL);
// check if the change would switch types
enum squeek_arrangement_kind new_type = get_type((uint32_t)width, (uint32_t)height);
if (context->last_type != new_type) {
context->last_type = new_type;
eekboard_context_service_update_layout(context->state, context->last_type);
}
guint desired_height = squeek_uiman_get_perceptual_height(context->manager); 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
// (for the same set of surrounding properties), // (for the same set of surrounding properties),
@ -120,14 +171,14 @@ make_window (ServerContextService *context)
if (context->window) if (context->window)
g_error("Window already present"); g_error("Window already present");
struct squeek_output_handle output = squeek_outputs_get_current(squeek_wayland->outputs); struct wl_output *output = squeek_outputs_get_current(squeek_wayland->outputs);
squeek_uiman_set_output(context->manager, output); int32_t width = squeek_outputs_get_perceptual_width(squeek_wayland->outputs, output);
uint32_t height = squeek_uiman_get_perceptual_height(context->manager); uint32_t height = calculate_height(width);
context->window = g_object_new ( context->window = g_object_new (
PHOSH_TYPE_LAYER_SURFACE, PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell, "layer-shell", squeek_wayland->layer_shell,
"wl-output", output.output, "wl-output", output,
"height", height, "height", height,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
@ -139,7 +190,6 @@ make_window (ServerContextService *context)
NULL NULL
); );
squeek_uiman_set_surface(context->manager, context->window);
g_object_connect (context->window, g_object_connect (context->window,
"signal::destroy", G_CALLBACK(on_destroy), context, "signal::destroy", G_CALLBACK(on_destroy), context,
"signal::map", G_CALLBACK(on_notify_map), context, "signal::map", G_CALLBACK(on_notify_map), context,
@ -174,11 +224,14 @@ make_widget (ServerContextService *context)
gtk_widget_destroy(context->widget); gtk_widget_destroy(context->widget);
context->widget = NULL; context->widget = NULL;
} }
context->widget = eek_gtk_keyboard_new (context->state, context->submission, context->layout);
LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (context->state);
context->widget = eek_gtk_keyboard_new (keyboard, context->state, context->submission);
gtk_widget_set_has_tooltip (context->widget, TRUE); gtk_widget_set_has_tooltip (context->widget, TRUE);
gtk_container_add (GTK_CONTAINER(context->window), context->widget); gtk_container_add (GTK_CONTAINER(context->window), context->widget);
gtk_widget_show_all(context->widget); gtk_widget_show (context->widget);
} }
static gboolean static gboolean
@ -243,8 +296,21 @@ server_context_service_set_property (GObject *object,
GParamSpec *pspec) GParamSpec *pspec)
{ {
ServerContextService *context = SERVER_CONTEXT_SERVICE(object); ServerContextService *context = SERVER_CONTEXT_SERVICE(object);
GVariant *variant;
switch (prop_id) { switch (prop_id) {
case PROP_SIZE_CONSTRAINT_LANDSCAPE:
variant = g_value_get_variant (value);
g_variant_get (variant, "(dd)",
&context->size_constraint_landscape[0],
&context->size_constraint_landscape[1]);
break;
case PROP_SIZE_CONSTRAINT_PORTRAIT:
variant = g_value_get_variant (value);
g_variant_get (variant, "(dd)",
&context->size_constraint_portrait[0],
&context->size_constraint_portrait[1]);
break;
case PROP_VISIBLE: case PROP_VISIBLE:
context->visible = g_value_get_boolean (value); context->visible = g_value_get_boolean (value);
break; break;
@ -293,6 +359,26 @@ server_context_service_class_init (ServerContextServiceClass *klass)
gobject_class->get_property = server_context_service_get_property; gobject_class->get_property = server_context_service_get_property;
gobject_class->dispose = server_context_service_dispose; gobject_class->dispose = server_context_service_dispose;
pspec = g_param_spec_variant ("size-constraint-landscape",
"Size constraint landscape",
"Size constraint landscape",
G_VARIANT_TYPE("(dd)"),
NULL,
G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_SIZE_CONSTRAINT_LANDSCAPE,
pspec);
pspec = g_param_spec_variant ("size-constraint-portrait",
"Size constraint portrait",
"Size constraint portrait",
G_VARIANT_TYPE("(dd)"),
NULL,
G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_SIZE_CONSTRAINT_PORTRAIT,
pspec);
/** /**
* Flag to indicate if keyboard is visible or not. * Flag to indicate if keyboard is visible or not.
*/ */
@ -312,12 +398,19 @@ server_context_service_init (ServerContextService *state) {
} }
ServerContextService * ServerContextService *
server_context_service_new (LayoutHolder *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman) server_context_service_new (EekboardContextService *state, struct submission *submission)
{ {
ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL); ServerContextService *ui = g_object_new (SERVER_TYPE_CONTEXT_SERVICE, NULL);
ui->submission = submission; ui->submission = submission;
ui->state = state; ui->state = state;
ui->layout = layout; g_signal_connect (state,
ui->manager = uiman; "notify::keyboard",
G_CALLBACK(on_notify_keyboard),
ui);
return ui; return ui;
} }
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *service)
{
return service->last_type;
}

View File

@ -20,7 +20,6 @@
#include "src/layout.h" #include "src/layout.h"
#include "src/submission.h" #include "src/submission.h"
#include "ui_manager.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -37,7 +36,7 @@ typedef struct _ServerContextService ServerContextService;
GType server_context_service_get_type GType server_context_service_get_type
(void) G_GNUC_CONST; (void) G_GNUC_CONST;
ServerContextService *server_context_service_new(LayoutHolder *state, struct submission *submission, struct squeek_layout_state *layout, struct ui_manager *uiman); ServerContextService *server_context_service_new(EekboardContextService *state, struct submission *submission);
enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *); enum squeek_arrangement_kind server_context_service_get_layout_type(ServerContextService *);
void server_context_service_show_keyboard (ServerContextService *context); void server_context_service_show_keyboard (ServerContextService *context);
void server_context_service_hide_keyboard (ServerContextService *context); void server_context_service_hide_keyboard (ServerContextService *context);

View File

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

View File

@ -10,10 +10,10 @@ struct submission;
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,
struct wl_seat *seat, struct wl_seat *seat,
LayoutHolder *state); EekboardContextService *state);
// Defined in Rust // Defined in Rust
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, LayoutHolder *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); void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
#endif #endif

View File

@ -17,18 +17,9 @@
* and those events SHOULD NOT cause any lost events. * and those events SHOULD NOT cause any lost events.
* */ * */
use std::collections::HashSet;
use std::ffi::CString;
use ::action::Modifier;
use ::imservice;
use ::imservice::IMService; use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
use ::util::vec_remove;
use ::vkeyboard::VirtualKeyboard; use ::vkeyboard::VirtualKeyboard;
// traits
use std::iter::FromIterator;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
pub mod c { pub mod c {
use super::*; use super::*;
@ -65,9 +56,7 @@ pub mod c {
Box::<Submission>::into_raw(Box::new( Box::<Submission>::into_raw(Box::new(
Submission { Submission {
imservice, imservice,
modifiers_active: Vec::new(),
virtual_keyboard: VirtualKeyboard(vk), virtual_keyboard: VirtualKeyboard(vk),
pressed: Vec::new(),
} }
)) ))
} }
@ -103,144 +92,9 @@ pub mod c {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Timestamp(pub u32); pub struct Timestamp(pub u32);
enum SubmittedAction {
/// A collection of keycodes that were pressed
VirtualKeyboard(Vec<KeyCode>),
IMService,
}
pub struct Submission { pub struct Submission {
// used by C callbacks internally, TODO: make use with virtual keyboard
#[allow(dead_code)]
imservice: Option<Box<IMService>>, imservice: Option<Box<IMService>>,
virtual_keyboard: VirtualKeyboard, pub virtual_keyboard: VirtualKeyboard,
modifiers_active: Vec<(KeyStateId, Modifier)>,
pressed: Vec<(KeyStateId, SubmittedAction)>,
}
pub enum SubmitData<'a> {
Text(&'a CString),
Erase,
Keycodes,
}
impl Submission {
/// Sends a submit text event if possible;
/// otherwise sends key press and makes a note of it
pub fn handle_press(
&mut self,
key_id: KeyStateId,
data: SubmitData,
keycodes: &Vec<KeyCode>,
time: Timestamp,
) {
let mods_are_on = !self.modifiers_active.is_empty();
let was_committed_as_text = match (&mut self.imservice, mods_are_on) {
(Some(imservice), false) => {
enum Outcome {
Submitted(Result<(), imservice::SubmitError>),
NotSubmitted,
};
let submit_outcome = match data {
SubmitData::Text(text) => {
Outcome::Submitted(imservice.commit_string(text))
},
SubmitData::Erase => {
/* Delete_surrounding_text takes byte offsets,
* so cannot work without get_surrounding_text.
* This is a bug in the protocol.
*/
// imservice.delete_surrounding_text(1, 0),
Outcome::NotSubmitted
},
SubmitData::Keycodes => Outcome::NotSubmitted,
};
match submit_outcome {
Outcome::Submitted(result) => {
match result.and_then(|()| imservice.commit()) {
Ok(()) => true,
Err(imservice::SubmitError::NotActive) => false,
}
},
Outcome::NotSubmitted => false,
}
},
(_, _) => false,
};
let submit_action = match was_committed_as_text {
true => SubmittedAction::IMService,
false => {
self.virtual_keyboard.switch(
keycodes,
PressType::Pressed,
time,
);
SubmittedAction::VirtualKeyboard(keycodes.clone())
},
};
self.pressed.push((key_id, submit_action));
}
pub fn handle_release(&mut self, key_id: KeyStateId, time: Timestamp) {
let index = self.pressed.iter().position(|(id, _)| *id == key_id);
if let Some(index) = index {
let (_id, action) = self.pressed.remove(index);
match action {
// string already sent, nothing to do
SubmittedAction::IMService => {},
// no matter if the imservice got activated,
// keys must be released
SubmittedAction::VirtualKeyboard(keycodes) => {
self.virtual_keyboard.switch(
&keycodes,
PressType::Released,
time,
)
},
}
};
}
pub fn handle_add_modifier(
&mut self,
key_id: KeyStateId,
modifier: Modifier, _time: Timestamp,
) {
self.modifiers_active.push((key_id, modifier));
self.update_modifiers();
}
pub fn handle_drop_modifier(
&mut self,
key_id: KeyStateId,
_time: Timestamp,
) {
vec_remove(&mut self.modifiers_active, |(id, _)| *id == key_id);
self.update_modifiers();
}
fn update_modifiers(&mut self) {
let raw_modifiers = self.modifiers_active.iter()
.map(|(_id, m)| match m {
Modifier::Control => Modifiers::CONTROL,
Modifier::Alt => Modifiers::MOD1,
})
.fold(Modifiers::empty(), |m, n| m | n);
self.virtual_keyboard.set_modifiers_state(raw_modifiers);
}
pub fn is_modifier_active(&self, modifier: Modifier) -> bool {
self.modifiers_active.iter()
.position(|(_id, m)| *m == modifier)
.is_some()
}
pub fn get_active_modifiers(&self) -> HashSet<Modifier> {
HashSet::from_iter(
self.modifiers_active.iter().map(|(_id, m)| m.clone())
)
}
} }

View File

@ -60,7 +60,7 @@ fn check_layout(layout: Layout) {
let state = xkb::State::new(&keymap); let state = xkb::State::new(&keymap);
// "Press" each button with keysyms // "Press" each button with keysyms
for (_pos, view) in layout.views.values() { for view in layout.views.values() {
for (_y, row) in &view.get_rows() { for (_y, row) in &view.get_rows() {
for (_x, button) in &row.buttons { for (_x, button) in &row.buttons {
let keystate = button.state.borrow(); let keystate = button.state.borrow();

View File

@ -1,8 +0,0 @@
#include "eek/layersurface.h"
void squeek_manager_set_surface_height(PhoshLayerSurface *surface, uint32_t desired_height) {
phosh_layer_surface_set_size(surface, 0,
(gint)desired_height);
phosh_layer_surface_set_exclusive_zone(surface, (gint)desired_height);
phosh_layer_surface_wl_surface_commit (surface);
}

View File

@ -1,15 +0,0 @@
#ifndef UI_MANAGER__
#define UI_MANAGER__
#include <inttypes.h>
#include "eek/layersurface.h"
#include "outputs.h"
struct ui_manager;
struct ui_manager *squeek_uiman_new(struct squeek_outputs *outputs);
void squeek_uiman_set_output(struct ui_manager *uiman, struct squeek_output_handle output);
void squeek_uiman_set_surface(struct ui_manager *uiman, PhoshLayerSurface *surface);
uint32_t squeek_uiman_get_perceptual_height(struct ui_manager *uiman);
#endif

View File

@ -1,241 +0,0 @@
/* Copyright (C) 2020 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
*/
/*! Centrally manages the shape of the UI widgets, and the choice of layout.
*
* Coordinates this based on information collated from all possible sources.
*/
use std::cell::RefCell;
use std::cmp::min;
use std::rc::Rc;
use ::logging;
use ::outputs::{ OutputId, Outputs, OutputState};
use ::outputs::c::OutputHandle;
// Traits
use ::logging::Warn;
mod c {
use super::*;
use std::os::raw::c_void;
use ::outputs::c::COutputs;
use ::util::c::Wrapped;
#[derive(Clone, Copy)]
#[repr(C)]
pub struct PhoshLayerSurface(*const c_void);
extern "C" {
// Rustc wrongly assumes
// that COutputs allows C direct access to the underlying RefCell.
#[allow(improper_ctypes)]
pub fn squeek_manager_set_surface_height(
surface: PhoshLayerSurface,
height: u32,
);
}
#[no_mangle]
pub extern "C"
fn squeek_uiman_new(outputs: COutputs) -> Wrapped<Manager> {
let uiman_raw = Wrapped::new(Manager::new());
if !outputs.is_null() {
let uiman = uiman_raw.clone_ref();
let outputs = outputs.clone_ref();
let mut outputs = outputs.borrow_mut();
register_output_man(uiman, &mut outputs);
}
uiman_raw
}
/// Used to size the layer surface containing all the OSK widgets.
#[no_mangle]
pub extern "C"
fn squeek_uiman_get_perceptual_height(
uiman: Wrapped<Manager>,
) -> u32 {
let uiman = uiman.clone_ref();
let uiman = uiman.borrow();
// TODO: what to do when there's no output?
uiman.state.get_perceptual_height().unwrap_or(0)
}
#[no_mangle]
pub extern "C"
fn squeek_uiman_set_output(
uiman: Wrapped<Manager>,
output: OutputHandle,
) {
let uiman = uiman.clone_ref();
let mut uiman = uiman.borrow_mut();
uiman.set_output(output)
}
#[no_mangle]
pub extern "C"
fn squeek_uiman_set_surface(
uiman: Wrapped<Manager>,
surface: PhoshLayerSurface,
) {
let uiman = uiman.clone_ref();
let mut uiman = uiman.borrow_mut();
// Surface is not state, so doesn't need to propagate updates.
uiman.surface = Some(surface);
}
}
/// Stores current state of all things influencing what the UI should look like.
#[derive(Clone, PartialEq)]
pub struct ManagerState {
current_output: Option<(OutputId, OutputState)>,
//// Pixel size of the surface. Needs explicit updating.
//surface_size: Option<Size>,
}
impl ManagerState {
/// The largest ideal heigth for the keyboard as a whole
/// judged by the ease of hitting targets within.
/// Ideally related to finger size, the crammedness of the layout,
/// distance from display, and motor skills of the user.
// FIXME: Start by making this aware of display's dpi,
// then layout number of rows.
fn get_max_target_height(output: &OutputState) -> u32 {
let layout_rows = 4; // FIXME: use number from layout.
let px_size = output.get_pixel_size();
let phys_size = output.get_phys_size();
let finger_height_px = match (px_size, phys_size) {
(Some(px_size), Some(phys_size)) => {
// Fudged to result in 420px from the original design.
// That gives about 9.5mm per finger height.
// Maybe floats are not the best choice here,
// but it gets rounded ASAP. Consider rationals.
let keyboard_fraction_of_display: f64 = 420. / 1440.;
let keyboard_mm = keyboard_fraction_of_display * 130.;
let finger_height_mm = keyboard_mm / 4.;
// TODO: Take into account target shape/area, not just height.
finger_height_mm * px_size.height as f64 / phys_size.height as f64
},
(_, None) => output.scale as f64 * 52.5, // match total 420px at scale 2 from original design
(None, Some(_)) => {
log_print!(
logging::Level::Surprise,
"Output has physical size data but no pixel info",
);
output.scale as f64 * 52.5
},
};
(layout_rows as f64 * finger_height_px) as u32
}
fn get_perceptual_height(&self) -> Option<u32> {
let output_info = (&self.current_output).as_ref()
.map(|(_id, os)| (
os.scale as u32,
os.get_pixel_size(),
ManagerState::get_max_target_height(&os),
));
match output_info {
Some((scale, Some(px_size), target_height)) => Some({
let height = if (px_size.width < 720) & (px_size.width > 0) {
px_size.width * 7 / 12 // to match 360×210
} else if px_size.width < 1080 {
360 + (1080 - px_size.width) * 60 / 360 // smooth transition
} else {
360
};
// Don't exceed half the display size.
let height = min(height, px_size.height / 2);
// Don't waste screen space by exceeding best target height.
let height = min(height, target_height);
height / scale
}),
Some((scale, None, _)) => Some(360 / scale),
None => None,
}
}
}
pub struct Manager {
state: ManagerState,
surface: Option<c::PhoshLayerSurface>,
}
impl Manager {
fn new() -> Manager {
Manager {
state: ManagerState { current_output: None, },
surface: None,
}
}
fn set_output(&mut self, output: OutputHandle) {
let output_state = output.get_state()
.or_warn(
&mut logging::Print,
logging::Problem::Bug,
// This is really bad. It only happens when the layer surface
// is placed, and it happens once.
// The layer surface is on an output that can't be tracked.
"Tried to set output that's not known to exist. Ignoring.",
);
self.state.current_output = output_state.map(
|state| (output.get_id(), state)
);
// TODO: At the time of writing, this function is only used once,
// before the layer surface is initialized.
// Therefore it doesn't update anything. Maybe it should in the future,
// if it sees more use.
}
fn handle_output_change(&mut self, output: OutputHandle) {
let (id, output_state) = match &self.state.current_output {
Some((id, state)) => {
if *id != output.get_id() { return } // Not the current output.
else { (id.clone(), state.clone()) }
},
None => return, // Keyboard isn't on any output.
};
if let Some(new_output_state) = output.get_state() {
if new_output_state != output_state {
let new_state = ManagerState {
current_output: Some((id.clone(), new_output_state)),
..self.state.clone()
};
if let Some(surface) = &self.surface {
let new_height = new_state.get_perceptual_height();
if new_height != self.state.get_perceptual_height() {
// TODO: here hard-size the keyboard and suggestion box too.
match new_height {
Some(new_height) => unsafe {
c::squeek_manager_set_surface_height(
*surface,
new_height,
)
}
None => log_print!(
logging::Level::Bug,
"Can't calculate new size",
),
}
}
}
self.state = new_state;
}
};
}
}
fn register_output_man(
ui_man: Rc<RefCell<Manager>>,
output_man: &mut Outputs,
) {
let ui_man = ui_man.clone();
output_man.set_update_cb(Box::new(move |output: OutputHandle| {
let mut ui_man = ui_man.borrow_mut();
ui_man.handle_output_change(output)
}))
}

View File

@ -94,13 +94,11 @@ pub mod c {
} }
/// Extracts the reference to the data. /// Extracts the reference to the data.
/// It may cause problems if attempted in more than one place /// It may cause problems if attempted in more than one place
// FIXME: check for null
pub unsafe fn unwrap(self) -> Rc<RefCell<T>> { pub unsafe fn unwrap(self) -> Rc<RefCell<T>> {
Rc::from_raw(self.0) Rc::from_raw(self.0)
} }
/// Creates a new Rc reference to the same data. /// Creates a new Rc reference to the same data
/// Use for accessing the underlying data as a reference.
pub fn clone_ref(&self) -> Rc<RefCell<T>> { pub fn clone_ref(&self) -> Rc<RefCell<T>> {
// A bit dangerous: the Rc may be in use elsewhere // A bit dangerous: the Rc may be in use elsewhere
let used_rc = unsafe { Rc::from_raw(self.0) }; let used_rc = unsafe { Rc::from_raw(self.0) };
@ -108,10 +106,6 @@ pub mod c {
Rc::into_raw(used_rc); // prevent dropping the original reference Rc::into_raw(used_rc); // prevent dropping the original reference
rc rc
} }
pub fn is_null(&self) -> bool {
self.0.is_null()
}
} }
impl<T> Clone for Wrapped<T> { impl<T> Clone for Wrapped<T> {
@ -136,7 +130,6 @@ pub mod c {
impl<T> COpaquePtr for Wrapped<T> {} impl<T> COpaquePtr for Wrapped<T> {}
} }
/// Clones the underlying data structure, like ToOwned.
pub trait CloneOwned { pub trait CloneOwned {
type Owned; type Owned;
fn clone_owned(&self) -> Self::Owned; fn clone_owned(&self) -> Self::Owned;
@ -202,12 +195,6 @@ pub trait WarningHandler {
fn handle(&mut self, warning: &str); fn handle(&mut self, warning: &str);
} }
/// Removes the first matcing item
pub fn vec_remove<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, pred: F) -> Option<T> {
let idx = v.iter().position(pred);
idx.map(|idx| v.remove(idx))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

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

View File

@ -20,12 +20,6 @@ void eek_virtual_keyboard_update_keymap(struct zwp_virtual_keyboard_v1 *zwp_virt
keyboard->keymap_fd, keyboard->keymap_len); keyboard->keymap_fd, keyboard->keymap_len);
} }
void
eek_virtual_keyboard_set_modifiers(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t mods_depressed) {
zwp_virtual_keyboard_v1_modifiers(zwp_virtual_keyboard_v1,
mods_depressed, 0, 0, 0);
}
int squeek_output_add_listener(struct wl_output *wl_output, int squeek_output_add_listener(struct wl_output *wl_output,
const struct wl_output_listener *listener, void *data) { const struct wl_output_listener *listener, void *data) {
return wl_output_add_listener(wl_output, listener, data); return wl_output_add_listener(wl_output, listener, data);

View File

@ -50,14 +50,13 @@ endforeach
foreach layout : [ foreach layout : [
'us', 'us_wide', 'us', 'us_wide',
'de', 'de_wide', 'de', 'de_wide',
'el',
'es', 'es',
'fi', 'fi',
'gr',
'it', 'it',
'jp+kana','jp+kana_wide', 'jp+kana','jp+kana_wide',
'no', 'no',
'number', 'number',
'pl', 'pl_wide',
'se', 'se',
'terminal', 'terminal',