Compare commits
63 Commits
v1.3.0
...
squeekboar
| Author | SHA1 | Date | |
|---|---|---|---|
| 3af10285b7 | |||
| 53997abc46 | |||
| 34765be22e | |||
| 8b4c643d3e | |||
| 358b25c431 | |||
| 2749fdb686 | |||
| 8e7909e877 | |||
| afaacd3f68 | |||
| 6a164d8119 | |||
| 3c45e3e53c | |||
| cdf263d984 | |||
| 2ddfcfaff0 | |||
| a901c85bcb | |||
| fdbbe8f126 | |||
| f284627beb | |||
| d45724c462 | |||
| 93d0dcdc99 | |||
| b252f7659b | |||
| af6ad1fce6 | |||
| 4ee8a91dfe | |||
| 6d5f793718 | |||
| 59f6173282 | |||
| 3aec821f92 | |||
| 3ac4caa3b9 | |||
| 80ac591535 | |||
| 579ba8ab87 | |||
| 5a262242a3 | |||
| 034570bfa0 | |||
| 1abca0a44e | |||
| 23693521b7 | |||
| bb18e60754 | |||
| 35bc163107 | |||
| 0179507254 | |||
| 0c7e77a05f | |||
| 317d8a58dc | |||
| 89b3ab9a81 | |||
| 8690808a29 | |||
| be4ac32477 | |||
| 5068f36d9d | |||
| 8c48e96f50 | |||
| f15619287a | |||
| eb5c28e77f | |||
| 7fb34feaf8 | |||
| 20a6cf52ac | |||
| c566d8853e | |||
| 0beddc6856 | |||
| 0adde1004f | |||
| 9b271a6919 | |||
| 1db561d33a | |||
| 3170a0b615 | |||
| 9571adb107 | |||
| f834f174d8 | |||
| 3c0b142c4f | |||
| 4f28e3413a | |||
| 291be9fa79 | |||
| fd5d060eee | |||
| cffe07521e | |||
| 36af546362 | |||
| 0da848a5a0 | |||
| 8c9ae98bda | |||
| ef025509ba | |||
| c096698ec1 | |||
| 3aaaed3bae |
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.0.*"
|
||||
clap = "2.32.*"
|
||||
maplit = "1.0.*"
|
||||
regex = "1.1.*"
|
||||
serde = { version = "1.0.*", features = ["derive"] }
|
||||
|
||||
12
HACKING.md
12
HACKING.md
@ -30,7 +30,7 @@ Most common testing is done in CI. Occasionally, and for each release, do perfor
|
||||
- the application draws correctly
|
||||
- it shows when relevant
|
||||
- it changes layouts
|
||||
- it changes levels
|
||||
- it changes views
|
||||
|
||||
Testing with an application:
|
||||
|
||||
@ -50,10 +50,8 @@ Testing layouts:
|
||||
Layouts can be selected using the GNOME Settings application.
|
||||
|
||||
```
|
||||
# define all available layouts
|
||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
|
||||
# choose the active layout
|
||||
$ gsettings set org.gnome.desktop.input-sources current 1
|
||||
# define all available layouts. First one is currently selected.
|
||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]"
|
||||
```
|
||||
|
||||
Coding
|
||||
@ -114,7 +112,7 @@ Use the `cargo.sh` script for maintaining the Cargo part of the build. The scrip
|
||||
|
||||
```
|
||||
cd build_dir
|
||||
sh /source_path/cargo.sh '' test
|
||||
sh /source_path/cargo.sh test
|
||||
```
|
||||
|
||||
### Cargo dependencies
|
||||
@ -125,6 +123,6 @@ Dependencies must be specified in `Cargo.toml` with 2 numbers: "major.minor". Si
|
||||
|
||||
```
|
||||
cd build_dir
|
||||
sh /source_path/cargo.sh '' update
|
||||
sh /source_path/cargo.sh update
|
||||
ninja test
|
||||
```
|
||||
|
||||
7
cargo.sh
7
cargo.sh
@ -11,14 +11,7 @@ SOURCE_DIR="$(dirname "$SCRIPT_PATH")"
|
||||
|
||||
CARGO_TARGET_DIR="$(pwd)"
|
||||
export CARGO_TARGET_DIR
|
||||
if [ -n "${1}" ]; then
|
||||
OUT_PATH="$(realpath "$1")"
|
||||
fi
|
||||
|
||||
cd "$SOURCE_DIR"
|
||||
shift
|
||||
cargo "$@"
|
||||
|
||||
if [ -n "${OUT_PATH}" ]; then
|
||||
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
|
||||
fi
|
||||
|
||||
34
cargo_build.sh
Executable file
34
cargo_build.sh
Executable file
@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script manages Cargo builds
|
||||
# while keeping the artifact directory within the build tree
|
||||
# instead of the source tree
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_PATH="$(realpath "$0")"
|
||||
SOURCE_DIR="$(dirname "$SCRIPT_PATH")"
|
||||
|
||||
RELEASE=""
|
||||
BINARY_DIR="debug"
|
||||
if [ "${1}" = "--release" ]; then
|
||||
shift
|
||||
BINARY_DIR="release"
|
||||
RELEASE="--release"
|
||||
fi
|
||||
|
||||
if [ "${1}" = "--rename" ]; then
|
||||
shift
|
||||
FILENAME="${1}"
|
||||
shift
|
||||
fi
|
||||
OUT_PATH="$(realpath "${1}")"
|
||||
shift
|
||||
OUT_BASENAME="$(basename "${OUT_PATH}")"
|
||||
FILENAME="${FILENAME:-"${OUT_BASENAME}"}"
|
||||
|
||||
sh "$SOURCE_DIR"/cargo.sh build $RELEASE "$@"
|
||||
|
||||
if [ -n "${OUT_PATH}" ]; then
|
||||
cp -a ./"${BINARY_DIR}"/"${FILENAME}" "${OUT_PATH}"
|
||||
fi
|
||||
@ -1,7 +1,6 @@
|
||||
# German layout by Mark Müller
|
||||
# Version 2019101900
|
||||
# Maintained by: Mark Müller <markmueller86@gmail.com>
|
||||
---
|
||||
bounds: { x: 0, y: 1, width: 360, height: 210 }
|
||||
bounds: { x: 0, y: 1, width: 360, height: 208 }
|
||||
|
||||
outlines:
|
||||
default:
|
||||
@ -20,27 +19,27 @@ views:
|
||||
- "q w e r t z u i o p"
|
||||
- "a s d f g h j k l"
|
||||
- "Shift_L y x c v b n m BackSpace"
|
||||
- "show_numbers show_dechars preferences space , period Return"
|
||||
- "show_numbers show_eschars preferences space , . Return"
|
||||
upper:
|
||||
- "Q W E R T Z U I O P"
|
||||
- "A S D F G H J K L"
|
||||
- "Shift_L Y X C V B N M BackSpace"
|
||||
- "show_numbers show_dechars preferences space ! ? Return"
|
||||
- "show_numbers show_eschars preferences space ! ? Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # € % & - _ + ( )"
|
||||
- "show_symbols , \" ' colon = < > BackSpace"
|
||||
- "show_letters show_dechars preferences space , period Return"
|
||||
- "show_symbols , \" ' : = < > BackSpace"
|
||||
- "show_letters show_eschars preferences space , . Return"
|
||||
symbols:
|
||||
- "~ ` ´ | · √ µ ÷ × ¶"
|
||||
- "© ® £ $ ¥ ^ ° * { }"
|
||||
- "show_numbers \\ / § π τ [ ] BackSpace"
|
||||
- "show_letters show_dechars preferences space , period Return"
|
||||
dechars:
|
||||
- "show_letters show_eschars preferences space , . Return"
|
||||
eschars:
|
||||
- "ä è é ö ü Ä È É Ö Ü"
|
||||
- "à â ê î ô À Â È Î Ô"
|
||||
- "show_numbers « » ç Ç æ œ ß BackSpace"
|
||||
- "show_letters show_dechars preferences space „ “ Return"
|
||||
- "show_letters show_eschars preferences space „ “ Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
@ -53,6 +52,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
@ -72,24 +72,18 @@ buttons:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
show_dechars:
|
||||
show_eschars:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "dechars"
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "äÄ"
|
||||
|
||||
period:
|
||||
outline: "default"
|
||||
label: "."
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "altline"
|
||||
icon: "key-enter"
|
||||
colon:
|
||||
label: ":"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
keysym: "Return"
|
||||
|
||||
88
data/keyboards/de_wide.yaml
Normal file
88
data/keyboards/de_wide.yaml
Normal file
@ -0,0 +1,88 @@
|
||||
# Maintained by: Mark Müller <markmueller86@gmail.com>
|
||||
---
|
||||
bounds: { x: 0, y: 1, width: 540, height: 168 }
|
||||
|
||||
outlines:
|
||||
default:
|
||||
bounds: { x: 0, y: 0, width: 48, height: 42 }
|
||||
altline:
|
||||
bounds: { x: 0, y: 0, width: 81, height: 42 }
|
||||
wide:
|
||||
bounds: { x: 0, y: 0, width: 108, height: 42 }
|
||||
spaceline:
|
||||
bounds: { x: 0, y: 0, width: 216, height: 42 }
|
||||
special:
|
||||
bounds: { x: 0, y: 0, width: 48, height: 42 }
|
||||
|
||||
views:
|
||||
base:
|
||||
- "q w e r t z u i o p ü"
|
||||
- "a s d f g h j k l ö ä"
|
||||
- "Shift_L y x c v b n m BackSpace"
|
||||
- "show_numbers preferences space , . Return"
|
||||
upper:
|
||||
- "Q W E R T Z U I O P Ü"
|
||||
- "A S D F G H J K L Ö Ä"
|
||||
- "Shift_L Y X C V B N M BackSpace"
|
||||
- "show_numbers preferences space ! ? Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # % & - _ + ( ) ß"
|
||||
- "show_symbols , \" ' : = < > BackSpace"
|
||||
- "show_letters preferences space , . Return"
|
||||
symbols:
|
||||
- "~ ` ´ · © ® ÷ × ¶"
|
||||
- "€ £ $ ¥ ^ ° * { } |"
|
||||
- "show_numbers \\ / § π τ [ ] BackSpace"
|
||||
- "show_letters preferences space , . Return"
|
||||
eschars:
|
||||
- "ä è é ö ü Ä È É Ö Ü"
|
||||
- "à â ê î ô À Â È Î Ô"
|
||||
- "show_numbers « » ç Ç æ œ ß BackSpace"
|
||||
- "show_letters preferences space „ “ Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "upper"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
icon: "key-shift"
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
show_numbers:
|
||||
action:
|
||||
set_view: "numbers"
|
||||
outline: "altline"
|
||||
label: "123"
|
||||
show_letters:
|
||||
action:
|
||||
set_view: "base"
|
||||
outline: "altline"
|
||||
label: "abc"
|
||||
show_symbols:
|
||||
action:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
show_eschars:
|
||||
action:
|
||||
locking:
|
||||
lock_view: "eschars"
|
||||
unlock_view: "base"
|
||||
outline: "altline"
|
||||
label: "äÄ"
|
||||
space:
|
||||
outline: "spaceline"
|
||||
text: " "
|
||||
Return:
|
||||
outline: "altline"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
@ -53,6 +53,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "altline"
|
||||
@ -86,111 +87,112 @@ buttons:
|
||||
label: "αι"
|
||||
period:
|
||||
outline: "altline"
|
||||
label: "."
|
||||
text: "."
|
||||
space:
|
||||
outline: spaceline
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
aring:
|
||||
label: "å"
|
||||
text: "å"
|
||||
Aring:
|
||||
label: "Å"
|
||||
text: "Å"
|
||||
oslash:
|
||||
label: "ø"
|
||||
text: "ø"
|
||||
Oslash:
|
||||
label: "Ø"
|
||||
text: "Ø"
|
||||
ae:
|
||||
label: "æ"
|
||||
text: "æ"
|
||||
AE:
|
||||
label: "Æ"
|
||||
text: "Æ"
|
||||
asterisk:
|
||||
label: "*"
|
||||
text: "*"
|
||||
asciitilde:
|
||||
label: "~"
|
||||
text: "~"
|
||||
quoteleft:
|
||||
label: "`"
|
||||
text: "`"
|
||||
bar:
|
||||
label: "|"
|
||||
text: "|"
|
||||
U00B7:
|
||||
label: "·"
|
||||
text: "·"
|
||||
squareroot:
|
||||
label: "√"
|
||||
text: "√"
|
||||
Greek_pi:
|
||||
label: "π"
|
||||
text: "π"
|
||||
division:
|
||||
label: "÷"
|
||||
text: "÷"
|
||||
multiply:
|
||||
label: "×"
|
||||
text: "×"
|
||||
paragraph:
|
||||
label: "¶"
|
||||
text: "¶"
|
||||
Greek_tau:
|
||||
label: "τ"
|
||||
text: "τ"
|
||||
copyright:
|
||||
label: "©"
|
||||
text: "©"
|
||||
numbersign:
|
||||
label: "#"
|
||||
text: "#"
|
||||
U00AE:
|
||||
label: "®"
|
||||
text: "®"
|
||||
at:
|
||||
label: "@"
|
||||
text: "@"
|
||||
dollar:
|
||||
label: "$"
|
||||
text: "$"
|
||||
U00A3:
|
||||
label: "£"
|
||||
text: "£"
|
||||
percent:
|
||||
label: "%"
|
||||
text: "%"
|
||||
EuroSign:
|
||||
label: "€"
|
||||
text: "€"
|
||||
ampersand:
|
||||
label: "&"
|
||||
text: "&"
|
||||
U00A5:
|
||||
label: "¥"
|
||||
text: "¥"
|
||||
minus:
|
||||
label: "-"
|
||||
text: "-"
|
||||
asciicircum:
|
||||
label: "^"
|
||||
text: "^"
|
||||
underscore:
|
||||
label: "_"
|
||||
text: "_"
|
||||
degree:
|
||||
label: "°"
|
||||
text: "°"
|
||||
plus:
|
||||
label: "+"
|
||||
text: "+"
|
||||
equal:
|
||||
label: "="
|
||||
text: "="
|
||||
parenleft:
|
||||
label: "("
|
||||
text: "("
|
||||
parenright:
|
||||
label: ")"
|
||||
text: ")"
|
||||
braceleft:
|
||||
label: "{"
|
||||
text: "{"
|
||||
braceright:
|
||||
label: "}"
|
||||
text: "}"
|
||||
comma:
|
||||
label: ","
|
||||
text: ","
|
||||
backslash:
|
||||
label: "\\"
|
||||
text: "\\"
|
||||
slash:
|
||||
label: "/"
|
||||
text: "/"
|
||||
quotedbl:
|
||||
label: "\""
|
||||
text: "\""
|
||||
quoteright:
|
||||
label: "'"
|
||||
text: "'"
|
||||
less:
|
||||
label: "<"
|
||||
text: "<"
|
||||
greater:
|
||||
label: ">"
|
||||
text: ">"
|
||||
colon:
|
||||
label: ":"
|
||||
text: ":"
|
||||
semicolon:
|
||||
label: ";"
|
||||
text: ";"
|
||||
exclam:
|
||||
label: "!"
|
||||
text: "!"
|
||||
question:
|
||||
label: "?"
|
||||
text: "?"
|
||||
bracketleft:
|
||||
label: "["
|
||||
text: "["
|
||||
bracketright:
|
||||
label: "]"
|
||||
text: "]"
|
||||
|
||||
|
||||
@ -51,6 +51,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "default"
|
||||
@ -80,14 +81,14 @@ buttons:
|
||||
|
||||
period:
|
||||
outline: "default"
|
||||
label: "."
|
||||
text: "."
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "altline"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
label: ":"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
text: ":"
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "altline"
|
||||
@ -69,108 +70,109 @@ buttons:
|
||||
outline: altline
|
||||
space:
|
||||
outline: spaceline
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
aring:
|
||||
label: "å"
|
||||
text: "å"
|
||||
Aring:
|
||||
label: "Å"
|
||||
text: "Å"
|
||||
ouml:
|
||||
label: "ö"
|
||||
text: "ö"
|
||||
Ouml:
|
||||
label: "Ö"
|
||||
text: "Ö"
|
||||
auml:
|
||||
label: "ä"
|
||||
text: "ä"
|
||||
Auml:
|
||||
label: "Ä"
|
||||
text: "Ä"
|
||||
asterisk:
|
||||
label: "*"
|
||||
text: "*"
|
||||
asciitilde:
|
||||
label: "~"
|
||||
text: "~"
|
||||
quoteleft:
|
||||
label: "`"
|
||||
text: "`"
|
||||
bar:
|
||||
label: "|"
|
||||
text: "|"
|
||||
U00B7:
|
||||
label: "·"
|
||||
text: "·"
|
||||
squareroot:
|
||||
label: "√"
|
||||
text: "√"
|
||||
Greek_pi:
|
||||
label: "π"
|
||||
text: "π"
|
||||
division:
|
||||
label: "÷"
|
||||
text: "÷"
|
||||
multiply:
|
||||
label: "×"
|
||||
text: "×"
|
||||
paragraph:
|
||||
label: "¶"
|
||||
text: "¶"
|
||||
Greek_tau:
|
||||
label: "τ"
|
||||
text: "τ"
|
||||
copyright:
|
||||
label: "©"
|
||||
text: "©"
|
||||
numbersign:
|
||||
label: "#"
|
||||
text: "#"
|
||||
U00AE:
|
||||
label: "®"
|
||||
text: "®"
|
||||
at:
|
||||
label: "@"
|
||||
text: "@"
|
||||
dollar:
|
||||
label: "$"
|
||||
text: "$"
|
||||
U00A3:
|
||||
label: "£"
|
||||
text: "£"
|
||||
percent:
|
||||
label: "%"
|
||||
text: "%"
|
||||
EuroSign:
|
||||
label: "€"
|
||||
text: "€"
|
||||
ampersand:
|
||||
label: "&"
|
||||
text: "&"
|
||||
U00A5:
|
||||
label: "¥"
|
||||
text: "¥"
|
||||
minus:
|
||||
label: "-"
|
||||
text: "-"
|
||||
asciicircum:
|
||||
label: "^"
|
||||
text: "^"
|
||||
underscore:
|
||||
label: "_"
|
||||
text: "_"
|
||||
degree:
|
||||
label: "°"
|
||||
text: "°"
|
||||
plus:
|
||||
label: "+"
|
||||
text: "+"
|
||||
equal:
|
||||
label: "="
|
||||
text: "="
|
||||
parenleft:
|
||||
label: "("
|
||||
text: "("
|
||||
parenright:
|
||||
label: ")"
|
||||
text: ")"
|
||||
braceleft:
|
||||
label: "{"
|
||||
text: "{"
|
||||
braceright:
|
||||
label: "}"
|
||||
text: "}"
|
||||
comma:
|
||||
label: ","
|
||||
text: ","
|
||||
backslash:
|
||||
label: "\\"
|
||||
text: "\\"
|
||||
slash:
|
||||
label: "/"
|
||||
text: "/"
|
||||
quotedbl:
|
||||
label: "\""
|
||||
text: "\""
|
||||
quoteright:
|
||||
label: "'"
|
||||
text: "'"
|
||||
less:
|
||||
label: "<"
|
||||
text: "<"
|
||||
greater:
|
||||
label: ">"
|
||||
text: ">"
|
||||
colon:
|
||||
label: ":"
|
||||
text: ":"
|
||||
semicolon:
|
||||
label: ";"
|
||||
text: ";"
|
||||
exclam:
|
||||
label: "!"
|
||||
text: "!"
|
||||
question:
|
||||
label: "?"
|
||||
text: "?"
|
||||
bracketleft:
|
||||
label: "["
|
||||
text: "["
|
||||
bracketright:
|
||||
label: "]"
|
||||
text: "]"
|
||||
|
||||
|
||||
@ -53,6 +53,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "default"
|
||||
@ -91,6 +92,7 @@ buttons:
|
||||
Return:
|
||||
outline: "altline"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
label: ":"
|
||||
"\"":
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
# Japanese Kana layout by Mark Müller
|
||||
# Version 2019101900
|
||||
# Maintained by: Mark Müller <markmueller86@gmail.com>
|
||||
---
|
||||
bounds: { x: 0, y: 1, width: 360, height: 210 }
|
||||
bounds: { x: 0, y: 1, width: 360, height: 208 }
|
||||
|
||||
outlines:
|
||||
default:
|
||||
@ -199,29 +198,33 @@ views:
|
||||
- "\\ ´ ` · ¶"
|
||||
|
||||
buttons:
|
||||
# following 4 buttons are keysyms from libxkbcommon
|
||||
# following 4 buttons use the corresponding xkb name as keysym
|
||||
BackSpace:
|
||||
outline: "wide"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
Left:
|
||||
outline: "wide"
|
||||
label: "←"
|
||||
keysym: "Left"
|
||||
Right:
|
||||
outline: "wide"
|
||||
label: "→"
|
||||
keysym: "Right"
|
||||
# special button "preferences" is handled in the code
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
icon: "keyboard-mode-symbolic"
|
||||
# space button (unicode)
|
||||
# space button using text tag for ideographic space
|
||||
space:
|
||||
outline: "default-wide"
|
||||
label: "␣"
|
||||
keysym: "U3000"
|
||||
text: " "
|
||||
# switch to number view
|
||||
numbers:
|
||||
action:
|
||||
@ -26,13 +26,13 @@ views:
|
||||
- "show_numbers preferences space . Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "at numbersign dollar percent ampersand minus underscore plus parenleft parenright"
|
||||
- "show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace"
|
||||
- "@ # $ % & - _ + ( )"
|
||||
- "show_symbols , \" ' : ; ! ? BackSpace"
|
||||
- "show_letters preferences space . Return"
|
||||
symbols:
|
||||
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
|
||||
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
|
||||
- "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
|
||||
- "~ ` | U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
|
||||
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree * { }"
|
||||
- "show_numbers \\ / < > = [ ] BackSpace"
|
||||
- "show_letters preferences space . Return"
|
||||
|
||||
buttons:
|
||||
@ -46,6 +46,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "altline"
|
||||
@ -69,108 +70,37 @@ buttons:
|
||||
outline: altline
|
||||
space:
|
||||
outline: spaceline
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
aring:
|
||||
label: "å"
|
||||
Aring:
|
||||
label: "Å"
|
||||
oslash:
|
||||
label: "ø"
|
||||
Oslash:
|
||||
label: "Ø"
|
||||
ae:
|
||||
label: "æ"
|
||||
AE:
|
||||
label: "Æ"
|
||||
asterisk:
|
||||
label: "*"
|
||||
asciitilde:
|
||||
label: "~"
|
||||
quoteleft:
|
||||
label: "`"
|
||||
bar:
|
||||
label: "|"
|
||||
keysym: "Return"
|
||||
U00B7:
|
||||
label: "·"
|
||||
text: "·"
|
||||
squareroot:
|
||||
label: "√"
|
||||
text: "√"
|
||||
Greek_pi:
|
||||
label: "π"
|
||||
text: "π"
|
||||
division:
|
||||
label: "÷"
|
||||
text: "÷"
|
||||
multiply:
|
||||
label: "×"
|
||||
text: "×"
|
||||
paragraph:
|
||||
label: "¶"
|
||||
text: "¶"
|
||||
Greek_tau:
|
||||
label: "τ"
|
||||
text: "τ"
|
||||
copyright:
|
||||
label: "©"
|
||||
numbersign:
|
||||
label: "#"
|
||||
text: "©"
|
||||
U00AE:
|
||||
label: "®"
|
||||
at:
|
||||
label: "@"
|
||||
dollar:
|
||||
label: "$"
|
||||
text: "®"
|
||||
U00A3:
|
||||
label: "£"
|
||||
percent:
|
||||
label: "%"
|
||||
text: "£"
|
||||
EuroSign:
|
||||
label: "€"
|
||||
ampersand:
|
||||
label: "&"
|
||||
text: "€"
|
||||
U00A5:
|
||||
label: "¥"
|
||||
minus:
|
||||
label: "-"
|
||||
text: "¥"
|
||||
asciicircum:
|
||||
label: "^"
|
||||
underscore:
|
||||
label: "_"
|
||||
text: "^"
|
||||
degree:
|
||||
label: "°"
|
||||
plus:
|
||||
label: "+"
|
||||
equal:
|
||||
label: "="
|
||||
parenleft:
|
||||
label: "("
|
||||
parenright:
|
||||
label: ")"
|
||||
braceleft:
|
||||
label: "{"
|
||||
braceright:
|
||||
label: "}"
|
||||
comma:
|
||||
label: ","
|
||||
backslash:
|
||||
label: "\\"
|
||||
slash:
|
||||
label: "/"
|
||||
quotedbl:
|
||||
label: "\""
|
||||
quoteright:
|
||||
label: "'"
|
||||
less:
|
||||
label: "<"
|
||||
greater:
|
||||
label: ">"
|
||||
colon:
|
||||
label: ":"
|
||||
semicolon:
|
||||
label: ";"
|
||||
exclam:
|
||||
label: "!"
|
||||
question:
|
||||
label: "?"
|
||||
bracketleft:
|
||||
label: "["
|
||||
bracketright:
|
||||
label: "]"
|
||||
text: "°"
|
||||
|
||||
|
||||
@ -22,22 +22,24 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
space:
|
||||
outline: spaceline
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: outline7
|
||||
icon: "key-enter"
|
||||
keysym: "BackSpace"
|
||||
asterisk:
|
||||
label: "*"
|
||||
text: "*"
|
||||
numbersign:
|
||||
label: "#"
|
||||
text: "#"
|
||||
minus:
|
||||
label: "-"
|
||||
text: "-"
|
||||
plus:
|
||||
label: "+"
|
||||
text: "+"
|
||||
parenleft:
|
||||
label: "("
|
||||
text: "("
|
||||
parenright:
|
||||
label: ")"
|
||||
text: ")"
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "altline"
|
||||
@ -69,96 +70,97 @@ buttons:
|
||||
outline: altline
|
||||
space:
|
||||
outline: spaceline
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
asterisk:
|
||||
label: "*"
|
||||
text: "*"
|
||||
asciitilde:
|
||||
label: "~"
|
||||
text: "~"
|
||||
quoteleft:
|
||||
label: "`"
|
||||
text: "`"
|
||||
bar:
|
||||
label: "|"
|
||||
text: "|"
|
||||
U00B7:
|
||||
label: "·"
|
||||
text: "·"
|
||||
squareroot:
|
||||
label: "√"
|
||||
text: "√"
|
||||
Greek_pi:
|
||||
label: "π"
|
||||
text: "π"
|
||||
division:
|
||||
label: "÷"
|
||||
text: "÷"
|
||||
multiply:
|
||||
label: "×"
|
||||
text: "×"
|
||||
paragraph:
|
||||
label: "¶"
|
||||
text: "¶"
|
||||
Greek_tau:
|
||||
label: "τ"
|
||||
text: "τ"
|
||||
copyright:
|
||||
label: "©"
|
||||
text: "©"
|
||||
numbersign:
|
||||
label: "#"
|
||||
text: "#"
|
||||
U00AE:
|
||||
label: "®"
|
||||
text: "®"
|
||||
at:
|
||||
label: "@"
|
||||
text: "@"
|
||||
dollar:
|
||||
label: "$"
|
||||
text: "$"
|
||||
U00A3:
|
||||
label: "£"
|
||||
text: "£"
|
||||
percent:
|
||||
label: "%"
|
||||
text: "%"
|
||||
EuroSign:
|
||||
label: "€"
|
||||
text: "€"
|
||||
ampersand:
|
||||
label: "&"
|
||||
text: "&"
|
||||
U00A5:
|
||||
label: "¥"
|
||||
text: "¥"
|
||||
minus:
|
||||
label: "-"
|
||||
text: "-"
|
||||
asciicircum:
|
||||
label: "^"
|
||||
text: "^"
|
||||
underscore:
|
||||
label: "_"
|
||||
text: "_"
|
||||
degree:
|
||||
label: "°"
|
||||
text: "°"
|
||||
plus:
|
||||
label: "+"
|
||||
text: "+"
|
||||
equal:
|
||||
label: "="
|
||||
text: "="
|
||||
parenleft:
|
||||
label: "("
|
||||
text: "("
|
||||
parenright:
|
||||
label: ")"
|
||||
text: ")"
|
||||
braceleft:
|
||||
label: "{"
|
||||
text: "{"
|
||||
braceright:
|
||||
label: "}"
|
||||
text: "}"
|
||||
comma:
|
||||
label: ","
|
||||
text: ","
|
||||
backslash:
|
||||
label: "\\"
|
||||
text: "\\"
|
||||
slash:
|
||||
label: "/"
|
||||
text: "/"
|
||||
quotedbl:
|
||||
label: "\""
|
||||
text: "\""
|
||||
quoteright:
|
||||
label: "'"
|
||||
text: "'"
|
||||
less:
|
||||
label: "<"
|
||||
text: "<"
|
||||
greater:
|
||||
label: ">"
|
||||
text: ">"
|
||||
colon:
|
||||
label: ":"
|
||||
text: ":"
|
||||
semicolon:
|
||||
label: ";"
|
||||
text: ";"
|
||||
exclam:
|
||||
label: "!"
|
||||
text: "!"
|
||||
question:
|
||||
label: "?"
|
||||
text: "?"
|
||||
bracketleft:
|
||||
label: "["
|
||||
text: "["
|
||||
bracketright:
|
||||
label: "]"
|
||||
text: "]"
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
@ -72,14 +73,13 @@ buttons:
|
||||
label: "*/="
|
||||
period:
|
||||
outline: "special"
|
||||
label: "."
|
||||
text: "."
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
label: ":"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
text: ":"
|
||||
|
||||
@ -18,22 +18,22 @@ views:
|
||||
- "q w e r t y u i o p"
|
||||
- "a s d f g h j k l"
|
||||
- "Shift_L z x c v b n m BackSpace"
|
||||
- "show_numbers preferences space period Return"
|
||||
- "show_numbers preferences space . Return"
|
||||
upper:
|
||||
- "Q W E R T Y U I O P"
|
||||
- "A S D F G H J K L"
|
||||
- "Shift_L Z X C V B N M BackSpace"
|
||||
- "show_numbers preferences space period Return"
|
||||
- "show_numbers preferences space . Return"
|
||||
numbers:
|
||||
- "1 2 3 4 5 6 7 8 9 0"
|
||||
- "@ # $ % & - _ + ( )"
|
||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||
- "show_letters preferences space period Return"
|
||||
- "show_letters preferences space . Return"
|
||||
symbols:
|
||||
- "~ ` | · √ π τ ÷ × ¶"
|
||||
- "© ® £ € ¥ ^ ° * { }"
|
||||
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||
- "show_letters preferences space period Return"
|
||||
- "show_letters preferences space . Return"
|
||||
|
||||
buttons:
|
||||
Shift_L:
|
||||
@ -46,6 +46,7 @@ buttons:
|
||||
BackSpace:
|
||||
outline: "altline"
|
||||
icon: "edit-clear-symbolic"
|
||||
keysym: "BackSpace"
|
||||
preferences:
|
||||
action: "show_prefs"
|
||||
outline: "special"
|
||||
@ -70,16 +71,15 @@ buttons:
|
||||
set_view: "symbols"
|
||||
outline: "altline"
|
||||
label: "*/="
|
||||
period:
|
||||
".":
|
||||
outline: "special"
|
||||
label: "."
|
||||
text: "."
|
||||
space:
|
||||
outline: "spaceline"
|
||||
label: " "
|
||||
text: " "
|
||||
Return:
|
||||
outline: "wide"
|
||||
icon: "key-enter"
|
||||
keysym: "Return"
|
||||
colon:
|
||||
label: ":"
|
||||
"\"":
|
||||
keysym: "quotedbl"
|
||||
text: ":"
|
||||
|
||||
8
data/langs/de-DE.txt
Normal file
8
data/langs/de-DE.txt
Normal file
@ -0,0 +1,8 @@
|
||||
us Englisch (US)
|
||||
de Deutsch
|
||||
el Griechisch
|
||||
es Spanisch
|
||||
it Italienisch
|
||||
jp+kana Japanisch (Kana)
|
||||
no Norwegisch
|
||||
|
||||
@ -2,7 +2,9 @@ us English (US)
|
||||
de German
|
||||
el Greek
|
||||
es Spanish
|
||||
fi Finnish
|
||||
it Italian
|
||||
jp+kana Japanese (kana)
|
||||
nb Norwegian
|
||||
no Norwegian
|
||||
se Swedish
|
||||
|
||||
|
||||
7
data/langs/es-ES.txt
Normal file
7
data/langs/es-ES.txt
Normal file
@ -0,0 +1,7 @@
|
||||
us Inglés (EE.UU.)
|
||||
de Alemán
|
||||
el Griego
|
||||
es Español
|
||||
it Italiano
|
||||
jp+kana Japonés (Kana)
|
||||
no Noruego
|
||||
8
data/langs/ja-JP.txt
Normal file
8
data/langs/ja-JP.txt
Normal file
@ -0,0 +1,8 @@
|
||||
us 英語 (US)
|
||||
de ドイツ語
|
||||
el ギリシャ語
|
||||
es スペイン語
|
||||
it イタリア語
|
||||
jp+kana 日本語 (かな)
|
||||
nb ノルウェー語
|
||||
|
||||
@ -2,7 +2,8 @@ us angielski (USA)
|
||||
de niemiecki
|
||||
el grecki
|
||||
es hiszpański
|
||||
fi fiński
|
||||
it włoski
|
||||
jp+kana japoński (kana)
|
||||
nb norweski
|
||||
|
||||
no norweski
|
||||
se szwedzki
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
<gresources>
|
||||
<gresource prefix="/sm/puri/squeekboard">
|
||||
<file compressed="true">style.css</file>
|
||||
<file compressed="true">style-Adwaita:dark.css</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">popup.ui</file>
|
||||
<file>icons/key-enter.svg</file>
|
||||
<file>icons/key-shift.svg</file>
|
||||
|
||||
46
data/style-Adwaita:dark.css
Normal file
46
data/style-Adwaita:dark.css
Normal file
@ -0,0 +1,46 @@
|
||||
sq_view {
|
||||
background-color: rgba(0, 0, 0, 255);
|
||||
color: #ffffff;
|
||||
font-family: cantarell, sans-serif;
|
||||
}
|
||||
|
||||
sq_view sq_button {
|
||||
color: #deddda;
|
||||
background: #464448;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #5e5c64;
|
||||
border-radius: 3px;
|
||||
margin: 4px 2px 4px 2px;
|
||||
}
|
||||
|
||||
sq_view.wide sq_button {
|
||||
margin: 1px 1px 1px 1px;
|
||||
}
|
||||
|
||||
sq_button:active {
|
||||
background: #747077;
|
||||
border-color: #96949d;
|
||||
}
|
||||
|
||||
sq_button.altline,
|
||||
sq_button.special,
|
||||
sq_button.wide {
|
||||
background: #2b292f;
|
||||
border-color: #3e3a44;
|
||||
}
|
||||
|
||||
sq_button.locked {
|
||||
background: #ffffff;
|
||||
color: #2b292f;
|
||||
}
|
||||
|
||||
#Return {
|
||||
background: #1c71d8;
|
||||
border-color: #1a5fb4;
|
||||
}
|
||||
|
||||
#Return:active {
|
||||
background: #1c71d8;
|
||||
border-color: #3584e4;
|
||||
}
|
||||
@ -1,15 +1,15 @@
|
||||
sq_view {
|
||||
background-color: rgba(0, 0, 0, 255);
|
||||
color: #ffffff;
|
||||
background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/
|
||||
color: @theme_text_color; /*#ffffff;*/
|
||||
font-family: cantarell, sans-serif;
|
||||
}
|
||||
|
||||
sq_view sq_button {
|
||||
color: #deddda;
|
||||
background: #464448;
|
||||
color: @theme_fg_color; /*#deddda;*/
|
||||
background: mix(@theme_bg_color, @theme_base_color, -0.5); /* #464448; */
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #5e5c64;
|
||||
border-color: @borders; /* #5e5c64;*/
|
||||
border-radius: 3px;
|
||||
margin: 4px 2px 4px 2px;
|
||||
}
|
||||
@ -18,29 +18,32 @@ sq_view.wide sq_button {
|
||||
margin: 1px 1px 1px 1px;
|
||||
}
|
||||
|
||||
sq_button:active {
|
||||
background: #747077;
|
||||
border-color: #96949d;
|
||||
sq_button:active,
|
||||
sq_button.altline:active,
|
||||
sq_button.special:active,
|
||||
sq_button.wide:active {
|
||||
background: mix(@theme_bg_color, @theme_selected_bg_color, 0.4);/* #747077; */
|
||||
border-color: mix(@borders, @theme_selected_fg_color, 0.5);/* #96949d; */
|
||||
}
|
||||
|
||||
sq_button.altline,
|
||||
sq_button.special,
|
||||
sq_button.wide {
|
||||
background: #2b292f;
|
||||
border-color: #3e3a44;
|
||||
background: mix(@theme_bg_color, @theme_base_color, 0.5); /*#2b292f;*/
|
||||
border-color: @borders; /* #3e3a44; */
|
||||
}
|
||||
|
||||
sq_button.locked {
|
||||
background: #ffffff;
|
||||
color: #2b292f;
|
||||
background: @theme_fg_color; /*#ffffff;*/
|
||||
color: @theme_bg_color; /*#2b292f;*/
|
||||
}
|
||||
|
||||
#Return {
|
||||
background: #1c71d8;
|
||||
border-color: #1a5fb4;
|
||||
background: @theme_selected_bg_color; /* #1c71d8; */
|
||||
border-color: @borders; /*#1a5fb4;*/
|
||||
}
|
||||
|
||||
#Return:active {
|
||||
background: #1c71d8;
|
||||
border-color: #3584e4;
|
||||
background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/
|
||||
border-color: @borders; /*#3584e4;*/
|
||||
}
|
||||
|
||||
20
debian/changelog
vendored
20
debian/changelog
vendored
@ -1,3 +1,23 @@
|
||||
squeekboard (1.4.0) amber-phone; urgency=medium
|
||||
|
||||
* "text" property in layouts
|
||||
* Adjusts to user's color scheme
|
||||
|
||||
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 02 Dec 2019 19:37:01 +0000
|
||||
|
||||
squeekboard (1.3.2) amber-phone; urgency=medium
|
||||
|
||||
* Make sure all key presses get accepted by the compositor
|
||||
|
||||
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 26 Nov 2019 15:36:27 +0000
|
||||
|
||||
squeekboard (1.3.1) amber-phone; urgency=medium
|
||||
|
||||
* Update and fix layouts and languages
|
||||
* Make tests less likely to fail
|
||||
|
||||
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Wed, 20 Nov 2019 22:10:48 +0000
|
||||
|
||||
squeekboard (1.3.0) amber-phone; urgency=medium
|
||||
|
||||
* Language selection popup
|
||||
|
||||
11
debian/control
vendored
11
debian/control
vendored
@ -12,6 +12,7 @@ Build-Depends:
|
||||
libgtk-3-dev,
|
||||
libcroco3-dev,
|
||||
librust-bitflags-1-dev (>= 1.0),
|
||||
librust-clap-2+default-dev (>= 2.32),
|
||||
librust-gio+v2-44-dev,
|
||||
librust-glib+v2-44-dev,
|
||||
librust-glib-sys-dev,
|
||||
@ -40,3 +41,13 @@ Depends:
|
||||
${misc:Depends}
|
||||
Description: On-screen keyboard for Wayland
|
||||
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
|
||||
|
||||
Package: squeekboard-devel
|
||||
Architecture: linux-any
|
||||
Depends:
|
||||
${shlibs:Depends}
|
||||
${misc:Depends}
|
||||
Description: Resources for making Squeekboard layouts
|
||||
Tools for creating Squeekboard layouts:
|
||||
.
|
||||
* squeekboard-test-layout
|
||||
|
||||
1
debian/squeekboard-devel.install
vendored
Normal file
1
debian/squeekboard-devel.install
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/bin/squeekboard-test-layout /usr/bin
|
||||
2
debian/squeekboard.install
vendored
Normal file
2
debian/squeekboard.install
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
tools/squeekboard-restyled usr/bin
|
||||
usr/bin/squeekboard /usr/bin
|
||||
2
debian/squeekboard.lintian-overrides
vendored
2
debian/squeekboard.lintian-overrides
vendored
@ -1,2 +1,2 @@
|
||||
# yaml-rust 0.4.3 shares some roots with libyaml, including the string which lintian checks, creating a false positive
|
||||
squeekboard binary: embedded-library usr/bin/squeekboard-real: libyaml
|
||||
squeekboard binary: embedded-library usr/bin/squeekboard: libyaml
|
||||
|
||||
@ -158,6 +158,8 @@ eek_gtk_keyboard_real_button_press_event (GtkWidget *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render
|
||||
static gboolean
|
||||
eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
|
||||
@ -170,6 +172,18 @@ eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
eek_gtk_keyboard_leave_event (GtkWidget *self,
|
||||
GdkEventCrossing *event)
|
||||
{
|
||||
if (event->type == GDK_LEAVE_NOTIFY) {
|
||||
// TODO: can the event have different coords than the previous move event?
|
||||
release(EEK_GTK_KEYBOARD(self), event->time);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self,
|
||||
GdkEventMotion *event)
|
||||
@ -279,6 +293,9 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
|
||||
eek_gtk_keyboard_real_button_release_event;
|
||||
widget_class->motion_notify_event =
|
||||
eek_gtk_keyboard_real_motion_notify_event;
|
||||
widget_class->leave_notify_event =
|
||||
eek_gtk_keyboard_leave_event;
|
||||
|
||||
widget_class->touch_event = handle_touch_event;
|
||||
|
||||
gobject_class->set_property = eek_gtk_keyboard_set_property;
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
|
||||
#include "eek-keyboard.h"
|
||||
#include "eek-renderer.h"
|
||||
#include "src/style.h"
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
@ -50,8 +51,7 @@ typedef struct _EekRendererPrivate
|
||||
gint origin_x;
|
||||
gint origin_y;
|
||||
|
||||
PangoFontDescription *ascii_font;
|
||||
PangoFontDescription *font;
|
||||
PangoFontDescription *font; // owned reference
|
||||
cairo_surface_t *keyboard_surface;
|
||||
|
||||
} EekRendererPrivate;
|
||||
@ -59,8 +59,7 @@ typedef struct _EekRendererPrivate
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
|
||||
|
||||
/* eek-keyboard-drawing.c */
|
||||
static void eek_renderer_real_render_button_label (EekRenderer *self,
|
||||
PangoLayout *layout,
|
||||
static void eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, GtkStyleContext *ctx,
|
||||
const struct squeek_button *button);
|
||||
|
||||
static void invalidate (EekRenderer *renderer);
|
||||
@ -125,9 +124,9 @@ static void
|
||||
render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
|
||||
{
|
||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
||||
EekColor foreground;
|
||||
|
||||
eek_renderer_get_foreground_color (priv->view_context, &foreground);
|
||||
GdkRGBA color = {0};
|
||||
gtk_style_context_get_color (priv->view_context, GTK_STATE_FLAG_NORMAL, &color);
|
||||
|
||||
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
|
||||
|
||||
@ -152,15 +151,16 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
|
||||
cairo_translate (data.cr, bounds.x, bounds.y);
|
||||
|
||||
cairo_set_source_rgba (data.cr,
|
||||
foreground.red,
|
||||
foreground.green,
|
||||
foreground.blue,
|
||||
foreground.alpha);
|
||||
color.red,
|
||||
color.green,
|
||||
color.blue,
|
||||
color.alpha);
|
||||
|
||||
/* draw rows */
|
||||
squeek_view_foreach(level_keyboard_current(priv->keyboard),
|
||||
create_keyboard_surface_row_callback,
|
||||
&data);
|
||||
|
||||
cairo_restore (data.cr);
|
||||
|
||||
cairo_destroy (data.cr);
|
||||
@ -198,9 +198,6 @@ static void render_button_in_context(EekRenderer *self,
|
||||
struct button_place *place,
|
||||
gboolean active) {
|
||||
cairo_surface_t *outline_surface = NULL;
|
||||
PangoLayout *layout;
|
||||
PangoRectangle extents = { 0, };
|
||||
EekColor foreground;
|
||||
|
||||
/* render outline */
|
||||
EekBounds bounds = squeek_button_get_bounds(place->button);
|
||||
@ -231,7 +228,6 @@ static void render_button_in_context(EekRenderer *self,
|
||||
cairo_surface_destroy(outline_surface);
|
||||
cairo_paint (cr);
|
||||
|
||||
eek_renderer_get_foreground_color (ctx, &foreground);
|
||||
/* render icon (if any) */
|
||||
const char *icon_name = squeek_button_get_icon_name(place->button);
|
||||
|
||||
@ -249,10 +245,13 @@ static void render_button_in_context(EekRenderer *self,
|
||||
cairo_rectangle (cr, 0, 0, width, height);
|
||||
cairo_clip (cr);
|
||||
/* Draw the shape of the icon using the foreground color */
|
||||
cairo_set_source_rgba (cr, foreground.red,
|
||||
foreground.green,
|
||||
foreground.blue,
|
||||
foreground.alpha);
|
||||
GdkRGBA color = {0};
|
||||
gtk_style_context_get_color (ctx, GTK_STATE_FLAG_NORMAL, &color);
|
||||
|
||||
cairo_set_source_rgba (cr, color.red,
|
||||
color.green,
|
||||
color.blue,
|
||||
color.alpha);
|
||||
cairo_mask_surface (cr, icon_surface, 0.0, 0.0);
|
||||
cairo_surface_destroy(icon_surface);
|
||||
cairo_fill (cr);
|
||||
@ -260,25 +259,7 @@ static void render_button_in_context(EekRenderer *self,
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* render label */
|
||||
layout = pango_cairo_create_layout (cr);
|
||||
eek_renderer_real_render_button_label (self, layout, place->button);
|
||||
pango_layout_get_extents (layout, NULL, &extents);
|
||||
|
||||
cairo_save (cr);
|
||||
cairo_move_to
|
||||
(cr,
|
||||
(bounds.width - (double)extents.width / PANGO_SCALE) / 2,
|
||||
(bounds.height - (double)extents.height / PANGO_SCALE) / 2);
|
||||
|
||||
cairo_set_source_rgba (cr,
|
||||
foreground.red,
|
||||
foreground.green,
|
||||
foreground.blue,
|
||||
foreground.alpha);
|
||||
pango_cairo_show_layout (cr, layout);
|
||||
cairo_restore (cr);
|
||||
g_object_unref (layout);
|
||||
eek_renderer_render_button_label (self, cr, ctx, place->button);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -363,9 +344,10 @@ eek_renderer_apply_transformation_for_button (cairo_t *cr,
|
||||
}
|
||||
|
||||
static void
|
||||
eek_renderer_real_render_button_label (EekRenderer *self,
|
||||
PangoLayout *layout,
|
||||
const struct squeek_button *button)
|
||||
eek_renderer_render_button_label (EekRenderer *self,
|
||||
cairo_t *cr,
|
||||
GtkStyleContext *ctx,
|
||||
const struct squeek_button *button)
|
||||
{
|
||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
||||
|
||||
@ -381,16 +363,11 @@ eek_renderer_real_render_button_label (EekRenderer *self,
|
||||
|
||||
if (!priv->font) {
|
||||
const PangoFontDescription *base_font;
|
||||
gdouble ascii_size, size;
|
||||
gdouble size;
|
||||
|
||||
base_font = pango_context_get_font_description (priv->pcontext);
|
||||
// FIXME: Base font size on the same size unit used for button sizing,
|
||||
// and make the default about 1/3 of the current row height
|
||||
ascii_size = 30000.0;
|
||||
priv->ascii_font = pango_font_description_copy (base_font);
|
||||
pango_font_description_set_size (priv->ascii_font,
|
||||
(gint)round(ascii_size));
|
||||
|
||||
size = 30000.0;
|
||||
priv->font = pango_font_description_copy (base_font);
|
||||
pango_font_description_set_size (priv->font, (gint)round(size * 0.6));
|
||||
@ -403,6 +380,8 @@ eek_renderer_real_render_button_label (EekRenderer *self,
|
||||
font = pango_font_description_copy (priv->font);
|
||||
pango_font_description_set_size (font,
|
||||
(gint)round(pango_font_description_get_size (font) * scale));
|
||||
|
||||
PangoLayout *layout = pango_cairo_create_layout (cr);
|
||||
pango_layout_set_font_description (layout, font);
|
||||
pango_font_description_free (font);
|
||||
|
||||
@ -413,6 +392,27 @@ eek_renderer_real_render_button_label (EekRenderer *self,
|
||||
}
|
||||
pango_layout_set_width (layout,
|
||||
PANGO_SCALE * bounds.width * scale);
|
||||
|
||||
PangoRectangle extents = { 0, };
|
||||
pango_layout_get_extents (layout, NULL, &extents);
|
||||
|
||||
cairo_save (cr);
|
||||
cairo_move_to
|
||||
(cr,
|
||||
(bounds.width - (double)extents.width / PANGO_SCALE) / 2,
|
||||
(bounds.height - (double)extents.height / PANGO_SCALE) / 2);
|
||||
|
||||
GdkRGBA color = {0};
|
||||
gtk_style_context_get_color (ctx, GTK_STATE_FLAG_NORMAL, &color);
|
||||
|
||||
cairo_set_source_rgba (cr,
|
||||
color.red,
|
||||
color.green,
|
||||
color.blue,
|
||||
color.alpha);
|
||||
pango_cairo_show_layout (cr, layout);
|
||||
cairo_restore (cr);
|
||||
g_object_unref (layout);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -550,7 +550,6 @@ eek_renderer_finalize (GObject *object)
|
||||
g_object_unref(priv->css_provider);
|
||||
g_object_unref(priv->view_context);
|
||||
g_object_unref(priv->button_context);
|
||||
pango_font_description_free (priv->ascii_font);
|
||||
pango_font_description_free (priv->font);
|
||||
G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object);
|
||||
}
|
||||
@ -625,10 +624,7 @@ eek_renderer_init (EekRenderer *self)
|
||||
|
||||
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
|
||||
|
||||
/* Create a default CSS provider and load a style sheet */
|
||||
priv->css_provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_resource (priv->css_provider,
|
||||
"/sm/puri/squeekboard/style.css");
|
||||
priv->css_provider = squeek_load_style();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -850,22 +846,6 @@ eek_renderer_render_keyboard (EekRenderer *renderer,
|
||||
EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr);
|
||||
}
|
||||
|
||||
void
|
||||
eek_renderer_get_foreground_color (GtkStyleContext *context,
|
||||
EekColor *color)
|
||||
{
|
||||
g_return_if_fail (color);
|
||||
|
||||
GtkStateFlags flags = GTK_STATE_FLAG_NORMAL;
|
||||
GdkRGBA gcolor;
|
||||
|
||||
gtk_style_context_get_color (context, flags, &gcolor);
|
||||
color->red = gcolor.red;
|
||||
color->green = gcolor.green;
|
||||
color->blue = gcolor.blue;
|
||||
color->alpha = gcolor.alpha;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
|
||||
{
|
||||
|
||||
@ -86,16 +86,6 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name,
|
||||
|
||||
void eek_renderer_render_keyboard (EekRenderer *renderer,
|
||||
cairo_t *cr);
|
||||
|
||||
void eek_renderer_set_default_foreground_color
|
||||
(EekRenderer *renderer,
|
||||
const EekColor *color);
|
||||
void eek_renderer_set_default_background_color
|
||||
(EekRenderer *renderer,
|
||||
const EekColor *color);
|
||||
void eek_renderer_get_foreground_color
|
||||
(GtkStyleContext *context,
|
||||
EekColor *color);
|
||||
void eek_renderer_set_border_width (EekRenderer *renderer,
|
||||
gdouble border_width);
|
||||
void eek_renderer_apply_transformation_for_button
|
||||
|
||||
@ -72,35 +72,3 @@ eek_bounds_free (EekBounds *bounds)
|
||||
{
|
||||
g_slice_free (EekBounds, bounds);
|
||||
}
|
||||
|
||||
/* EekColor */
|
||||
G_DEFINE_BOXED_TYPE(EekColor, eek_color, eek_color_copy, eek_color_free);
|
||||
|
||||
EekColor *
|
||||
eek_color_copy (const EekColor *color)
|
||||
{
|
||||
return g_slice_dup (EekColor, color);
|
||||
}
|
||||
|
||||
void
|
||||
eek_color_free (EekColor *color)
|
||||
{
|
||||
g_slice_free (EekColor, color);
|
||||
}
|
||||
|
||||
EekColor *
|
||||
eek_color_new (gdouble red,
|
||||
gdouble green,
|
||||
gdouble blue,
|
||||
gdouble alpha)
|
||||
{
|
||||
EekColor *color;
|
||||
|
||||
color = g_slice_new (EekColor);
|
||||
color->red = red;
|
||||
color->green = green;
|
||||
color->blue = blue;
|
||||
color->alpha = alpha;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@ -34,10 +34,8 @@ G_BEGIN_DECLS
|
||||
|
||||
#define EEK_TYPE_POINT (eek_point_get_type ())
|
||||
#define EEK_TYPE_BOUNDS (eek_bounds_get_type ())
|
||||
#define EEK_TYPE_COLOR (eek_color_get_type ())
|
||||
|
||||
typedef struct _EekBounds EekBounds;
|
||||
typedef struct _EekColor EekColor;
|
||||
|
||||
typedef struct _EekboardContextService EekboardContextService;
|
||||
typedef struct _LevelKeyboard LevelKeyboard;
|
||||
@ -85,33 +83,6 @@ GType eek_bounds_get_type (void) G_GNUC_CONST;
|
||||
EekBounds *eek_bounds_copy (const EekBounds *bounds);
|
||||
void eek_bounds_free (EekBounds *bounds);
|
||||
|
||||
/**
|
||||
* EekColor:
|
||||
* @red: red component of color, between 0.0 and 1.0
|
||||
* @green: green component of color, between 0.0 and 1.0
|
||||
* @blue: blue component of color, between 0.0 and 1.0
|
||||
* @alpha: alpha component of color, between 0.0 and 1.0
|
||||
*
|
||||
* Color used for drawing.
|
||||
*/
|
||||
struct _EekColor
|
||||
{
|
||||
/*< public >*/
|
||||
gdouble red;
|
||||
gdouble green;
|
||||
gdouble blue;
|
||||
gdouble alpha;
|
||||
};
|
||||
|
||||
GType eek_color_get_type (void) G_GNUC_CONST;
|
||||
|
||||
EekColor *eek_color_new (gdouble red,
|
||||
gdouble green,
|
||||
gdouble blue,
|
||||
gdouble alpha);
|
||||
EekColor *eek_color_copy (const EekColor *color);
|
||||
void eek_color_free (EekColor *color);
|
||||
|
||||
struct transformation {
|
||||
gdouble origin_x;
|
||||
gdouble origin_y;
|
||||
|
||||
@ -207,22 +207,8 @@ static void
|
||||
settings_get_layout(GSettings *settings, char **type, char **layout)
|
||||
{
|
||||
GVariant *inputs = g_settings_get_value(settings, "sources");
|
||||
guint32 index;
|
||||
g_settings_get(settings, "current", "u", &index);
|
||||
|
||||
GVariantIter *iter;
|
||||
g_variant_get(inputs, "a(ss)", &iter);
|
||||
|
||||
for (unsigned i = 0;
|
||||
g_variant_iter_loop(iter, "(ss)", type, layout);
|
||||
i++) {
|
||||
if (i == index) {
|
||||
//printf("Found layout %s %s\n", *type, *layout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_variant_iter_free(iter);
|
||||
g_variant_unref(inputs);
|
||||
// current layout is always first
|
||||
g_variant_get_child(inputs, 0, "(ss)", type, layout);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -1,52 +1,10 @@
|
||||
extern crate rs;
|
||||
extern crate xkbcommon;
|
||||
|
||||
use rs::tests::check_builtin_layout;
|
||||
use std::env;
|
||||
|
||||
use rs::data::{ Layout, LoadError };
|
||||
|
||||
use xkbcommon::xkb;
|
||||
|
||||
|
||||
fn check_layout(name: &str) {
|
||||
let layout = Layout::from_resource(name)
|
||||
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
||||
.expect("layout broken");
|
||||
|
||||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||
|
||||
let keymap_str = layout.keymap_str
|
||||
.clone()
|
||||
.into_string().expect("Failed to decode keymap string");
|
||||
|
||||
let keymap = xkb::Keymap::new_from_string(
|
||||
&context,
|
||||
keymap_str.clone(),
|
||||
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||
).expect("Failed to create keymap");
|
||||
|
||||
let state = xkb::State::new(&keymap);
|
||||
|
||||
// "Press" each button with keysyms
|
||||
for view in layout.views.values() {
|
||||
for row in &view.rows {
|
||||
for button in &row.buttons {
|
||||
let keystate = button.state.borrow();
|
||||
for keycode in &keystate.keycodes {
|
||||
match state.key_get_one_sym(*keycode) {
|
||||
xkb::KEY_NoSymbol => {
|
||||
eprintln!("{}", keymap_str);
|
||||
panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> () {
|
||||
check_layout(env::args().nth(1).expect("No argument given").as_str());
|
||||
check_builtin_layout(
|
||||
env::args().nth(1).expect("No argument given").as_str()
|
||||
);
|
||||
}
|
||||
|
||||
10
meson.build
10
meson.build
@ -1,7 +1,7 @@
|
||||
project(
|
||||
'squeekboard',
|
||||
'c', 'rust',
|
||||
version: '1.3.0',
|
||||
version: '1.4.0',
|
||||
license: 'GPLv3',
|
||||
meson_version: '>=0.51.0',
|
||||
default_options: [
|
||||
@ -33,6 +33,11 @@ endif
|
||||
if get_option('buildtype') != 'plain'
|
||||
add_project_arguments('-fstack-protector-strong', language: 'c')
|
||||
endif
|
||||
if get_option('buildtype') == 'release'
|
||||
cargo_build_flags = ['--release'] # for artifacts
|
||||
else
|
||||
cargo_build_flags = []
|
||||
endif
|
||||
|
||||
prefix = get_option('prefix')
|
||||
datadir = join_paths(prefix, get_option('datadir'))
|
||||
@ -54,8 +59,9 @@ summary = [
|
||||
]
|
||||
message('\n'.join(summary))
|
||||
|
||||
cargo = find_program('cargo')
|
||||
dep_cargo = find_program('cargo')
|
||||
cargo_script = find_program('cargo.sh')
|
||||
cargo_build = find_program('cargo_build.sh')
|
||||
|
||||
subdir('data')
|
||||
subdir('protocols')
|
||||
|
||||
16
src/bin/test_layout.rs
Normal file
16
src/bin/test_layout.rs
Normal file
@ -0,0 +1,16 @@
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
extern crate rs;
|
||||
|
||||
use rs::tests::check_layout_file;
|
||||
|
||||
fn main() -> () {
|
||||
let matches = clap_app!(test_layout =>
|
||||
(name: "squeekboard-test-layout")
|
||||
(about: "Test keyboard layout for errors. Returns OK or an error message containing further information.")
|
||||
(@arg INPUT: +required "Yaml keyboard layout file to test")
|
||||
).get_matches();
|
||||
if check_layout_file(matches.value_of("INPUT").unwrap()) == () {
|
||||
println!("Test result: OK");
|
||||
}
|
||||
}
|
||||
282
src/data.rs
282
src/data.rs
@ -26,8 +26,8 @@ use ::xdg;
|
||||
// traits, derives
|
||||
use std::io::BufReader;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use serde::Deserialize;
|
||||
use util::WarningHandler;
|
||||
|
||||
|
||||
/// Gathers stuff defined in C or called by C
|
||||
@ -151,21 +151,30 @@ fn list_layout_sources(
|
||||
ret
|
||||
}
|
||||
|
||||
struct PrintWarnings;
|
||||
|
||||
impl WarningHandler for PrintWarnings {
|
||||
fn handle(&mut self, warning: &str) {
|
||||
println!("{}", warning);
|
||||
}
|
||||
}
|
||||
|
||||
fn load_layout_data(source: DataSource)
|
||||
-> Result<::layout::LayoutData, LoadError>
|
||||
{
|
||||
let handler = PrintWarnings{};
|
||||
match source {
|
||||
DataSource::File(path) => {
|
||||
Layout::from_file(path.clone())
|
||||
.map_err(LoadError::BadData)
|
||||
.and_then(|layout|
|
||||
layout.build().map_err(LoadError::BadKeyMap)
|
||||
layout.build(handler).0.map_err(LoadError::BadKeyMap)
|
||||
)
|
||||
},
|
||||
DataSource::Resource(name) => {
|
||||
Layout::from_resource(&name)
|
||||
.and_then(|layout|
|
||||
layout.build().map_err(LoadError::BadKeyMap)
|
||||
layout.build(handler).0.map_err(LoadError::BadKeyMap)
|
||||
)
|
||||
},
|
||||
}
|
||||
@ -225,22 +234,28 @@ struct Bounds {
|
||||
/// Buttons are embedded in a single string
|
||||
type ButtonIds = String;
|
||||
|
||||
/// All info about a single button
|
||||
/// Buttons can have multiple instances though.
|
||||
#[derive(Debug, Default, Deserialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct ButtonMeta {
|
||||
/// Action other than keysym (conflicts with keysym)
|
||||
/// Special action to perform on activation. Conflicts with keysym, text.
|
||||
action: Option<Action>,
|
||||
/// The name of the outline. If not present, will be "default"
|
||||
outline: Option<String>,
|
||||
/// FIXME: start using it
|
||||
/// The name of the XKB keysym to emit on activation.
|
||||
/// Conflicts with action, text
|
||||
keysym: Option<String>,
|
||||
/// If not present, will be derived from the button ID
|
||||
/// The text to submit on activation. Will be derived from ID if not present
|
||||
/// Conflicts with action, keysym
|
||||
text: Option<String>,
|
||||
/// If not present, will be derived from text or the button ID
|
||||
label: Option<String>,
|
||||
/// Conflicts with label
|
||||
icon: Option<String>,
|
||||
/// The name of the outline. If not present, will be "default"
|
||||
outline: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
enum Action {
|
||||
#[serde(rename="locking")]
|
||||
@ -296,7 +311,7 @@ impl Layout {
|
||||
.map_err(LoadError::BadResource)
|
||||
}
|
||||
|
||||
fn from_file(path: PathBuf) -> Result<Layout, Error> {
|
||||
pub fn from_file(path: PathBuf) -> Result<Layout, Error> {
|
||||
let infile = BufReader::new(
|
||||
fs::OpenOptions::new()
|
||||
.read(true)
|
||||
@ -305,8 +320,8 @@ impl Layout {
|
||||
serde_yaml::from_reader(infile).map_err(Error::Yaml)
|
||||
}
|
||||
|
||||
pub fn build(self)
|
||||
-> Result<::layout::LayoutData, FormattingError>
|
||||
pub fn build<H: WarningHandler>(self, mut warning_handler: H)
|
||||
-> (Result<::layout::LayoutData, FormattingError>, H)
|
||||
{
|
||||
let button_names = self.views.values()
|
||||
.flat_map(|rows| {
|
||||
@ -323,7 +338,8 @@ impl Layout {
|
||||
create_action(
|
||||
&self.buttons,
|
||||
name,
|
||||
self.views.keys().collect()
|
||||
self.views.keys().collect(),
|
||||
&mut warning_handler,
|
||||
)
|
||||
)}).collect();
|
||||
|
||||
@ -368,13 +384,15 @@ impl Layout {
|
||||
)
|
||||
});
|
||||
|
||||
let button_states
|
||||
= HashMap::<String, KeyState>::from_iter(
|
||||
button_states
|
||||
);
|
||||
let button_states = HashMap::<String, KeyState>::from_iter(
|
||||
button_states
|
||||
);
|
||||
|
||||
// TODO: generate from symbols
|
||||
let keymap_str = generate_keymap(&button_states)?;
|
||||
let keymap_str = match generate_keymap(&button_states) {
|
||||
Err(e) => { return (Err(e), warning_handler) },
|
||||
Ok(v) => v,
|
||||
};
|
||||
|
||||
let button_states_cache = hash_map_map(
|
||||
button_states,
|
||||
@ -405,7 +423,8 @@ impl Layout {
|
||||
name,
|
||||
button_states_cache.get(name.into())
|
||||
.expect("Button state not created")
|
||||
.clone()
|
||||
.clone(),
|
||||
&mut warning_handler,
|
||||
))
|
||||
}).collect(),
|
||||
})
|
||||
@ -414,116 +433,148 @@ impl Layout {
|
||||
)})
|
||||
);
|
||||
|
||||
Ok(::layout::LayoutData {
|
||||
views: views,
|
||||
keymap_str: {
|
||||
CString::new(keymap_str)
|
||||
.expect("Invalid keymap string generated")
|
||||
},
|
||||
})
|
||||
(
|
||||
Ok(::layout::LayoutData {
|
||||
views: views,
|
||||
keymap_str: {
|
||||
CString::new(keymap_str)
|
||||
.expect("Invalid keymap string generated")
|
||||
},
|
||||
}),
|
||||
warning_handler,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_action(
|
||||
fn create_action<H: WarningHandler>(
|
||||
button_info: &HashMap<String, ButtonMeta>,
|
||||
name: &str,
|
||||
view_names: Vec<&String>,
|
||||
warning_handler: &mut H,
|
||||
) -> ::action::Action {
|
||||
let default_meta = ButtonMeta::default();
|
||||
let symbol_meta = button_info.get(name)
|
||||
.unwrap_or(&default_meta);
|
||||
|
||||
fn filter_view_name(
|
||||
button_name: &str,
|
||||
view_name: String,
|
||||
view_names: &Vec<&String>
|
||||
) -> String {
|
||||
if view_names.contains(&&view_name) {
|
||||
view_name
|
||||
} else {
|
||||
eprintln!(
|
||||
"Button {} switches to missing view {}",
|
||||
button_name,
|
||||
view_name
|
||||
);
|
||||
"base".into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn keysym_valid(name: &str) -> bool {
|
||||
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
|
||||
}
|
||||
|
||||
let keysyms = match &symbol_meta.action {
|
||||
// Non-submit action
|
||||
Some(_) => Vec::new(),
|
||||
// Submit action
|
||||
None => match &symbol_meta.keysym {
|
||||
// Keysym given explicitly
|
||||
Some(keysym) => vec!(match keysym_valid(keysym.as_str()) {
|
||||
true => keysym.clone(),
|
||||
false => {
|
||||
eprintln!("Keysym name invalid: {}", keysym);
|
||||
"space".into() // placeholder
|
||||
},
|
||||
}),
|
||||
// Keysyms left open to derive
|
||||
// TODO: when button name is meant diretly as xkb keysym name,
|
||||
// mark it so, e.g. with a "#"
|
||||
None => match keysym_valid(name) {
|
||||
// Button name is actually a valid xkb name
|
||||
true => vec!(String::from(name)),
|
||||
// Button name is not a valid xkb name,
|
||||
// so assume it's a literal string to be submitted
|
||||
false => {
|
||||
if name.chars().count() == 0 {
|
||||
// A name read from yaml with no valid Unicode.
|
||||
// Highly improbable, but let's be safe.
|
||||
eprintln!("Key {} doesn't have any characters", name);
|
||||
vec!("space".into()) // placeholder
|
||||
} else {
|
||||
name.chars().map(|codepoint| {
|
||||
let codepoint_string = codepoint.to_string();
|
||||
match keysym_valid(codepoint_string.as_str()) {
|
||||
true => codepoint_string,
|
||||
false => format!("U{:04X}", codepoint as u32),
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
enum SubmitData {
|
||||
Action(Action),
|
||||
Text(String),
|
||||
Keysym(String),
|
||||
};
|
||||
|
||||
match &symbol_meta.action {
|
||||
Some(Action::SetView(view_name)) => ::action::Action::SetLevel(
|
||||
filter_view_name(name, view_name.clone(), &view_names)
|
||||
let submission = match (
|
||||
&symbol_meta.action,
|
||||
&symbol_meta.keysym,
|
||||
&symbol_meta.text
|
||||
) {
|
||||
(Some(action), None, None) => SubmitData::Action(action.clone()),
|
||||
(None, Some(keysym), None) => SubmitData::Keysym(keysym.clone()),
|
||||
(None, None, Some(text)) => SubmitData::Text(text.clone()),
|
||||
(None, None, None) => SubmitData::Text(name.into()),
|
||||
_ => {
|
||||
warning_handler.handle(&format!(
|
||||
"Button {} has more than one of (action, keysym, text)",
|
||||
name
|
||||
));
|
||||
SubmitData::Text("".into())
|
||||
},
|
||||
};
|
||||
|
||||
fn filter_view_name<H: WarningHandler>(
|
||||
button_name: &str,
|
||||
view_name: String,
|
||||
view_names: &Vec<&String>,
|
||||
warning_handler: &mut H,
|
||||
) -> String {
|
||||
if view_names.contains(&&view_name) {
|
||||
view_name
|
||||
} else {
|
||||
warning_handler.handle(&format!("Button {} switches to missing view {}",
|
||||
button_name,
|
||||
view_name,
|
||||
));
|
||||
"base".into()
|
||||
}
|
||||
}
|
||||
|
||||
match submission {
|
||||
SubmitData::Action(
|
||||
Action::SetView(view_name)
|
||||
) => ::action::Action::SetLevel(
|
||||
filter_view_name(
|
||||
name, view_name.clone(), &view_names,
|
||||
warning_handler,
|
||||
)
|
||||
),
|
||||
Some(Action::Locking {
|
||||
SubmitData::Action(Action::Locking {
|
||||
lock_view, unlock_view
|
||||
}) => ::action::Action::LockLevel {
|
||||
lock: filter_view_name(name, lock_view.clone(), &view_names),
|
||||
lock: filter_view_name(
|
||||
name,
|
||||
lock_view.clone(),
|
||||
&view_names,
|
||||
warning_handler,
|
||||
),
|
||||
unlock: filter_view_name(
|
||||
name,
|
||||
unlock_view.clone(),
|
||||
&view_names
|
||||
&view_names,
|
||||
warning_handler,
|
||||
),
|
||||
},
|
||||
Some(Action::ShowPrefs) => ::action::Action::ShowPreferences,
|
||||
None => ::action::Action::Submit {
|
||||
SubmitData::Action(
|
||||
Action::ShowPrefs
|
||||
) => ::action::Action::ShowPreferences,
|
||||
SubmitData::Keysym(keysym) => ::action::Action::Submit {
|
||||
text: None,
|
||||
keys: keysyms.into_iter().map(::action::KeySym).collect(),
|
||||
keys: vec!(::action::KeySym(
|
||||
match keysym_valid(keysym.as_str()) {
|
||||
true => keysym.clone(),
|
||||
false => {
|
||||
warning_handler.handle(&format!(
|
||||
"Keysym name invalid: {}",
|
||||
keysym,
|
||||
));
|
||||
"space".into() // placeholder
|
||||
},
|
||||
}
|
||||
)),
|
||||
},
|
||||
SubmitData::Text(text) => ::action::Action::Submit {
|
||||
text: {
|
||||
CString::new(text.clone())
|
||||
.map_err(|e| {
|
||||
warning_handler.handle(&format!(
|
||||
"Text {} contains problems: {:?}",
|
||||
text,
|
||||
e
|
||||
));
|
||||
e
|
||||
}).ok()
|
||||
},
|
||||
keys: text.chars().map(|codepoint| {
|
||||
let codepoint_string = codepoint.to_string();
|
||||
::action::KeySym(match keysym_valid(codepoint_string.as_str()) {
|
||||
true => codepoint_string,
|
||||
false => format!("U{:04X}", codepoint as u32),
|
||||
})
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: Since this will receive user-provided data,
|
||||
/// all .expect() on them should be turned into soft fails
|
||||
fn create_button(
|
||||
fn create_button<H: WarningHandler>(
|
||||
button_info: &HashMap<String, ButtonMeta>,
|
||||
outlines: &HashMap<String, Outline>,
|
||||
name: &str,
|
||||
state: Rc<RefCell<KeyState>>,
|
||||
warning_handler: &mut H,
|
||||
) -> ::layout::Button {
|
||||
let cname = CString::new(name.clone())
|
||||
.expect("Bad name");
|
||||
@ -539,6 +590,18 @@ fn create_button(
|
||||
} else if let Some(icon) = &button_meta.icon {
|
||||
::layout::Label::IconName(CString::new(icon.as_str())
|
||||
.expect("Bad icon"))
|
||||
} else if let Some(text) = &button_meta.text {
|
||||
::layout::Label::Text(
|
||||
CString::new(text.as_str())
|
||||
.unwrap_or_else(|e| {
|
||||
warning_handler.handle(&format!(
|
||||
"Text {} is invalid: {}",
|
||||
text,
|
||||
e,
|
||||
));
|
||||
CString::new("").unwrap()
|
||||
})
|
||||
)
|
||||
} else {
|
||||
::layout::Label::Text(cname.clone())
|
||||
};
|
||||
@ -548,7 +611,7 @@ fn create_button(
|
||||
if outlines.contains_key(outline) {
|
||||
outline.clone()
|
||||
} else {
|
||||
eprintln!("Outline named {} does not exist! Using default for button {}", outline, name);
|
||||
warning_handler.handle(&format!("Outline named {} does not exist! Using default for button {}", outline, name));
|
||||
"default".into()
|
||||
}
|
||||
}
|
||||
@ -558,7 +621,9 @@ fn create_button(
|
||||
let outline = outlines.get(&outline_name)
|
||||
.map(|outline| (*outline).clone())
|
||||
.unwrap_or_else(|| {
|
||||
eprintln!("No default outline defied Using 1x1!");
|
||||
warning_handler.handle(
|
||||
&format!("No default outline defined! Using 1x1!")
|
||||
);
|
||||
Outline {
|
||||
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
|
||||
}
|
||||
@ -585,6 +650,14 @@ mod tests {
|
||||
|
||||
use std::error::Error as ErrorTrait;
|
||||
|
||||
struct PanicWarn;
|
||||
|
||||
impl WarningHandler for PanicWarn {
|
||||
fn handle(&mut self, warning: &str) {
|
||||
panic!("{}", warning);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_path() {
|
||||
assert_eq!(
|
||||
@ -599,6 +672,7 @@ mod tests {
|
||||
icon: None,
|
||||
keysym: None,
|
||||
action: None,
|
||||
text: None,
|
||||
label: Some("test".into()),
|
||||
outline: None,
|
||||
}
|
||||
@ -656,7 +730,7 @@ mod tests {
|
||||
fn test_layout_punctuation() {
|
||||
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
|
||||
.unwrap()
|
||||
.build()
|
||||
.build(PanicWarn).0
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
out.views["base"]
|
||||
@ -671,7 +745,7 @@ mod tests {
|
||||
fn test_layout_unicode() {
|
||||
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
|
||||
.unwrap()
|
||||
.build()
|
||||
.build(PanicWarn).0
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
out.views["base"]
|
||||
@ -687,7 +761,7 @@ mod tests {
|
||||
fn test_layout_unicode_multi() {
|
||||
let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
|
||||
.unwrap()
|
||||
.build()
|
||||
.build(PanicWarn).0
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
out.views["base"]
|
||||
@ -702,7 +776,7 @@ mod tests {
|
||||
#[test]
|
||||
fn parsing_fallback() {
|
||||
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
|
||||
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
||||
.map(|layout| layout.build(PanicWarn).0.unwrap())
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
@ -742,18 +816,20 @@ mod tests {
|
||||
".".into() => ButtonMeta {
|
||||
icon: None,
|
||||
keysym: None,
|
||||
text: None,
|
||||
action: None,
|
||||
label: Some("test".into()),
|
||||
outline: None,
|
||||
}
|
||||
},
|
||||
".",
|
||||
Vec::new()
|
||||
Vec::new(),
|
||||
&mut PanicWarn,
|
||||
),
|
||||
::action::Action::Submit {
|
||||
text: None,
|
||||
text: Some(CString::new(".").unwrap()),
|
||||
keys: vec!(::action::KeySym("U002E".into())),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,14 +51,26 @@ pub struct KeyState {
|
||||
pub action: Action,
|
||||
}
|
||||
|
||||
/// Generates a mapping where each key gets a keycode, starting from 8
|
||||
/// Sorts an iterator by converting it to a Vector and back
|
||||
fn sorted<'a, I: Iterator<Item=&'a str>>(
|
||||
iter: I
|
||||
) -> impl Iterator<Item=&'a str> {
|
||||
let mut v: Vec<&'a str> = iter.collect();
|
||||
v.sort();
|
||||
v.into_iter()
|
||||
}
|
||||
|
||||
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
||||
/// HACK: starting from 9, because 8 results in keycode 0,
|
||||
/// which the compositor likes to discard
|
||||
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
|
||||
key_names: C
|
||||
) -> HashMap<String, u32> {
|
||||
HashMap::from_iter(
|
||||
key_names.into_iter()
|
||||
// sort to remove a source of indeterminism in keycode assignment
|
||||
sorted(key_names.into_iter())
|
||||
.map(|name| String::from(name))
|
||||
.zip(8..)
|
||||
.zip(9..)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -24,5 +24,7 @@ mod outputs;
|
||||
mod popover;
|
||||
mod resources;
|
||||
mod submission;
|
||||
mod util;
|
||||
mod style;
|
||||
pub mod tests;
|
||||
pub mod util;
|
||||
mod xdg;
|
||||
|
||||
@ -58,7 +58,7 @@ rslibs = custom_target(
|
||||
output: ['librs.a'],
|
||||
install: false,
|
||||
console: true,
|
||||
command: [cargo_script, '@OUTPUT@', 'build']
|
||||
command: [cargo_build] + cargo_build_flags + ['@OUTPUT@', '--lib']
|
||||
)
|
||||
|
||||
build_rstests = custom_target(
|
||||
@ -72,14 +72,16 @@ build_rstests = custom_target(
|
||||
output: ['src'],
|
||||
install: false,
|
||||
console: true,
|
||||
command: [cargo_script, '', 'build', '--tests'],
|
||||
command: [cargo_script, 'test', '--no-run'],
|
||||
depends: rslibs, # no point building tests if the code itself fails
|
||||
)
|
||||
|
||||
test(
|
||||
'rstest',
|
||||
cargo_script,
|
||||
args: ['', 'test'],
|
||||
args: ['test'],
|
||||
# this is a whole Carg-based test suite, let it run for a while
|
||||
timeout: 900,
|
||||
depends: build_rstests,
|
||||
)
|
||||
|
||||
@ -95,20 +97,7 @@ libsqueekboard = static_library('libsqueekboard',
|
||||
'-DEEK_COMPILATION=1'],
|
||||
)
|
||||
|
||||
# the straight binary needs to be demoted in favor of the wrapper script
|
||||
# due to styling being inconsistent
|
||||
bindir = join_paths(prefix, get_option('bindir'))
|
||||
wrapper_conf = configuration_data()
|
||||
wrapper_conf.set('bindir', bindir)
|
||||
configure_file(
|
||||
input: '../tools/squeekboard.in',
|
||||
output: 'squeekboard',
|
||||
install_dir: bindir,
|
||||
configuration: wrapper_conf,
|
||||
install: true,
|
||||
)
|
||||
|
||||
squeekboard = executable('squeekboard-real',
|
||||
squeekboard = executable('squeekboard',
|
||||
'server-main.c',
|
||||
wl_proto_sources,
|
||||
squeekboard_resources,
|
||||
@ -122,3 +111,17 @@ squeekboard = executable('squeekboard-real',
|
||||
'-DEEKBOARD_COMPILATION=1',
|
||||
'-DEEK_COMPILATION=1'],
|
||||
)
|
||||
|
||||
bindir = join_paths(prefix, get_option('bindir'))
|
||||
|
||||
test_layout = custom_target('squeekboard-test-layout',
|
||||
build_by_default: true,
|
||||
# meson doesn't track all inputs, cargo does
|
||||
build_always_stale: true,
|
||||
output: ['squeekboard-test-layout'],
|
||||
console: true,
|
||||
command: [cargo_build] + cargo_build_flags
|
||||
+ ['--rename', 'test_layout', '@OUTPUT@', '--bin', 'test_layout'],
|
||||
install: true,
|
||||
install_dir: bindir,
|
||||
)
|
||||
|
||||
115
src/popover.rs
115
src/popover.rs
@ -7,9 +7,9 @@ use ::locale::compare_current_locale;
|
||||
use ::locale_config::system_locale;
|
||||
use ::resources;
|
||||
|
||||
use gio::ActionExt;
|
||||
use gio::ActionMapExt;
|
||||
use gio::SettingsExt;
|
||||
use gio::SimpleActionExt;
|
||||
use glib::translate::FromGlibPtrNone;
|
||||
use glib::variant::ToVariant;
|
||||
use gtk::PopoverExt;
|
||||
@ -18,8 +18,11 @@ use std::io::Write;
|
||||
|
||||
mod variants {
|
||||
use glib;
|
||||
use glib::Variant;
|
||||
use glib_sys;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use glib::ToVariant;
|
||||
use glib::translate::FromGlibPtrFull;
|
||||
use glib::translate::ToGlibPtr;
|
||||
|
||||
@ -49,6 +52,44 @@ mod variants {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// "a(ss)" variant
|
||||
/// Rust doesn't allow implementing existing traits for existing types
|
||||
pub struct ArrayPairString(pub Vec<(String, String)>);
|
||||
|
||||
impl ToVariant for ArrayPairString {
|
||||
fn to_variant(&self) -> Variant {
|
||||
let tspec = "a(ss)".to_glib_none();
|
||||
let builder = unsafe {
|
||||
let vtype = glib_sys::g_variant_type_checked_(tspec.0);
|
||||
glib_sys::g_variant_builder_new(vtype)
|
||||
};
|
||||
let ispec = "(ss)".to_glib_none();
|
||||
for (a, b) in &self.0 {
|
||||
let a = a.to_glib_none();
|
||||
let b = b.to_glib_none();
|
||||
// string pointers are weak references
|
||||
// and will get silently invalidated
|
||||
// as soon as the source is out of scope
|
||||
{
|
||||
let a: *const c_char = a.0;
|
||||
let b: *const c_char = b.0;
|
||||
unsafe {
|
||||
glib_sys::g_variant_builder_add(
|
||||
builder,
|
||||
ispec.0,
|
||||
a, b
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
let ret = glib_sys::g_variant_builder_end(builder);
|
||||
glib_sys::g_variant_builder_unref(builder);
|
||||
glib::Variant::from_glib_full(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
|
||||
@ -88,12 +129,15 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
|
||||
fn set_layout(kind: String, name: String) {
|
||||
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||
let inputs = settings.get_value("sources").unwrap();
|
||||
let inputs = variants::get_tuples(inputs).into_iter();
|
||||
for (index, (ikind, iname)) in inputs.enumerate() {
|
||||
if (&ikind, &iname) == (&kind, &name) {
|
||||
settings.set_uint("current", index as u32);
|
||||
}
|
||||
}
|
||||
let current = (kind.clone(), name.clone());
|
||||
let inputs = variants::get_tuples(inputs).into_iter()
|
||||
.filter(|t| t != ¤t);
|
||||
let inputs = vec![(kind, name)].into_iter()
|
||||
.chain(inputs).collect();
|
||||
settings.set_value(
|
||||
"sources",
|
||||
&variants::ArrayPairString(inputs).to_variant()
|
||||
);
|
||||
settings.apply();
|
||||
}
|
||||
|
||||
@ -103,7 +147,6 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
||||
|
||||
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||
let inputs = settings.get_value("sources").unwrap();
|
||||
let current = settings.get_uint("current") as usize;
|
||||
let inputs = variants::get_tuples(inputs);
|
||||
|
||||
let input_names: Vec<&str> = inputs.iter()
|
||||
@ -152,39 +195,35 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
||||
height: position.width.floor() as i32,
|
||||
});
|
||||
|
||||
let initial_state = input_names[current].to_variant();
|
||||
if let Some(current_name) = input_names.get(0) {
|
||||
let current_name = current_name.to_variant();
|
||||
|
||||
let layout_action = gio::SimpleAction::new_stateful(
|
||||
"layout",
|
||||
Some(initial_state.type_()),
|
||||
&initial_state,
|
||||
);
|
||||
let layout_action = gio::SimpleAction::new_stateful(
|
||||
"layout",
|
||||
Some(current_name.type_()),
|
||||
¤t_name,
|
||||
);
|
||||
|
||||
let action_group = gio::SimpleActionGroup::new();
|
||||
action_group.add_action(&layout_action);
|
||||
layout_action.connect_change_state(|_action, state| {
|
||||
match state {
|
||||
Some(v) => {
|
||||
v.get::<String>()
|
||||
.or_else(|| {
|
||||
eprintln!("Variant is not string: {:?}", v);
|
||||
None
|
||||
})
|
||||
.map(|state| set_layout("xkb".into(), state));
|
||||
},
|
||||
None => eprintln!("No variant selected"),
|
||||
};
|
||||
});
|
||||
|
||||
let action_group = gio::SimpleActionGroup::new();
|
||||
action_group.add_action(&layout_action);
|
||||
|
||||
menu.insert_action_group("popup", Some(&action_group));
|
||||
};
|
||||
|
||||
menu.insert_action_group("popup", Some(&action_group));
|
||||
menu.bind_model(Some(&model), Some("popup"));
|
||||
|
||||
menu.connect_closed(move |_menu| {
|
||||
let state = match layout_action.get_state() {
|
||||
Some(v) => {
|
||||
let s = v.get::<String>().or_else(|| {
|
||||
eprintln!("Variant is not string: {:?}", v);
|
||||
None
|
||||
});
|
||||
// FIXME: the `get_state` docs call for unrefing,
|
||||
// but the function is nowhere to be found
|
||||
// glib::Variant::unref(v);
|
||||
s
|
||||
},
|
||||
None => {
|
||||
eprintln!("No variant selected");
|
||||
None
|
||||
},
|
||||
};
|
||||
set_layout("xkb".into(), state.unwrap_or("us".into()));
|
||||
});
|
||||
|
||||
menu.popup();
|
||||
}
|
||||
|
||||
@ -13,11 +13,12 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
|
||||
("us", include_str!("../data/keyboards/us.yaml")),
|
||||
("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
|
||||
("de", include_str!("../data/keyboards/de.yaml")),
|
||||
("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
|
||||
("el", include_str!("../data/keyboards/el.yaml")),
|
||||
("es", include_str!("../data/keyboards/es.yaml")),
|
||||
("fi", include_str!("../data/keyboards/fi.yaml")),
|
||||
("it", include_str!("../data/keyboards/it.yaml")),
|
||||
("ja+kana", include_str!("../data/keyboards/ja+kana.yaml")),
|
||||
("jp+kana", include_str!("../data/keyboards/jp+kana.yaml")),
|
||||
("no", include_str!("../data/keyboards/no.yaml")),
|
||||
("number", include_str!("../data/keyboards/number.yaml")),
|
||||
("se", include_str!("../data/keyboards/se.yaml")),
|
||||
@ -39,7 +40,10 @@ pub fn get_keyboard(needle: &str) -> Option<&'static str> {
|
||||
|
||||
/// Translations of the layout identifier strings
|
||||
const LAYOUT_NAMES: &[(*const str, *const str)] = &[
|
||||
("de-DE", include_str!("../data/langs/de-DE.txt")),
|
||||
("en-US", include_str!("../data/langs/en-US.txt")),
|
||||
("es-ES", include_str!("../data/langs/es-ES.txt")),
|
||||
("ja-JP", include_str!("../data/langs/ja-JP.txt")),
|
||||
("pl-PL", include_str!("../data/langs/pl-PL.txt")),
|
||||
];
|
||||
|
||||
|
||||
7
src/style.h
Normal file
7
src/style.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef __STYLE_H
|
||||
#define __STYLE_H
|
||||
#include "gtk/gtk.h"
|
||||
|
||||
GtkCssProvider *squeek_load_style();
|
||||
|
||||
#endif
|
||||
124
src/style.rs
Normal file
124
src/style.rs
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Red Hat, Inc.
|
||||
* Copyright (C) 2019 Purism, SPC
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
|
||||
*/
|
||||
|
||||
/*! CSS data loading */
|
||||
|
||||
use std::env;
|
||||
|
||||
use glib::object::ObjectExt;
|
||||
use util::Warn;
|
||||
|
||||
/// Gathers stuff defined in C or called by C
|
||||
pub mod c {
|
||||
use super::*;
|
||||
use gio;
|
||||
use gtk;
|
||||
use gtk_sys;
|
||||
|
||||
use gtk::CssProviderExt;
|
||||
use glib::translate::ToGlibPtr;
|
||||
|
||||
/// Loads the layout style based on current theme
|
||||
/// without having to worry about string allocation
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_load_style() -> *const gtk_sys::GtkCssProvider {
|
||||
unsafe { gtk::set_initialized() };
|
||||
let theme = gtk::Settings::get_default()
|
||||
.map(|settings| get_theme_name(&settings));
|
||||
|
||||
let css_name = path_from_theme(theme);
|
||||
|
||||
let resource_name = if gio::resources_get_info(
|
||||
&css_name,
|
||||
gio::ResourceLookupFlags::NONE
|
||||
).is_ok() {
|
||||
css_name
|
||||
} else { // use default if this path doesn't exist
|
||||
path_from_theme(None)
|
||||
};
|
||||
|
||||
let provider = gtk::CssProvider::new();
|
||||
provider.load_from_resource(&resource_name);
|
||||
provider.to_glib_full()
|
||||
}
|
||||
}
|
||||
|
||||
// not Adwaita, but rather fall back to default
|
||||
const DEFAULT_THEME_NAME: &str = "";
|
||||
|
||||
struct GtkTheme {
|
||||
name: String,
|
||||
variant: Option<String>,
|
||||
}
|
||||
|
||||
/// Gets theme as determined by the toolkit
|
||||
/// Ported from GTK's gtksettings.c
|
||||
fn get_theme_name(settings: >k::Settings) -> GtkTheme {
|
||||
let env_theme = env::var("GTK_THEME")
|
||||
.map(|theme| {
|
||||
let mut parts = theme.splitn(2, ":");
|
||||
GtkTheme {
|
||||
// guaranteed at least empty string
|
||||
// as the first result from splitting a string
|
||||
name: parts.next().unwrap().into(),
|
||||
variant: parts.next().map(String::from)
|
||||
}
|
||||
})
|
||||
.map_err(|e| {
|
||||
match &e {
|
||||
env::VarError::NotPresent => {},
|
||||
e => eprintln!("GTK_THEME variable invalid: {}", e),
|
||||
};
|
||||
e
|
||||
}).ok();
|
||||
|
||||
match env_theme {
|
||||
Some(theme) => theme,
|
||||
None => GtkTheme {
|
||||
name: {
|
||||
settings.get_property("gtk-theme-name")
|
||||
.ok_warn("No theme name")
|
||||
.and_then(|value| value.get::<String>())
|
||||
.unwrap_or(DEFAULT_THEME_NAME.into())
|
||||
},
|
||||
variant: {
|
||||
settings.get_property("gtk-application-prefer-dark-theme")
|
||||
.ok_warn("No settings key")
|
||||
.and_then(|value| value.get::<bool>())
|
||||
.and_then(|dark_preferred| match dark_preferred {
|
||||
true => Some("dark".into()),
|
||||
false => None,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn path_from_theme(theme: Option<GtkTheme>) -> String {
|
||||
format!(
|
||||
"/sm/puri/squeekboard/style{}.css",
|
||||
match theme {
|
||||
Some(GtkTheme { name, variant: Some(variant) }) => {
|
||||
format!("-{}:{}", name, variant)
|
||||
},
|
||||
Some(GtkTheme { name, variant: None }) => format!("-{}", name),
|
||||
None => "".into(),
|
||||
}
|
||||
)
|
||||
}
|
||||
78
src/tests.rs
Normal file
78
src/tests.rs
Normal file
@ -0,0 +1,78 @@
|
||||
/*! Testing functionality */
|
||||
|
||||
use ::data::Layout;
|
||||
use xkbcommon::xkb;
|
||||
|
||||
use ::util::WarningHandler;
|
||||
|
||||
|
||||
pub struct CountAndPrint(u32);
|
||||
|
||||
impl WarningHandler for CountAndPrint {
|
||||
fn handle(&mut self, warning: &str) {
|
||||
self.0 = self.0 + 1;
|
||||
println!("{}", warning);
|
||||
}
|
||||
}
|
||||
|
||||
impl CountAndPrint {
|
||||
fn new() -> CountAndPrint {
|
||||
CountAndPrint(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_builtin_layout(name: &str) {
|
||||
check_layout(Layout::from_resource(name).expect("Invalid layout data"))
|
||||
}
|
||||
|
||||
pub fn check_layout_file(path: &str) {
|
||||
check_layout(Layout::from_file(path.into()).expect("Invalid layout file"))
|
||||
}
|
||||
|
||||
fn check_layout(layout: Layout) {
|
||||
let handler = CountAndPrint::new();
|
||||
let (layout, handler) = layout.build(handler);
|
||||
|
||||
if handler.0 > 0 {
|
||||
println!("{} mistakes in layout", handler.0)
|
||||
}
|
||||
|
||||
let layout = layout.expect("layout broken");
|
||||
|
||||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||
|
||||
let keymap_str = layout.keymap_str
|
||||
.clone()
|
||||
.into_string().expect("Failed to decode keymap string");
|
||||
|
||||
let keymap = xkb::Keymap::new_from_string(
|
||||
&context,
|
||||
keymap_str.clone(),
|
||||
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||
).expect("Failed to create keymap");
|
||||
|
||||
let state = xkb::State::new(&keymap);
|
||||
|
||||
// "Press" each button with keysyms
|
||||
for view in layout.views.values() {
|
||||
for row in &view.rows {
|
||||
for button in &row.buttons {
|
||||
let keystate = button.state.borrow();
|
||||
for keycode in &keystate.keycodes {
|
||||
match state.key_get_one_sym(*keycode) {
|
||||
xkb::KEY_NoSymbol => {
|
||||
eprintln!("{}", keymap_str);
|
||||
panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if handler.0 > 0 {
|
||||
panic!("Layout contains mistakes");
|
||||
}
|
||||
}
|
||||
21
src/util.rs
21
src/util.rs
@ -177,6 +177,27 @@ impl<T> Borrow<Rc<T>> for Pointer<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sugar for logging errors in results
|
||||
pub trait Warn {
|
||||
type Value;
|
||||
fn ok_warn(self, msg: &str) -> Option<Self::Value>;
|
||||
}
|
||||
|
||||
impl<T, E: std::error::Error> Warn for Result<T, E> {
|
||||
type Value = T;
|
||||
fn ok_warn(self, msg: &str) -> Option<T> {
|
||||
self.map_err(|e| {
|
||||
eprintln!("{}: {}", msg, e);
|
||||
e
|
||||
}).ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WarningHandler {
|
||||
/// Handle a warning
|
||||
fn handle(&mut self, warning: &str);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@ -49,12 +49,12 @@ endforeach
|
||||
# and the need to call it manually
|
||||
foreach layout : [
|
||||
'us', 'us_wide',
|
||||
'de',
|
||||
'de', 'de_wide',
|
||||
'el',
|
||||
'es',
|
||||
'fi',
|
||||
'it',
|
||||
'ja+kana',
|
||||
'jp+kana',
|
||||
'no',
|
||||
'number',
|
||||
'se',
|
||||
@ -62,7 +62,7 @@ foreach layout : [
|
||||
test(
|
||||
'test_layout_' + layout,
|
||||
cargo_script,
|
||||
args: ['', 'run', '--example', 'test_layout', layout]
|
||||
args: ['run', '--example', 'test_layout', layout]
|
||||
)
|
||||
endforeach
|
||||
|
||||
|
||||
@ -12,4 +12,4 @@ for DIR in ${DIRS}; do
|
||||
fi;
|
||||
done;
|
||||
|
||||
exec @bindir@/squeekboard-real
|
||||
exec $(which squeekboard)
|
||||
Reference in New Issue
Block a user