Compare commits

..

1 Commits

45 changed files with 613 additions and 1045 deletions

20
Cargo.lock generated
View File

@ -54,6 +54,15 @@ name = "cc"
version = "1.0.45" version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dbus"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"libdbus-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "0.4.4" version = "0.4.4"
@ -232,6 +241,14 @@ name = "libc"
version = "0.2.62" version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libdbus-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.2" version = "0.5.2"
@ -324,6 +341,7 @@ name = "rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"dbus 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -436,6 +454,7 @@ dependencies = [
"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"
"checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2" "checksum cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d25596627380be4381247dba06c69ad05ca21b3b065bd9827e416882ac41dcd2"
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum dbus 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819"
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9" "checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96" "checksum gdk 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc52c7244046df9d959df87289f1fc5cca23f9f850bab0c967963e2ecb83a96"
@ -451,6 +470,7 @@ dependencies = [
"checksum gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d487d333a4b87072e6bf9f2e55befa0ebef01b9496c2e263c0f4a1ff3d6c04b1" "checksum gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d487d333a4b87072e6bf9f2e55befa0ebef01b9496c2e263c0f4a1ff3d6c04b1"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum libdbus-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0"
"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.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"

View File

@ -4,7 +4,7 @@ version = "0.1.0"
[dependencies] [dependencies]
bitflags = "1.0.*" bitflags = "1.0.*"
clap = "2.32.*" dbus = "0.6.*"
maplit = "1.0.*" maplit = "1.0.*"
regex = "1.1.*" regex = "1.1.*"
serde = { version = "1.0.*", features = ["derive"] } serde = { version = "1.0.*", features = ["derive"] }

View File

@ -30,7 +30,7 @@ Most common testing is done in CI. Occasionally, and for each release, do perfor
- the application draws correctly - the application draws correctly
- it shows when relevant - it shows when relevant
- it changes layouts - it changes layouts
- it changes views - it changes levels
Testing with an application: Testing with an application:
@ -50,8 +50,10 @@ Testing layouts:
Layouts can be selected using the GNOME Settings application. Layouts can be selected using the GNOME Settings application.
``` ```
# define all available layouts. First one is currently selected. # define all available layouts
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]" $ 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
``` ```
Coding Coding
@ -112,7 +114,7 @@ Use the `cargo.sh` script for maintaining the Cargo part of the build. The scrip
``` ```
cd build_dir cd build_dir
sh /source_path/cargo.sh test sh /source_path/cargo.sh '' test
``` ```
### Cargo dependencies ### Cargo dependencies
@ -123,6 +125,6 @@ Dependencies must be specified in `Cargo.toml` with 2 numbers: "major.minor". Si
``` ```
cd build_dir cd build_dir
sh /source_path/cargo.sh update sh /source_path/cargo.sh '' update
ninja test ninja test
``` ```

View File

@ -11,7 +11,14 @@ SOURCE_DIR="$(dirname "$SCRIPT_PATH")"
CARGO_TARGET_DIR="$(pwd)" CARGO_TARGET_DIR="$(pwd)"
export CARGO_TARGET_DIR export CARGO_TARGET_DIR
if [ -n "${1}" ]; then
OUT_PATH="$(realpath "$1")"
fi
cd "$SOURCE_DIR" cd "$SOURCE_DIR"
shift
cargo "$@" cargo "$@"
if [ -n "${OUT_PATH}" ]; then
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
fi

View File

@ -1,34 +0,0 @@
#!/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

View File

@ -1,4 +1,5 @@
# Maintained by: Mark Müller <markmueller86@gmail.com> # German layout by Mark Müller
# Version 2019111700
--- ---
bounds: { x: 0, y: 1, width: 360, height: 208 } bounds: { x: 0, y: 1, width: 360, height: 208 }
@ -82,7 +83,7 @@ buttons:
space: space:
outline: "spaceline" outline: "spaceline"
label: " " label: " "
text: " " keysym: "space"
Return: Return:
outline: "altline" outline: "altline"
icon: "key-enter" icon: "key-enter"

View File

@ -1,88 +0,0 @@
# 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"

View File

@ -53,7 +53,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"
@ -87,112 +86,111 @@ buttons:
label: "αι" label: "αι"
period: period:
outline: "altline" outline: "altline"
text: "." label: "."
space: space:
outline: spaceline outline: spaceline
text: " " label: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
aring: aring:
text: "å" label: "å"
Aring: Aring:
text: "Å" label: "Å"
oslash: oslash:
text: "ø" label: "ø"
Oslash: Oslash:
text: "Ø" label: "Ø"
ae: ae:
text: "æ" label: "æ"
AE: AE:
text: "Æ" label: "Æ"
asterisk: asterisk:
text: "*" label: "*"
asciitilde: asciitilde:
text: "~" label: "~"
quoteleft: quoteleft:
text: "`" label: "`"
bar: bar:
text: "|" label: "|"
U00B7: U00B7:
text: "·" label: "·"
squareroot: squareroot:
text: "√" label: "√"
Greek_pi: Greek_pi:
text: "π" label: "π"
division: division:
text: "÷" label: "÷"
multiply: multiply:
text: "×" label: "×"
paragraph: paragraph:
text: "¶" label: "¶"
Greek_tau: Greek_tau:
text: "τ" label: "τ"
copyright: copyright:
text: "©" label: "©"
numbersign: numbersign:
text: "#" label: "#"
U00AE: U00AE:
text: "®" label: "®"
at: at:
text: "@" label: "@"
dollar: dollar:
text: "$" label: "$"
U00A3: U00A3:
text: "£" label: "£"
percent: percent:
text: "%" label: "%"
EuroSign: EuroSign:
text: "€" label: "€"
ampersand: ampersand:
text: "&" label: "&"
U00A5: U00A5:
text: "¥" label: "¥"
minus: minus:
text: "-" label: "-"
asciicircum: asciicircum:
text: "^" label: "^"
underscore: underscore:
text: "_" label: "_"
degree: degree:
text: "°" label: "°"
plus: plus:
text: "+" label: "+"
equal: equal:
text: "=" label: "="
parenleft: parenleft:
text: "(" label: "("
parenright: parenright:
text: ")" label: ")"
braceleft: braceleft:
text: "{" label: "{"
braceright: braceright:
text: "}" label: "}"
comma: comma:
text: "," label: ","
backslash: backslash:
text: "\\" label: "\\"
slash: slash:
text: "/" label: "/"
quotedbl: quotedbl:
text: "\"" label: "\""
quoteright: quoteright:
text: "'" label: "'"
less: less:
text: "<" label: "<"
greater: greater:
text: ">" label: ">"
colon: colon:
text: ":" label: ":"
semicolon: semicolon:
text: ";" label: ";"
exclam: exclam:
text: "!" label: "!"
question: question:
text: "?" label: "?"
bracketleft: bracketleft:
text: "[" label: "["
bracketright: bracketright:
text: "]" label: "]"

View File

@ -51,7 +51,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"
@ -81,14 +80,14 @@ buttons:
period: period:
outline: "default" outline: "default"
text: "." label: "."
space: space:
outline: "spaceline" outline: "spaceline"
text: " " label: " "
Return: Return:
outline: "altline" outline: "altline"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
text: ":" label: ":"
"\"":
keysym: "quotedbl"

View File

@ -46,7 +46,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"
@ -70,109 +69,108 @@ buttons:
outline: altline outline: altline
space: space:
outline: spaceline outline: spaceline
text: " " label: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
aring: aring:
text: "å" label: "å"
Aring: Aring:
text: "Å" label: "Å"
ouml: ouml:
text: "ö" label: "ö"
Ouml: Ouml:
text: "Ö" label: "Ö"
auml: auml:
text: "ä" label: "ä"
Auml: Auml:
text: "Ä" label: "Ä"
asterisk: asterisk:
text: "*" label: "*"
asciitilde: asciitilde:
text: "~" label: "~"
quoteleft: quoteleft:
text: "`" label: "`"
bar: bar:
text: "|" label: "|"
U00B7: U00B7:
text: "·" label: "·"
squareroot: squareroot:
text: "√" label: "√"
Greek_pi: Greek_pi:
text: "π" label: "π"
division: division:
text: "÷" label: "÷"
multiply: multiply:
text: "×" label: "×"
paragraph: paragraph:
text: "¶" label: "¶"
Greek_tau: Greek_tau:
text: "τ" label: "τ"
copyright: copyright:
text: "©" label: "©"
numbersign: numbersign:
text: "#" label: "#"
U00AE: U00AE:
text: "®" label: "®"
at: at:
text: "@" label: "@"
dollar: dollar:
text: "$" label: "$"
U00A3: U00A3:
text: "£" label: "£"
percent: percent:
text: "%" label: "%"
EuroSign: EuroSign:
text: "€" label: "€"
ampersand: ampersand:
text: "&" label: "&"
U00A5: U00A5:
text: "¥" label: "¥"
minus: minus:
text: "-" label: "-"
asciicircum: asciicircum:
text: "^" label: "^"
underscore: underscore:
text: "_" label: "_"
degree: degree:
text: "°" label: "°"
plus: plus:
text: "+" label: "+"
equal: equal:
text: "=" label: "="
parenleft: parenleft:
text: "(" label: "("
parenright: parenright:
text: ")" label: ")"
braceleft: braceleft:
text: "{" label: "{"
braceright: braceright:
text: "}" label: "}"
comma: comma:
text: "," label: ","
backslash: backslash:
text: "\\" label: "\\"
slash: slash:
text: "/" label: "/"
quotedbl: quotedbl:
text: "\"" label: "\""
quoteright: quoteright:
text: "'" label: "'"
less: less:
text: "<" label: "<"
greater: greater:
text: ">" label: ">"
colon: colon:
text: ":" label: ":"
semicolon: semicolon:
text: ";" label: ";"
exclam: exclam:
text: "!" label: "!"
question: question:
text: "?" label: "?"
bracketleft: bracketleft:
text: "[" label: "["
bracketright: bracketright:
text: "]" label: "]"

View File

@ -53,7 +53,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "default" outline: "default"
@ -92,7 +91,6 @@ buttons:
Return: Return:
outline: "altline" outline: "altline"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
label: ":" label: ":"
"\"": "\"":

View File

@ -1,4 +1,5 @@
# Maintained by: Mark Müller <markmueller86@gmail.com> # Japanese Kana layout by Mark Müller
# Version 2019111800
--- ---
bounds: { x: 0, y: 1, width: 360, height: 208 } bounds: { x: 0, y: 1, width: 360, height: 208 }
@ -220,11 +221,11 @@ buttons:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
# space button using text tag for ideographic space # space button with unicode keysym for ideographic space
space: space:
outline: "default-wide" outline: "default-wide"
label: "␣" label: "␣"
text: " " keysym: "U3000"
# switch to number view # switch to number view
numbers: numbers:
action: action:

View File

@ -26,13 +26,13 @@ views:
- "show_numbers preferences space . Return" - "show_numbers preferences space . Return"
numbers: numbers:
- "1 2 3 4 5 6 7 8 9 0" - "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )" - "at numbersign dollar percent ampersand minus underscore plus parenleft parenright"
- "show_symbols , \" ' : ; ! ? BackSpace" - "show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace"
- "show_letters preferences space . Return" - "show_letters preferences space . Return"
symbols: symbols:
- "~ ` | U00B7 squareroot Greek_pi Greek_tau division multiply paragraph" - "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree * { }" - "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
- "show_numbers \\ / < > = [ ] BackSpace" - "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
- "show_letters preferences space . Return" - "show_letters preferences space . Return"
buttons: buttons:
@ -46,7 +46,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"
@ -70,37 +69,108 @@ buttons:
outline: altline outline: altline
space: space:
outline: spaceline outline: spaceline
text: " " label: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return" aring:
label: "å"
Aring:
label: "Å"
oslash:
label: "ø"
Oslash:
label: "Ø"
ae:
label: "æ"
AE:
label: "Æ"
asterisk:
label: "*"
asciitilde:
label: "~"
quoteleft:
label: "`"
bar:
label: "|"
U00B7: U00B7:
text: "·" label: "·"
squareroot: squareroot:
text: "√" label: "√"
Greek_pi: Greek_pi:
text: "π" label: "π"
division: division:
text: "÷" label: "÷"
multiply: multiply:
text: "×" label: "×"
paragraph: paragraph:
text: "¶" label: "¶"
Greek_tau: Greek_tau:
text: "τ" label: "τ"
copyright: copyright:
text: "©" label: "©"
numbersign:
label: "#"
U00AE: U00AE:
text: "®" label: "®"
at:
label: "@"
dollar:
label: "$"
U00A3: U00A3:
text: "£" label: "£"
percent:
label: "%"
EuroSign: EuroSign:
text: "€" label: "€"
ampersand:
label: "&"
U00A5: U00A5:
text: "¥" label: "¥"
minus:
label: "-"
asciicircum: asciicircum:
text: "^" label: "^"
underscore:
label: "_"
degree: degree:
text: "°" 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: "]"

View File

@ -22,24 +22,22 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
space: space:
outline: spaceline outline: spaceline
text: " " label: " "
Return: Return:
outline: outline7 outline: outline7
icon: "key-enter" icon: "key-enter"
keysym: "BackSpace"
asterisk: asterisk:
text: "*" label: "*"
numbersign: numbersign:
text: "#" label: "#"
minus: minus:
text: "-" label: "-"
plus: plus:
text: "+" label: "+"
parenleft: parenleft:
text: "(" label: "("
parenright: parenright:
text: ")" label: ")"

View File

@ -46,7 +46,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "altline" outline: "altline"
@ -70,97 +69,96 @@ buttons:
outline: altline outline: altline
space: space:
outline: spaceline outline: spaceline
text: " " label: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
asterisk: asterisk:
text: "*" label: "*"
asciitilde: asciitilde:
text: "~" label: "~"
quoteleft: quoteleft:
text: "`" label: "`"
bar: bar:
text: "|" label: "|"
U00B7: U00B7:
text: "·" label: "·"
squareroot: squareroot:
text: "√" label: "√"
Greek_pi: Greek_pi:
text: "π" label: "π"
division: division:
text: "÷" label: "÷"
multiply: multiply:
text: "×" label: "×"
paragraph: paragraph:
text: "¶" label: "¶"
Greek_tau: Greek_tau:
text: "τ" label: "τ"
copyright: copyright:
text: "©" label: "©"
numbersign: numbersign:
text: "#" label: "#"
U00AE: U00AE:
text: "®" label: "®"
at: at:
text: "@" label: "@"
dollar: dollar:
text: "$" label: "$"
U00A3: U00A3:
text: "£" label: "£"
percent: percent:
text: "%" label: "%"
EuroSign: EuroSign:
text: "€" label: "€"
ampersand: ampersand:
text: "&" label: "&"
U00A5: U00A5:
text: "¥" label: "¥"
minus: minus:
text: "-" label: "-"
asciicircum: asciicircum:
text: "^" label: "^"
underscore: underscore:
text: "_" label: "_"
degree: degree:
text: "°" label: "°"
plus: plus:
text: "+" label: "+"
equal: equal:
text: "=" label: "="
parenleft: parenleft:
text: "(" label: "("
parenright: parenright:
text: ")" label: ")"
braceleft: braceleft:
text: "{" label: "{"
braceright: braceright:
text: "}" label: "}"
comma: comma:
text: "," label: ","
backslash: backslash:
text: "\\" label: "\\"
slash: slash:
text: "/" label: "/"
quotedbl: quotedbl:
text: "\"" label: "\""
quoteright: quoteright:
text: "'" label: "'"
less: less:
text: "<" label: "<"
greater: greater:
text: ">" label: ">"
colon: colon:
text: ":" label: ":"
semicolon: semicolon:
text: ";" label: ";"
exclam: exclam:
text: "!" label: "!"
question: question:
text: "?" label: "?"
bracketleft: bracketleft:
text: "[" label: "["
bracketright: bracketright:
text: "]" label: "]"

View File

@ -46,7 +46,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"
@ -73,13 +72,14 @@ buttons:
label: "*/=" label: "*/="
period: period:
outline: "special" outline: "special"
text: "." label: "."
space: space:
outline: "spaceline" outline: "spaceline"
text: " " label: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
text: ":" label: ":"
"\"":
keysym: "quotedbl"

View File

@ -18,22 +18,22 @@ views:
- "q w e r t y u i o p" - "q w e r t y u i o p"
- "a s d f g h j k l" - "a s d f g h j k l"
- "Shift_L z x c v b n m BackSpace" - "Shift_L z x c v b n m BackSpace"
- "show_numbers preferences space . Return" - "show_numbers preferences space period Return"
upper: upper:
- "Q W E R T Y U I O P" - "Q W E R T Y U I O P"
- "A S D F G H J K L" - "A S D F G H J K L"
- "Shift_L Z X C V B N M BackSpace" - "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space . Return" - "show_numbers preferences space period 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 . Return" - "show_letters preferences space period Return"
symbols: symbols:
- "~ ` | · √ π τ ÷ × ¶" - "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }" - "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace" - "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space . Return" - "show_letters preferences space period Return"
buttons: buttons:
Shift_L: Shift_L:
@ -46,7 +46,6 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"
@ -71,15 +70,16 @@ buttons:
set_view: "symbols" set_view: "symbols"
outline: "altline" outline: "altline"
label: "*/=" label: "*/="
".": period:
outline: "special" outline: "special"
text: "." label: "."
space: space:
outline: "spaceline" outline: "spaceline"
text: " " label: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
text: ":" label: ":"
"\"":
keysym: "quotedbl"

View File

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

View File

@ -2,7 +2,6 @@
<gresources> <gresources>
<gresource prefix="/sm/puri/squeekboard"> <gresource prefix="/sm/puri/squeekboard">
<file compressed="true">style.css</file> <file compressed="true">style.css</file>
<file compressed="true">style-Adwaita:dark.css</file>
<file compressed="true" preprocess="xml-stripblanks">popup.ui</file> <file compressed="true" preprocess="xml-stripblanks">popup.ui</file>
<file>icons/key-enter.svg</file> <file>icons/key-enter.svg</file>
<file>icons/key-shift.svg</file> <file>icons/key-shift.svg</file>

View File

@ -1,46 +0,0 @@
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;
}

View File

@ -1,15 +1,15 @@
sq_view { sq_view {
background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/ background-color: rgba(0, 0, 0, 255);
color: @theme_text_color; /*#ffffff;*/ color: #ffffff;
font-family: cantarell, sans-serif; font-family: cantarell, sans-serif;
} }
sq_view sq_button { sq_view sq_button {
color: @theme_fg_color; /*#deddda;*/ color: #deddda;
background: mix(@theme_bg_color, @theme_base_color, -0.5); /* #464448; */ background: #464448;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-color: @borders; /* #5e5c64;*/ border-color: #5e5c64;
border-radius: 3px; border-radius: 3px;
margin: 4px 2px 4px 2px; margin: 4px 2px 4px 2px;
} }
@ -18,32 +18,29 @@ sq_view.wide sq_button {
margin: 1px 1px 1px 1px; margin: 1px 1px 1px 1px;
} }
sq_button:active, sq_button:active {
sq_button.altline:active, background: #747077;
sq_button.special:active, border-color: #96949d;
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.altline,
sq_button.special, sq_button.special,
sq_button.wide { sq_button.wide {
background: mix(@theme_bg_color, @theme_base_color, 0.5); /*#2b292f;*/ background: #2b292f;
border-color: @borders; /* #3e3a44; */ border-color: #3e3a44;
} }
sq_button.locked { sq_button.locked {
background: @theme_fg_color; /*#ffffff;*/ background: #ffffff;
color: @theme_bg_color; /*#2b292f;*/ color: #2b292f;
} }
#Return { #Return {
background: @theme_selected_bg_color; /* #1c71d8; */ background: #1c71d8;
border-color: @borders; /*#1a5fb4;*/ border-color: #1a5fb4;
} }
#Return:active { #Return:active {
background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/ background: #1c71d8;
border-color: @borders; /*#3584e4;*/ border-color: #3584e4;
} }

20
debian/changelog vendored
View File

@ -1,23 +1,3 @@
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 squeekboard (1.3.0) amber-phone; urgency=medium
* Language selection popup * Language selection popup

12
debian/control vendored
View File

@ -12,7 +12,7 @@ Build-Depends:
libgtk-3-dev, libgtk-3-dev,
libcroco3-dev, libcroco3-dev,
librust-bitflags-1-dev (>= 1.0), librust-bitflags-1-dev (>= 1.0),
librust-clap-2+default-dev (>= 2.32), librust-dbus-0.6-dev (>= 0.6),
librust-gio+v2-44-dev, librust-gio+v2-44-dev,
librust-glib+v2-44-dev, librust-glib+v2-44-dev,
librust-glib-sys-dev, librust-glib-sys-dev,
@ -41,13 +41,3 @@ Depends:
${misc:Depends} ${misc:Depends}
Description: On-screen keyboard for Wayland Description: On-screen keyboard for Wayland
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone. Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
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

View File

@ -1 +0,0 @@
usr/bin/squeekboard-test-layout /usr/bin

View File

@ -1,2 +0,0 @@
tools/squeekboard-restyled usr/bin
usr/bin/squeekboard /usr/bin

View File

@ -1,2 +1,2 @@
# yaml-rust 0.4.3 shares some roots with libyaml, including the string which lintian checks, creating a false positive # 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: libyaml squeekboard binary: embedded-library usr/bin/squeekboard-real: libyaml

View File

@ -158,8 +158,6 @@ eek_gtk_keyboard_real_button_press_event (GtkWidget *self,
return TRUE; return TRUE;
} }
// TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render // TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render
static gboolean static gboolean
eek_gtk_keyboard_real_button_release_event (GtkWidget *self, eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
@ -172,18 +170,6 @@ eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
return TRUE; 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 static gboolean
eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self, eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self,
GdkEventMotion *event) GdkEventMotion *event)
@ -293,9 +279,6 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
eek_gtk_keyboard_real_button_release_event; eek_gtk_keyboard_real_button_release_event;
widget_class->motion_notify_event = widget_class->motion_notify_event =
eek_gtk_keyboard_real_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; widget_class->touch_event = handle_touch_event;
gobject_class->set_property = eek_gtk_keyboard_set_property; gobject_class->set_property = eek_gtk_keyboard_set_property;

View File

@ -26,7 +26,6 @@
#include "eek-keyboard.h" #include "eek-keyboard.h"
#include "eek-renderer.h" #include "eek-renderer.h"
#include "src/style.h"
enum { enum {
PROP_0, PROP_0,
@ -624,7 +623,10 @@ eek_renderer_init (EekRenderer *self)
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons"); gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
priv->css_provider = squeek_load_style(); /* 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");
} }
static void static void

View File

@ -207,8 +207,22 @@ static void
settings_get_layout(GSettings *settings, char **type, char **layout) settings_get_layout(GSettings *settings, char **type, char **layout)
{ {
GVariant *inputs = g_settings_get_value(settings, "sources"); GVariant *inputs = g_settings_get_value(settings, "sources");
// current layout is always first guint32 index;
g_variant_get_child(inputs, 0, "(ss)", type, layout); 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);
} }
void void

View File

@ -1,10 +1,52 @@
extern crate rs; extern crate rs;
extern crate xkbcommon;
use rs::tests::check_builtin_layout;
use std::env; use std::env;
fn main() -> () { use rs::data::{ Layout, LoadError };
check_builtin_layout(
env::args().nth(1).expect("No argument given").as_str() 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());
} }

View File

@ -1,7 +1,7 @@
project( project(
'squeekboard', 'squeekboard',
'c', 'rust', 'c', 'rust',
version: '1.4.0', version: '1.3.0',
license: 'GPLv3', license: 'GPLv3',
meson_version: '>=0.51.0', meson_version: '>=0.51.0',
default_options: [ default_options: [
@ -33,11 +33,6 @@ endif
if get_option('buildtype') != 'plain' if get_option('buildtype') != 'plain'
add_project_arguments('-fstack-protector-strong', language: 'c') add_project_arguments('-fstack-protector-strong', language: 'c')
endif endif
if get_option('buildtype') == 'release'
cargo_build_flags = ['--release'] # for artifacts
else
cargo_build_flags = []
endif
prefix = get_option('prefix') prefix = get_option('prefix')
datadir = join_paths(prefix, get_option('datadir')) datadir = join_paths(prefix, get_option('datadir'))
@ -59,9 +54,8 @@ summary = [
] ]
message('\n'.join(summary)) message('\n'.join(summary))
dep_cargo = find_program('cargo') cargo = find_program('cargo')
cargo_script = find_program('cargo.sh') cargo_script = find_program('cargo.sh')
cargo_build = find_program('cargo_build.sh')
subdir('data') subdir('data')
subdir('protocols') subdir('protocols')

View File

@ -1,16 +0,0 @@
#[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");
}
}

View File

@ -26,8 +26,8 @@ use ::xdg;
// traits, derives // traits, derives
use std::io::BufReader; use std::io::BufReader;
use std::iter::FromIterator; use std::iter::FromIterator;
use serde::Deserialize; use serde::Deserialize;
use util::WarningHandler;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
@ -151,30 +151,21 @@ fn list_layout_sources(
ret ret
} }
struct PrintWarnings;
impl WarningHandler for PrintWarnings {
fn handle(&mut self, warning: &str) {
println!("{}", warning);
}
}
fn load_layout_data(source: DataSource) fn load_layout_data(source: DataSource)
-> Result<::layout::LayoutData, LoadError> -> Result<::layout::LayoutData, LoadError>
{ {
let handler = PrintWarnings{};
match source { match source {
DataSource::File(path) => { DataSource::File(path) => {
Layout::from_file(path.clone()) Layout::from_file(path.clone())
.map_err(LoadError::BadData) .map_err(LoadError::BadData)
.and_then(|layout| .and_then(|layout|
layout.build(handler).0.map_err(LoadError::BadKeyMap) layout.build().map_err(LoadError::BadKeyMap)
) )
}, },
DataSource::Resource(name) => { DataSource::Resource(name) => {
Layout::from_resource(&name) Layout::from_resource(&name)
.and_then(|layout| .and_then(|layout|
layout.build(handler).0.map_err(LoadError::BadKeyMap) layout.build().map_err(LoadError::BadKeyMap)
) )
}, },
} }
@ -234,28 +225,22 @@ struct Bounds {
/// Buttons are embedded in a single string /// Buttons are embedded in a single string
type ButtonIds = String; type ButtonIds = String;
/// All info about a single button
/// Buttons can have multiple instances though.
#[derive(Debug, Default, Deserialize, PartialEq)] #[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
struct ButtonMeta { struct ButtonMeta {
/// Special action to perform on activation. Conflicts with keysym, text. /// Action other than keysym (conflicts with keysym)
action: Option<Action>, action: Option<Action>,
/// The name of the XKB keysym to emit on activation. /// The name of the outline. If not present, will be "default"
/// Conflicts with action, text outline: Option<String>,
/// FIXME: start using it
keysym: Option<String>, keysym: Option<String>,
/// The text to submit on activation. Will be derived from ID if not present /// If not present, will be derived from the button ID
/// Conflicts with action, keysym
text: Option<String>,
/// If not present, will be derived from text or the button ID
label: Option<String>, label: Option<String>,
/// Conflicts with label /// Conflicts with label
icon: Option<String>, icon: Option<String>,
/// The name of the outline. If not present, will be "default"
outline: Option<String>,
} }
#[derive(Debug, Deserialize, PartialEq, Clone)] #[derive(Debug, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
enum Action { enum Action {
#[serde(rename="locking")] #[serde(rename="locking")]
@ -311,7 +296,7 @@ impl Layout {
.map_err(LoadError::BadResource) .map_err(LoadError::BadResource)
} }
pub fn from_file(path: PathBuf) -> Result<Layout, Error> { fn from_file(path: PathBuf) -> Result<Layout, Error> {
let infile = BufReader::new( let infile = BufReader::new(
fs::OpenOptions::new() fs::OpenOptions::new()
.read(true) .read(true)
@ -320,8 +305,8 @@ impl Layout {
serde_yaml::from_reader(infile).map_err(Error::Yaml) serde_yaml::from_reader(infile).map_err(Error::Yaml)
} }
pub fn build<H: WarningHandler>(self, mut warning_handler: H) pub fn build(self)
-> (Result<::layout::LayoutData, FormattingError>, H) -> Result<::layout::LayoutData, FormattingError>
{ {
let button_names = self.views.values() let button_names = self.views.values()
.flat_map(|rows| { .flat_map(|rows| {
@ -338,8 +323,7 @@ impl Layout {
create_action( create_action(
&self.buttons, &self.buttons,
name, name,
self.views.keys().collect(), self.views.keys().collect()
&mut warning_handler,
) )
)}).collect(); )}).collect();
@ -384,15 +368,13 @@ impl Layout {
) )
}); });
let button_states = HashMap::<String, KeyState>::from_iter( let button_states
= HashMap::<String, KeyState>::from_iter(
button_states button_states
); );
// TODO: generate from symbols // TODO: generate from symbols
let keymap_str = match generate_keymap(&button_states) { let keymap_str = generate_keymap(&button_states)?;
Err(e) => { return (Err(e), warning_handler) },
Ok(v) => v,
};
let button_states_cache = hash_map_map( let button_states_cache = hash_map_map(
button_states, button_states,
@ -423,8 +405,7 @@ impl Layout {
name, name,
button_states_cache.get(name.into()) button_states_cache.get(name.into())
.expect("Button state not created") .expect("Button state not created")
.clone(), .clone()
&mut warning_handler,
)) ))
}).collect(), }).collect(),
}) })
@ -433,148 +414,116 @@ impl Layout {
)}) )})
); );
(
Ok(::layout::LayoutData { Ok(::layout::LayoutData {
views: views, views: views,
keymap_str: { keymap_str: {
CString::new(keymap_str) CString::new(keymap_str)
.expect("Invalid keymap string generated") .expect("Invalid keymap string generated")
}, },
}), })
warning_handler,
)
} }
} }
fn create_action<H: WarningHandler>( fn create_action(
button_info: &HashMap<String, ButtonMeta>, button_info: &HashMap<String, ButtonMeta>,
name: &str, name: &str,
view_names: Vec<&String>, view_names: Vec<&String>,
warning_handler: &mut H,
) -> ::action::Action { ) -> ::action::Action {
let default_meta = ButtonMeta::default(); let default_meta = ButtonMeta::default();
let symbol_meta = button_info.get(name) let symbol_meta = button_info.get(name)
.unwrap_or(&default_meta); .unwrap_or(&default_meta);
fn keysym_valid(name: &str) -> bool { fn filter_view_name(
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
}
enum SubmitData {
Action(Action),
Text(String),
Keysym(String),
};
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, button_name: &str,
view_name: String, view_name: String,
view_names: &Vec<&String>, view_names: &Vec<&String>
warning_handler: &mut H,
) -> String { ) -> String {
if view_names.contains(&&view_name) { if view_names.contains(&&view_name) {
view_name view_name
} else { } else {
warning_handler.handle(&format!("Button {} switches to missing view {}", eprintln!(
"Button {} switches to missing view {}",
button_name, button_name,
view_name, view_name
)); );
"base".into() "base".into()
} }
} }
match submission { fn keysym_valid(name: &str) -> bool {
SubmitData::Action( xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
Action::SetView(view_name) }
) => ::action::Action::SetLevel(
filter_view_name( let keysyms = match &symbol_meta.action {
name, view_name.clone(), &view_names, // Non-submit action
warning_handler, 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()
}
},
},
},
};
match &symbol_meta.action {
Some(Action::SetView(view_name)) => ::action::Action::SetLevel(
filter_view_name(name, view_name.clone(), &view_names)
), ),
SubmitData::Action(Action::Locking { Some(Action::Locking {
lock_view, unlock_view lock_view, unlock_view
}) => ::action::Action::LockLevel { }) => ::action::Action::LockLevel {
lock: filter_view_name( lock: filter_view_name(name, lock_view.clone(), &view_names),
name,
lock_view.clone(),
&view_names,
warning_handler,
),
unlock: filter_view_name( unlock: filter_view_name(
name, name,
unlock_view.clone(), unlock_view.clone(),
&view_names, &view_names
warning_handler,
), ),
}, },
SubmitData::Action( Some(Action::ShowPrefs) => ::action::Action::ShowPreferences,
Action::ShowPrefs None => ::action::Action::Submit {
) => ::action::Action::ShowPreferences,
SubmitData::Keysym(keysym) => ::action::Action::Submit {
text: None, text: None,
keys: vec!(::action::KeySym( keys: keysyms.into_iter().map(::action::KeySym).collect(),
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, /// TODO: Since this will receive user-provided data,
/// all .expect() on them should be turned into soft fails /// all .expect() on them should be turned into soft fails
fn create_button<H: WarningHandler>( fn create_button(
button_info: &HashMap<String, ButtonMeta>, button_info: &HashMap<String, ButtonMeta>,
outlines: &HashMap<String, Outline>, outlines: &HashMap<String, Outline>,
name: &str, name: &str,
state: Rc<RefCell<KeyState>>, state: Rc<RefCell<KeyState>>,
warning_handler: &mut H,
) -> ::layout::Button { ) -> ::layout::Button {
let cname = CString::new(name.clone()) let cname = CString::new(name.clone())
.expect("Bad name"); .expect("Bad name");
@ -590,18 +539,6 @@ fn create_button<H: WarningHandler>(
} else if let Some(icon) = &button_meta.icon { } else if let Some(icon) = &button_meta.icon {
::layout::Label::IconName(CString::new(icon.as_str()) ::layout::Label::IconName(CString::new(icon.as_str())
.expect("Bad icon")) .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 { } else {
::layout::Label::Text(cname.clone()) ::layout::Label::Text(cname.clone())
}; };
@ -611,7 +548,7 @@ fn create_button<H: WarningHandler>(
if outlines.contains_key(outline) { if outlines.contains_key(outline) {
outline.clone() outline.clone()
} else { } else {
warning_handler.handle(&format!("Outline named {} does not exist! Using default for button {}", outline, name)); eprintln!("Outline named {} does not exist! Using default for button {}", outline, name);
"default".into() "default".into()
} }
} }
@ -621,9 +558,7 @@ fn create_button<H: WarningHandler>(
let outline = outlines.get(&outline_name) let outline = outlines.get(&outline_name)
.map(|outline| (*outline).clone()) .map(|outline| (*outline).clone())
.unwrap_or_else(|| { .unwrap_or_else(|| {
warning_handler.handle( eprintln!("No default outline defied Using 1x1!");
&format!("No default outline defined! Using 1x1!")
);
Outline { Outline {
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 }, bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
} }
@ -650,14 +585,6 @@ mod tests {
use std::error::Error as ErrorTrait; use std::error::Error as ErrorTrait;
struct PanicWarn;
impl WarningHandler for PanicWarn {
fn handle(&mut self, warning: &str) {
panic!("{}", warning);
}
}
#[test] #[test]
fn test_parse_path() { fn test_parse_path() {
assert_eq!( assert_eq!(
@ -672,7 +599,6 @@ mod tests {
icon: None, icon: None,
keysym: None, keysym: None,
action: None, action: None,
text: None,
label: Some("test".into()), label: Some("test".into()),
outline: None, outline: None,
} }
@ -730,7 +656,7 @@ mod tests {
fn test_layout_punctuation() { fn test_layout_punctuation() {
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
.unwrap() .unwrap()
.build(PanicWarn).0 .build()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -745,7 +671,7 @@ mod tests {
fn test_layout_unicode() { fn test_layout_unicode() {
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
.unwrap() .unwrap()
.build(PanicWarn).0 .build()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -761,7 +687,7 @@ mod tests {
fn test_layout_unicode_multi() { fn test_layout_unicode_multi() {
let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml")) let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
.unwrap() .unwrap()
.build(PanicWarn).0 .build()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -776,7 +702,7 @@ mod tests {
#[test] #[test]
fn parsing_fallback() { fn parsing_fallback() {
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME) assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
.map(|layout| layout.build(PanicWarn).0.unwrap()) .and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
.is_ok() .is_ok()
); );
} }
@ -816,20 +742,18 @@ mod tests {
".".into() => ButtonMeta { ".".into() => ButtonMeta {
icon: None, icon: None,
keysym: None, keysym: None,
text: None,
action: None, action: None,
label: Some("test".into()), label: Some("test".into()),
outline: None, outline: None,
} }
}, },
".", ".",
Vec::new(), Vec::new()
&mut PanicWarn,
), ),
::action::Action::Submit { ::action::Action::Submit {
text: Some(CString::new(".").unwrap()), text: None,
keys: vec!(::action::KeySym("U002E".into())), keys: vec!(::action::KeySym("U002E".into())),
}, }
); );
} }
} }

View File

@ -51,26 +51,14 @@ pub struct KeyState {
pub action: Action, pub action: Action,
} }
/// Sorts an iterator by converting it to a Vector and back /// Generates a mapping where each key gets a keycode, starting from 8
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>>( pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C key_names: C
) -> HashMap<String, u32> { ) -> HashMap<String, u32> {
HashMap::from_iter( HashMap::from_iter(
// sort to remove a source of indeterminism in keycode assignment key_names.into_iter()
sorted(key_names.into_iter())
.map(|name| String::from(name)) .map(|name| String::from(name))
.zip(9..) .zip(8..)
) )
} }

View File

@ -1,5 +1,6 @@
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
extern crate dbus;
extern crate gio; extern crate gio;
extern crate glib; extern crate glib;
extern crate glib_sys; extern crate glib_sys;
@ -24,7 +25,5 @@ mod outputs;
mod popover; mod popover;
mod resources; mod resources;
mod submission; mod submission;
mod style; mod util;
pub mod tests;
pub mod util;
mod xdg; mod xdg;

View File

@ -1,8 +1,8 @@
/*! Locale detection and management. /*! Locale detection and management.
* Based on https://github.com/rust-locale/locale_config * Based on https://github.com/rust-locale/locale_config
* *
* Ready for deletion/replacement once Debian starts packaging this, * Modified for dbus querying.
* although this version doesn't need lazy_static. * This version doesn't need lazy_static.
* *
* Copyright (c) 20162019 Jan Hudec <bulb@ucw.cz> * Copyright (c) 20162019 Jan Hudec <bulb@ucw.cz>
Copyright (c) 2016 A.J. Gardner <aaron.j.gardner@gmail.com> Copyright (c) 2016 A.J. Gardner <aaron.j.gardner@gmail.com>
@ -11,10 +11,13 @@
Copyright (c) 2019, Sophie Tauchert <999eagle@999eagle.moe> Copyright (c) 2019, Sophie Tauchert <999eagle@999eagle.moe>
*/ */
use dbus::Connection;
use regex::Regex; use regex::Regex;
use std::borrow::Cow; use std::borrow::Cow;
use std::env; use std::env;
use dbus::stdintf::org_freedesktop_dbus::Properties;
/// Errors that may be returned by `locale_config`. /// Errors that may be returned by `locale_config`.
#[derive(Copy,Clone,Debug,PartialEq,Eq)] #[derive(Copy,Clone,Debug,PartialEq,Eq)]
pub enum Error { pub enum Error {
@ -463,19 +466,23 @@ fn tag(s: &str) -> Result<LanguageRange> {
// TODO: Read /etc/locale.alias // TODO: Read /etc/locale.alias
fn tag_inv(s: &str) -> LanguageRange { fn tag_inv(s: &str) -> LanguageRange {
tag(s).unwrap_or(LanguageRange::invariant()) tag(s).unwrap_or_else(|_e| LanguageRange::invariant())
} }
pub fn system_locale() -> Option<Locale> { /// Returns the locale using Unix roles and the given lookup method.
// TODO: maybe don't drop all errors on the floor like that
fn unix_locale<'a, 'b, F, E>(get: F) -> Locale
where F: Fn(&'a str) -> ::std::result::Result<String, E>
{
// LC_ALL overrides everything // LC_ALL overrides everything
if let Ok(all) = env::var("LC_ALL") { if let Ok(all) = get("LC_ALL") {
if let Ok(t) = tag(all.as_ref()) { if let Ok(t) = tag(all.as_ref()) {
return Some(Locale::from(t)); return Locale::from(t);
} }
} }
// LANG is default // LANG is default
let mut loc = let mut loc =
if let Ok(lang) = env::var("LANG") { if let Ok(lang) = get("LANG") {
Locale::from(tag_inv(lang.as_ref())) Locale::from(tag_inv(lang.as_ref()))
} else { } else {
Locale::invariant() Locale::invariant()
@ -494,7 +501,7 @@ pub fn system_locale() -> Option<Locale> {
("telephone", "LC_TELEPHONE"), ("telephone", "LC_TELEPHONE"),
("measurement", "LC_MEASUREMENT"), ("measurement", "LC_MEASUREMENT"),
].iter() { ].iter() {
if let Ok(val) = env::var(var) { if let Ok(val) = get(var) {
if let Ok(tag) = tag(val.as_ref()) if let Ok(tag) = tag(val.as_ref())
{ {
loc.add_category(cat, &tag); loc.add_category(cat, &tag);
@ -502,7 +509,7 @@ pub fn system_locale() -> Option<Locale> {
} }
} }
// LANGUAGE defines fallbacks // LANGUAGE defines fallbacks
if let Ok(langs) = env::var("LANGUAGE") { if let Ok(langs) = get("LANGUAGE") {
for i in langs.split(':') { for i in langs.split(':') {
if i != "" { if i != "" {
if let Ok(tag) = tag(i) { if let Ok(tag) = tag(i) {
@ -511,16 +518,44 @@ pub fn system_locale() -> Option<Locale> {
} }
} }
} }
if loc.as_ref() != "" {
return Some(loc); loc
} else { }
return None;
fn env_locale() -> Locale {
unix_locale(env::var)
}
fn dbus_locale()
-> std::result::Result<Locale, Box<dyn std::error::Error>>
{
let connection = Connection::get_private(dbus::BusType::Session)?;
let property = connection.with_path(
"org.freedesktop.locale1", "/org/freedesktop/locale1", 100
);
let locales: Vec<String>
= property.get("org.freedesktop.locale1", "Locale")?;
let val_pairs: Vec<(String, String)> = locales.into_iter().map(|s| {
let mut splits = s.splitn(2, "=");
(splits.next().unwrap().into(), splits.next().unwrap_or("").into())
}).collect();
Ok(unix_locale(|var| {
match val_pairs.iter().find(|(name, _val)| name == &var) {
Some((_name, val)) => Ok(val.clone()),
None => Err(()),
} }
}))
}
pub fn system_locale() -> Locale {
dbus_locale().unwrap_or_else(|_e| env_locale())
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::LanguageRange; use super::*;
#[test] #[test]
fn unix_tags() { fn unix_tags() {

View File

@ -39,6 +39,7 @@ cc = meson.get_compiler('c')
deps = [ deps = [
# dependency('glib-2.0', version: '>=2.26.0'), # dependency('glib-2.0', version: '>=2.26.0'),
dependency('dbus-1', version: '>=1.3'),
dependency('gio-2.0', version: '>=2.26.0'), dependency('gio-2.0', version: '>=2.26.0'),
dependency('gtk+-3.0', version: '>=3.0'), dependency('gtk+-3.0', version: '>=3.0'),
dependency('libcroco-0.6'), dependency('libcroco-0.6'),
@ -58,7 +59,7 @@ rslibs = custom_target(
output: ['librs.a'], output: ['librs.a'],
install: false, install: false,
console: true, console: true,
command: [cargo_build] + cargo_build_flags + ['@OUTPUT@', '--lib'] command: [cargo_script, '@OUTPUT@', 'build']
) )
build_rstests = custom_target( build_rstests = custom_target(
@ -72,14 +73,14 @@ build_rstests = custom_target(
output: ['src'], output: ['src'],
install: false, install: false,
console: true, console: true,
command: [cargo_script, 'test', '--no-run'], command: [cargo_script, '', 'test', '--no-run'],
depends: rslibs, # no point building tests if the code itself fails depends: rslibs, # no point building tests if the code itself fails
) )
test( test(
'rstest', 'rstest',
cargo_script, cargo_script,
args: ['test'], args: ['', 'test'],
# this is a whole Carg-based test suite, let it run for a while # this is a whole Carg-based test suite, let it run for a while
timeout: 900, timeout: 900,
depends: build_rstests, depends: build_rstests,
@ -97,7 +98,20 @@ libsqueekboard = static_library('libsqueekboard',
'-DEEK_COMPILATION=1'], '-DEEK_COMPILATION=1'],
) )
squeekboard = executable('squeekboard', # 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',
'server-main.c', 'server-main.c',
wl_proto_sources, wl_proto_sources,
squeekboard_resources, squeekboard_resources,
@ -111,17 +125,3 @@ squeekboard = executable('squeekboard',
'-DEEKBOARD_COMPILATION=1', '-DEEKBOARD_COMPILATION=1',
'-DEEK_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,
)

View File

@ -7,9 +7,9 @@ use ::locale::compare_current_locale;
use ::locale_config::system_locale; use ::locale_config::system_locale;
use ::resources; use ::resources;
use gio::ActionExt;
use gio::ActionMapExt; use gio::ActionMapExt;
use gio::SettingsExt; use gio::SettingsExt;
use gio::SimpleActionExt;
use glib::translate::FromGlibPtrNone; use glib::translate::FromGlibPtrNone;
use glib::variant::ToVariant; use glib::variant::ToVariant;
use gtk::PopoverExt; use gtk::PopoverExt;
@ -18,11 +18,8 @@ use std::io::Write;
mod variants { mod variants {
use glib; use glib;
use glib::Variant;
use glib_sys; use glib_sys;
use std::os::raw::c_char;
use glib::ToVariant;
use glib::translate::FromGlibPtrFull; use glib::translate::FromGlibPtrFull;
use glib::translate::ToGlibPtr; use glib::translate::ToGlibPtr;
@ -52,44 +49,6 @@ mod variants {
}) })
.collect() .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 { fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
@ -129,15 +88,12 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
fn set_layout(kind: String, name: String) { fn set_layout(kind: String, name: String) {
let settings = gio::Settings::new("org.gnome.desktop.input-sources"); let settings = gio::Settings::new("org.gnome.desktop.input-sources");
let inputs = settings.get_value("sources").unwrap(); let inputs = settings.get_value("sources").unwrap();
let current = (kind.clone(), name.clone()); let inputs = variants::get_tuples(inputs).into_iter();
let inputs = variants::get_tuples(inputs).into_iter() for (index, (ikind, iname)) in inputs.enumerate() {
.filter(|t| t != &current); if (&ikind, &iname) == (&kind, &name) {
let inputs = vec![(kind, name)].into_iter() settings.set_uint("current", index as u32);
.chain(inputs).collect(); }
settings.set_value( }
"sources",
&variants::ArrayPairString(inputs).to_variant()
);
settings.apply(); settings.apply();
} }
@ -147,20 +103,18 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
let settings = gio::Settings::new("org.gnome.desktop.input-sources"); let settings = gio::Settings::new("org.gnome.desktop.input-sources");
let inputs = settings.get_value("sources").unwrap(); let inputs = settings.get_value("sources").unwrap();
let current = settings.get_uint("current") as usize;
let inputs = variants::get_tuples(inputs); let inputs = variants::get_tuples(inputs);
let input_names: Vec<&str> = inputs.iter() let input_names: Vec<&str> = inputs.iter()
.map(|(_kind, name)| name.as_str()) .map(|(_kind, name)| name.as_str())
.collect(); .collect();
let translations = system_locale() let translations = resources::get_layout_names(system_locale()
.map(|locale| .tags_for("messages")
locale.tags_for("messages") .next().unwrap() // guaranteed to exist by locale_config
.next().unwrap() // guaranteed to exist
.as_ref() .as_ref()
.to_owned() );
)
.and_then(|lang| resources::get_layout_names(lang.as_str()));
// sorted collection of human and machine names // sorted collection of human and machine names
let mut human_names: Vec<(&str, &str)> = match translations { let mut human_names: Vec<(&str, &str)> = match translations {
@ -195,35 +149,39 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
height: position.width.floor() as i32, height: position.width.floor() as i32,
}); });
if let Some(current_name) = input_names.get(0) { let initial_state = input_names[current].to_variant();
let current_name = current_name.to_variant();
let layout_action = gio::SimpleAction::new_stateful( let layout_action = gio::SimpleAction::new_stateful(
"layout", "layout",
Some(current_name.type_()), Some(initial_state.type_()),
&current_name, &initial_state,
); );
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(); let action_group = gio::SimpleActionGroup::new();
action_group.add_action(&layout_action); 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.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(); menu.popup();
} }

View File

@ -13,7 +13,6 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
("us", include_str!("../data/keyboards/us.yaml")), ("us", include_str!("../data/keyboards/us.yaml")),
("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")),
("el", include_str!("../data/keyboards/el.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")),
@ -43,7 +42,6 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
("de-DE", include_str!("../data/langs/de-DE.txt")), ("de-DE", include_str!("../data/langs/de-DE.txt")),
("en-US", include_str!("../data/langs/en-US.txt")), ("en-US", include_str!("../data/langs/en-US.txt")),
("es-ES", include_str!("../data/langs/es-ES.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")), ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
]; ];

View File

@ -1,7 +0,0 @@
#ifndef __STYLE_H
#define __STYLE_H
#include "gtk/gtk.h"
GtkCssProvider *squeek_load_style();
#endif

View File

@ -1,124 +0,0 @@
/*
* 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: &gtk::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(),
}
)
}

View File

@ -1,78 +0,0 @@
/*! 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");
}
}

View File

@ -177,27 +177,6 @@ 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -49,7 +49,7 @@ endforeach
# and the need to call it manually # and the need to call it manually
foreach layout : [ foreach layout : [
'us', 'us_wide', 'us', 'us_wide',
'de', 'de_wide', 'de',
'el', 'el',
'es', 'es',
'fi', 'fi',
@ -62,7 +62,7 @@ foreach layout : [
test( test(
'test_layout_' + layout, 'test_layout_' + layout,
cargo_script, cargo_script,
args: ['run', '--example', 'test_layout', layout] args: ['', 'run', '--example', 'test_layout', layout]
) )
endforeach endforeach

View File

@ -12,4 +12,4 @@ for DIR in ${DIRS}; do
fi; fi;
done; done;
exec $(which squeekboard) exec @bindir@/squeekboard-real