Compare commits

..

53 Commits

Author SHA1 Message Date
6a164d8119 Release 1.4.0 "Nacelle"
Major changes:

- "text" property for layouts
- adjusting to user's color scheme
2019-12-02 19:40:14 +00:00
cdf263d984 Merge branch 'translation-and-minor-layout-fixes' into 'master'
translation: Japanese and minor layout fixes

See merge request Librem5/squeekboard!264
2019-11-30 12:22:45 +00:00
2ddfcfaff0 translation: Japanese and minor layout fixes 2019-11-30 12:22:45 +00:00
a901c85bcb Merge branch 'themes' into 'master'
Use appropriate styling for layouts

See merge request Librem5/squeekboard!253
2019-11-30 11:08:08 +00:00
fdbbe8f126 Merge branch 'settings' into 'master'
Implement the word-of-mouth layout selection

See merge request Librem5/squeekboard!260
2019-11-29 18:19:17 +00:00
f284627beb Merge branch 'release' into 'master'
Use Cargo release flag

See merge request Librem5/squeekboard!256
2019-11-29 15:33:00 +00:00
d45724c462 Merge branch 'leave' into 'master'
pointer: Release button when window is left

Closes #46

See merge request Librem5/squeekboard!262
2019-11-29 13:47:48 +00:00
93d0dcdc99 Merge branch 'text' into 'master'
Text property

Closes #153

See merge request Librem5/squeekboard!257
2019-11-28 09:19:30 +00:00
b252f7659b rust: Be compatible with older Rust 2019-11-27 16:52:50 +00:00
af6ad1fce6 buttons: Accept "text" and drop xkb keysym derivation 2019-11-27 16:52:50 +00:00
4ee8a91dfe build: Bring back squeekboard as a first class executable
With styles no longer being inconsistent, there's no need to override styles by default. The override script remains for PureOS packaging purposes.
2019-11-27 16:37:44 +00:00
6d5f793718 util: Include Result logger 2019-11-27 16:31:24 +00:00
59f6173282 theme: Use a matching layout theme for any widget theme
Dedicated styling is now possible for themes which have a corresponding style-theme.css file. Adwaita:dark gets one, whereas other themes use the new generic fallback theme.
2019-11-27 16:30:32 +00:00
3aec821f92 Merge branch 'errors' into 'master'
Better layout checking

Closes #131

See merge request Librem5/squeekboard!255
2019-11-27 16:22:08 +00:00
80ac591535 Merge branch 'deadkey' into 'master'
Bugfix release 1.3.2: work around sending keycode 0

See merge request Librem5/squeekboard!267
2019-11-26 15:55:10 +00:00
579ba8ab87 Release 1.3.2 2019-11-26 15:38:32 +00:00
5a262242a3 keymap: Work around sending keycode 0
If keycode 0 resolves to a letter, the the press is ignored by the compositor. This works around the bug.
2019-11-26 15:35:22 +00:00
034570bfa0 readme: Update language selection 2019-11-21 18:57:34 +00:00
1abca0a44e settings: Fetch current layout as the first item 2019-11-21 18:57:34 +00:00
23693521b7 popover: Don't change "current" field on language settings, change order
Also stops crashes when sources list is empty.
2019-11-21 18:57:09 +00:00
bb18e60754 Merge branch 'release1' into 'master'
Release 1.3.1

See merge request Librem5/squeekboard!265
2019-11-20 22:24:02 +00:00
35bc163107 Release 1.3.1
Fixing layout issues and building failures due to tests taking a long time.
2019-11-20 22:13:31 +00:00
0179507254 readme: Update cargo.sh usage 2019-11-20 14:32:42 +00:00
0c7e77a05f pointer: Release button when window is left 2019-11-20 13:17:47 +00:00
317d8a58dc Merge branch 'translations' into 'master'
translations: Updated pl and en with new layouts

See merge request Librem5/squeekboard!261
2019-11-20 12:26:39 +00:00
89b3ab9a81 translations: Updated pl and en with new layouts 2019-11-20 12:12:07 +00:00
8690808a29 layouts: Correct xkb symbols usage 2019-11-20 11:18:02 +00:00
be4ac32477 Merge branch '153-fix-German-layout-xkb-buttons-without-keycode-property' into 'master'
German layout: Correct xkb symbols usage

See merge request Librem5/squeekboard!251
2019-11-20 11:15:18 +00:00
5068f36d9d German layout: Correct xkb symbols usage 2019-11-20 11:15:18 +00:00
8c48e96f50 Merge branch 'codes' into 'master'
Language code fixes

See merge request Librem5/squeekboard!258
2019-11-20 11:12:47 +00:00
f15619287a Merge branch 'de' into 'master'
DE fixes

See merge request Librem5/squeekboard!259
2019-11-20 10:47:34 +00:00
eb5c28e77f translation: Use right codes in German 2019-11-20 10:25:56 +00:00
7fb34feaf8 translations: Use the correct code for Norwegian 2019-11-20 10:24:31 +00:00
20a6cf52ac layouts: Rename ja+kana to jp+kana 2019-11-20 10:23:05 +00:00
c566d8853e translations: Register Spanish 2019-11-20 10:21:41 +00:00
0beddc6856 Merge branch 'patch-8' into 'master'
translations: Spanish

See merge request Librem5/squeekboard!246
2019-11-20 10:11:48 +00:00
0adde1004f cargo: Use release mode for release builds 2019-11-19 13:50:36 +00:00
9b271a6919 devel: Package squeekboard-test-layout 2019-11-19 12:22:24 +00:00
1db561d33a build: Handle output files better 2019-11-19 12:15:08 +00:00
3170a0b615 Merge branch 'renderer' into 'master'
Renderer reworks

See merge request Librem5/squeekboard!238
2019-11-19 10:34:47 +00:00
9571adb107 tests: Executable for testing layouts 2019-11-19 09:47:32 +00:00
f834f174d8 cargo: Copy target with properties, find filename automatically 2019-11-19 09:35:32 +00:00
3c0b142c4f warnings: Print at runtime, crash at test time 2019-11-19 08:29:57 +00:00
4f28e3413a langs: add de-DE for switcher 2019-11-19 00:10:35 +01:00
291be9fa79 Update de-DE.txt 2019-11-18 22:58:27 +00:00
fd5d060eee langs: add de-DE for switcher 2019-11-18 22:56:47 +01:00
cffe07521e Merge branch 'prebuild_tests' into 'master'
Prebuild tests

See merge request Librem5/squeekboard!250
2019-11-17 15:14:50 +00:00
36af546362 tests: Bump timeout for the execution of all unit tests 2019-11-17 15:06:07 +00:00
0da848a5a0 tests: Use correct test compilation command 2019-11-17 14:56:25 +00:00
8c9ae98bda Merge branch 'release' into 'master'
Release 1.3.0

See merge request Librem5/squeekboard!247
2019-11-16 15:49:58 +00:00
ef025509ba translations: Spanish 2019-11-15 20:57:24 +00:00
c096698ec1 renderer: Remove unused ascii_font 2019-11-07 14:13:27 +00:00
3aaaed3bae renderer: Clean up EekColor & label 2019-11-07 14:10:20 +00:00
48 changed files with 1009 additions and 688 deletions

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 levels - it changes views
Testing with an application: Testing with an application:
@ -50,10 +50,8 @@ Testing layouts:
Layouts can be selected using the GNOME Settings application. Layouts can be selected using the GNOME Settings application.
``` ```
# define all available layouts # define all available layouts. First one is currently selected.
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]" $ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]"
# choose the active layout
$ gsettings set org.gnome.desktop.input-sources current 1
``` ```
Coding Coding
@ -114,7 +112,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
@ -125,6 +123,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,14 +11,7 @@ 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

34
cargo_build.sh Executable file
View 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

View File

@ -1,7 +1,6 @@
# German layout by Mark Müller # Maintained by: Mark Müller <markmueller86@gmail.com>
# Version 2019101900
--- ---
bounds: { x: 0, y: 1, width: 360, height: 210 } bounds: { x: 0, y: 1, width: 360, height: 208 }
outlines: outlines:
default: default:
@ -20,27 +19,27 @@ views:
- "q w e r t z u i o p" - "q w e r t z u i o p"
- "a s d f g h j k l" - "a s d f g h j k l"
- "Shift_L y x c v b n m BackSpace" - "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: upper:
- "Q W E R T Z U I O P" - "Q W E R T Z U I O P"
- "A S D F G H J K L" - "A S D F G H J K L"
- "Shift_L Y X C V B N M BackSpace" - "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: 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 , \" ' : = < > BackSpace"
- "show_letters show_dechars preferences space , period Return" - "show_letters show_eschars preferences space , . Return"
symbols: symbols:
- "~ ` ´ | · √ µ ÷ × ¶" - "~ ` ´ | · √ µ ÷ × ¶"
- "© ® £ $ ¥ ^ ° * { }" - "© ® £ $ ¥ ^ ° * { }"
- "show_numbers \\ / § π τ [ ] BackSpace" - "show_numbers \\ / § π τ [ ] BackSpace"
- "show_letters show_dechars preferences space , period Return" - "show_letters show_eschars preferences space , . Return"
dechars: eschars:
- "ä è é ö ü Ä È É Ö Ü" - "ä è é ö ü Ä È É Ö Ü"
- "à â ê î ô À Â È Î Ô" - "à â ê î ô À Â È Î Ô"
- "show_numbers « » ç Ç æ œ ß BackSpace" - "show_numbers « » ç Ç æ œ ß BackSpace"
- "show_letters show_dechars preferences space „ “ Return" - "show_letters show_eschars preferences space „ “ Return"
buttons: buttons:
Shift_L: Shift_L:
@ -53,6 +52,7 @@ 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"
@ -72,24 +72,18 @@ buttons:
set_view: "symbols" set_view: "symbols"
outline: "altline" outline: "altline"
label: "*/=" label: "*/="
show_dechars: show_eschars:
action: action:
locking: locking:
lock_view: "dechars" lock_view: "eschars"
unlock_view: "base" unlock_view: "base"
outline: "altline" outline: "altline"
label: "äÄ" label: "äÄ"
period:
outline: "default"
label: "."
space: space:
outline: "spaceline" outline: "spaceline"
label: " " label: " "
text: " "
Return: Return:
outline: "altline" outline: "altline"
icon: "key-enter" icon: "key-enter"
colon: keysym: "Return"
label: ":"
"\"":
keysym: "quotedbl"

View File

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

View File

@ -51,6 +51,7 @@ 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"
@ -80,14 +81,14 @@ buttons:
period: period:
outline: "default" outline: "default"
label: "." text: "."
space: space:
outline: "spaceline" outline: "spaceline"
label: " " text: " "
Return: Return:
outline: "altline" outline: "altline"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
label: ":" text: ":"
"\"":
keysym: "quotedbl"

View File

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

View File

@ -53,6 +53,7 @@ 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"
@ -91,6 +92,7 @@ buttons:
Return: Return:
outline: "altline" outline: "altline"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
label: ":" label: ":"
"\"": "\"":

View File

@ -1,7 +1,6 @@
# Japanese Kana layout by Mark Müller # Maintained by: Mark Müller <markmueller86@gmail.com>
# Version 2019101900
--- ---
bounds: { x: 0, y: 1, width: 360, height: 210 } bounds: { x: 0, y: 1, width: 360, height: 208 }
outlines: outlines:
default: default:
@ -199,29 +198,33 @@ views:
- "\\ ´ ` · ¶" - "\\ ´ ` · ¶"
buttons: buttons:
# following 4 buttons are keysyms from libxkbcommon # following 4 buttons use the corresponding xkb name as keysym
BackSpace: BackSpace:
outline: "wide" outline: "wide"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
Left: Left:
outline: "wide" outline: "wide"
label: "←" label: "←"
keysym: "Left"
Right: Right:
outline: "wide" outline: "wide"
label: "→" label: "→"
keysym: "Right"
# special button "preferences" is handled in the code # special button "preferences" is handled in the code
preferences: preferences:
action: "show_prefs" action: "show_prefs"
outline: "special" outline: "special"
icon: "keyboard-mode-symbolic" icon: "keyboard-mode-symbolic"
# space button (unicode) # space button using text tag for ideographic space
space: space:
outline: "default-wide" outline: "default-wide"
label: "␣" label: "␣"
keysym: "U3000" text: " "
# 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 comma quotedbl quoteright colon semicolon exclam question BackSpace" - "show_symbols , \" ' : ; ! ? BackSpace"
- "show_letters preferences space . Return" - "show_letters preferences space . Return"
symbols: symbols:
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph" - "~ ` | U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright" - "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree * { }"
- "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace" - "show_numbers \\ / < > = [ ] BackSpace"
- "show_letters preferences space . Return" - "show_letters preferences space . Return"
buttons: buttons:
@ -46,6 +46,7 @@ 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"
@ -69,108 +70,37 @@ buttons:
outline: altline outline: altline
space: space:
outline: spaceline outline: spaceline
label: " " text: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
aring: keysym: "Return"
label: "å"
Aring:
label: "Å"
oslash:
label: "ø"
Oslash:
label: "Ø"
ae:
label: "æ"
AE:
label: "Æ"
asterisk:
label: "*"
asciitilde:
label: "~"
quoteleft:
label: "`"
bar:
label: "|"
U00B7: U00B7:
label: "·" text: "·"
squareroot: squareroot:
label: "√" text: "√"
Greek_pi: Greek_pi:
label: "π" text: "π"
division: division:
label: "÷" text: "÷"
multiply: multiply:
label: "×" text: "×"
paragraph: paragraph:
label: "¶" text: "¶"
Greek_tau: Greek_tau:
label: "τ" text: "τ"
copyright: copyright:
label: "©" text: "©"
numbersign:
label: "#"
U00AE: U00AE:
label: "®" text: "®"
at:
label: "@"
dollar:
label: "$"
U00A3: U00A3:
label: "£" text: "£"
percent:
label: "%"
EuroSign: EuroSign:
label: "€" text: "€"
ampersand:
label: "&"
U00A5: U00A5:
label: "¥" text: "¥"
minus:
label: "-"
asciicircum: asciicircum:
label: "^" text: "^"
underscore:
label: "_"
degree: degree:
label: "°" text: "°"
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,22 +22,24 @@ buttons:
BackSpace: BackSpace:
outline: "altline" outline: "altline"
icon: "edit-clear-symbolic" icon: "edit-clear-symbolic"
keysym: "BackSpace"
space: space:
outline: spaceline outline: spaceline
label: " " text: " "
Return: Return:
outline: outline7 outline: outline7
icon: "key-enter" icon: "key-enter"
keysym: "BackSpace"
asterisk: asterisk:
label: "*" text: "*"
numbersign: numbersign:
label: "#" text: "#"
minus: minus:
label: "-" text: "-"
plus: plus:
label: "+" text: "+"
parenleft: parenleft:
label: "(" text: "("
parenright: parenright:
label: ")" text: ")"

View File

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

View File

@ -46,6 +46,7 @@ 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"
@ -72,14 +73,13 @@ buttons:
label: "*/=" label: "*/="
period: period:
outline: "special" outline: "special"
label: "." text: "."
space: space:
outline: "spaceline" outline: "spaceline"
label: " " text: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
label: ":" text: ":"
"\"":
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 period Return" - "show_numbers preferences space . 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 period 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"
- "@ # $ % & - _ + ( )" - "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace" - "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return" - "show_letters preferences space . Return"
symbols: symbols:
- "~ ` | · √ π τ ÷ × ¶" - "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }" - "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace" - "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
- "show_letters preferences space period Return" - "show_letters preferences space . Return"
buttons: buttons:
Shift_L: Shift_L:
@ -46,6 +46,7 @@ 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"
@ -70,16 +71,15 @@ buttons:
set_view: "symbols" set_view: "symbols"
outline: "altline" outline: "altline"
label: "*/=" label: "*/="
period: ".":
outline: "special" outline: "special"
label: "." text: "."
space: space:
outline: "spaceline" outline: "spaceline"
label: " " text: " "
Return: Return:
outline: "wide" outline: "wide"
icon: "key-enter" icon: "key-enter"
keysym: "Return"
colon: colon:
label: ":" text: ":"
"\"":
keysym: "quotedbl"

8
data/langs/de-DE.txt Normal file
View File

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

View File

@ -2,7 +2,9 @@ us English (US)
de German de German
el Greek el Greek
es Spanish es Spanish
fi Finnish
it Italian it Italian
jp+kana Japanese (kana) jp+kana Japanese (kana)
nb Norwegian no Norwegian
se Swedish

7
data/langs/es-ES.txt Normal file
View 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
View File

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

View File

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

View File

@ -2,6 +2,7 @@
<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

@ -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;
}

View File

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

20
debian/changelog vendored
View File

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

10
debian/control vendored
View File

@ -40,3 +40,13 @@ 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

1
debian/squeekboard-devel.install vendored Normal file
View File

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

2
debian/squeekboard.install vendored Normal file
View File

@ -0,0 +1,2 @@
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-real: libyaml squeekboard binary: embedded-library usr/bin/squeekboard: libyaml

View File

@ -158,6 +158,8 @@ 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,
@ -170,6 +172,18 @@ 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)
@ -279,6 +293,9 @@ 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,6 +26,7 @@
#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,
@ -50,8 +51,7 @@ typedef struct _EekRendererPrivate
gint origin_x; gint origin_x;
gint origin_y; gint origin_y;
PangoFontDescription *ascii_font; PangoFontDescription *font; // owned reference
PangoFontDescription *font;
cairo_surface_t *keyboard_surface; cairo_surface_t *keyboard_surface;
} EekRendererPrivate; } EekRendererPrivate;
@ -59,8 +59,7 @@ typedef struct _EekRendererPrivate
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT) G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
/* eek-keyboard-drawing.c */ /* eek-keyboard-drawing.c */
static void eek_renderer_real_render_button_label (EekRenderer *self, static void eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, GtkStyleContext *ctx,
PangoLayout *layout,
const struct squeek_button *button); const struct squeek_button *button);
static void invalidate (EekRenderer *renderer); static void invalidate (EekRenderer *renderer);
@ -125,9 +124,9 @@ static void
render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view) render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
{ {
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer); 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)); 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_translate (data.cr, bounds.x, bounds.y);
cairo_set_source_rgba (data.cr, cairo_set_source_rgba (data.cr,
foreground.red, color.red,
foreground.green, color.green,
foreground.blue, color.blue,
foreground.alpha); color.alpha);
/* draw rows */ /* draw rows */
squeek_view_foreach(level_keyboard_current(priv->keyboard), squeek_view_foreach(level_keyboard_current(priv->keyboard),
create_keyboard_surface_row_callback, create_keyboard_surface_row_callback,
&data); &data);
cairo_restore (data.cr); cairo_restore (data.cr);
cairo_destroy (data.cr); cairo_destroy (data.cr);
@ -198,9 +198,6 @@ static void render_button_in_context(EekRenderer *self,
struct button_place *place, struct button_place *place,
gboolean active) { gboolean active) {
cairo_surface_t *outline_surface = NULL; cairo_surface_t *outline_surface = NULL;
PangoLayout *layout;
PangoRectangle extents = { 0, };
EekColor foreground;
/* render outline */ /* render outline */
EekBounds bounds = squeek_button_get_bounds(place->button); 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_surface_destroy(outline_surface);
cairo_paint (cr); cairo_paint (cr);
eek_renderer_get_foreground_color (ctx, &foreground);
/* render icon (if any) */ /* render icon (if any) */
const char *icon_name = squeek_button_get_icon_name(place->button); 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_rectangle (cr, 0, 0, width, height);
cairo_clip (cr); cairo_clip (cr);
/* Draw the shape of the icon using the foreground color */ /* Draw the shape of the icon using the foreground color */
cairo_set_source_rgba (cr, foreground.red, GdkRGBA color = {0};
foreground.green, gtk_style_context_get_color (ctx, GTK_STATE_FLAG_NORMAL, &color);
foreground.blue,
foreground.alpha); cairo_set_source_rgba (cr, color.red,
color.green,
color.blue,
color.alpha);
cairo_mask_surface (cr, icon_surface, 0.0, 0.0); cairo_mask_surface (cr, icon_surface, 0.0, 0.0);
cairo_surface_destroy(icon_surface); cairo_surface_destroy(icon_surface);
cairo_fill (cr); cairo_fill (cr);
@ -260,25 +259,7 @@ static void render_button_in_context(EekRenderer *self,
return; return;
} }
} }
/* render label */ eek_renderer_render_button_label (self, cr, ctx, place->button);
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);
} }
static void static void
@ -363,9 +344,10 @@ eek_renderer_apply_transformation_for_button (cairo_t *cr,
} }
static void static void
eek_renderer_real_render_button_label (EekRenderer *self, eek_renderer_render_button_label (EekRenderer *self,
PangoLayout *layout, cairo_t *cr,
const struct squeek_button *button) GtkStyleContext *ctx,
const struct squeek_button *button)
{ {
EekRendererPrivate *priv = eek_renderer_get_instance_private (self); EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
@ -381,16 +363,11 @@ eek_renderer_real_render_button_label (EekRenderer *self,
if (!priv->font) { if (!priv->font) {
const PangoFontDescription *base_font; const PangoFontDescription *base_font;
gdouble ascii_size, size; gdouble size;
base_font = pango_context_get_font_description (priv->pcontext); base_font = pango_context_get_font_description (priv->pcontext);
// FIXME: Base font size on the same size unit used for button sizing, // 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 // 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; size = 30000.0;
priv->font = pango_font_description_copy (base_font); priv->font = pango_font_description_copy (base_font);
pango_font_description_set_size (priv->font, (gint)round(size * 0.6)); 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); font = pango_font_description_copy (priv->font);
pango_font_description_set_size (font, pango_font_description_set_size (font,
(gint)round(pango_font_description_get_size (font) * scale)); (gint)round(pango_font_description_get_size (font) * scale));
PangoLayout *layout = pango_cairo_create_layout (cr);
pango_layout_set_font_description (layout, font); pango_layout_set_font_description (layout, font);
pango_font_description_free (font); pango_font_description_free (font);
@ -413,6 +392,27 @@ eek_renderer_real_render_button_label (EekRenderer *self,
} }
pango_layout_set_width (layout, pango_layout_set_width (layout,
PANGO_SCALE * bounds.width * scale); 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->css_provider);
g_object_unref(priv->view_context); g_object_unref(priv->view_context);
g_object_unref(priv->button_context); g_object_unref(priv->button_context);
pango_font_description_free (priv->ascii_font);
pango_font_description_free (priv->font); pango_font_description_free (priv->font);
G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object); 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"); gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
/* Create a default CSS provider and load a style sheet */ priv->css_provider = squeek_load_style();
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
@ -850,22 +846,6 @@ eek_renderer_render_keyboard (EekRenderer *renderer,
EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr); 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 static gboolean
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3) sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
{ {

View File

@ -86,16 +86,6 @@ cairo_surface_t *eek_renderer_get_icon_surface(const gchar *icon_name,
void eek_renderer_render_keyboard (EekRenderer *renderer, void eek_renderer_render_keyboard (EekRenderer *renderer,
cairo_t *cr); 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, void eek_renderer_set_border_width (EekRenderer *renderer,
gdouble border_width); gdouble border_width);
void eek_renderer_apply_transformation_for_button void eek_renderer_apply_transformation_for_button

View File

@ -72,35 +72,3 @@ eek_bounds_free (EekBounds *bounds)
{ {
g_slice_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;
}

View File

@ -34,10 +34,8 @@ G_BEGIN_DECLS
#define EEK_TYPE_POINT (eek_point_get_type ()) #define EEK_TYPE_POINT (eek_point_get_type ())
#define EEK_TYPE_BOUNDS (eek_bounds_get_type ()) #define EEK_TYPE_BOUNDS (eek_bounds_get_type ())
#define EEK_TYPE_COLOR (eek_color_get_type ())
typedef struct _EekBounds EekBounds; typedef struct _EekBounds EekBounds;
typedef struct _EekColor EekColor;
typedef struct _EekboardContextService EekboardContextService; typedef struct _EekboardContextService EekboardContextService;
typedef struct _LevelKeyboard LevelKeyboard; typedef struct _LevelKeyboard LevelKeyboard;
@ -85,33 +83,6 @@ GType eek_bounds_get_type (void) G_GNUC_CONST;
EekBounds *eek_bounds_copy (const EekBounds *bounds); EekBounds *eek_bounds_copy (const EekBounds *bounds);
void eek_bounds_free (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 { struct transformation {
gdouble origin_x; gdouble origin_x;
gdouble origin_y; gdouble origin_y;

View File

@ -207,22 +207,8 @@ 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");
guint32 index; // current layout is always first
g_settings_get(settings, "current", "u", &index); g_variant_get_child(inputs, 0, "(ss)", type, layout);
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,52 +1,10 @@
extern crate rs; extern crate rs;
extern crate xkbcommon;
use rs::tests::check_builtin_layout;
use std::env; 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() -> () { 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()
);
} }

View File

@ -1,7 +1,7 @@
project( project(
'squeekboard', 'squeekboard',
'c', 'rust', 'c', 'rust',
version: '1.3.0', version: '1.4.0',
license: 'GPLv3', license: 'GPLv3',
meson_version: '>=0.51.0', meson_version: '>=0.51.0',
default_options: [ default_options: [
@ -33,6 +33,11 @@ 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'))
@ -54,8 +59,9 @@ summary = [
] ]
message('\n'.join(summary)) message('\n'.join(summary))
cargo = find_program('cargo') dep_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')

8
src/bin/test_layout.rs Normal file
View File

@ -0,0 +1,8 @@
extern crate rs;
use rs::tests::check_layout_file;
use std::env;
fn main() -> () {
check_layout_file(env::args().nth(1).expect("No argument given").as_str());
}

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,21 +151,30 @@ 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().map_err(LoadError::BadKeyMap) layout.build(handler).0.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().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 /// 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 {
/// Action other than keysym (conflicts with keysym) /// Special action to perform on activation. Conflicts with keysym, text.
action: Option<Action>, action: Option<Action>,
/// The name of the outline. If not present, will be "default" /// The name of the XKB keysym to emit on activation.
outline: Option<String>, /// Conflicts with action, text
/// FIXME: start using it
keysym: Option<String>, 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>, 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)] #[derive(Debug, Deserialize, PartialEq, Clone)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
enum Action { enum Action {
#[serde(rename="locking")] #[serde(rename="locking")]
@ -296,7 +311,7 @@ impl Layout {
.map_err(LoadError::BadResource) .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( let infile = BufReader::new(
fs::OpenOptions::new() fs::OpenOptions::new()
.read(true) .read(true)
@ -305,8 +320,8 @@ impl Layout {
serde_yaml::from_reader(infile).map_err(Error::Yaml) serde_yaml::from_reader(infile).map_err(Error::Yaml)
} }
pub fn build(self) pub fn build<H: WarningHandler>(self, mut warning_handler: H)
-> Result<::layout::LayoutData, FormattingError> -> (Result<::layout::LayoutData, FormattingError>, H)
{ {
let button_names = self.views.values() let button_names = self.views.values()
.flat_map(|rows| { .flat_map(|rows| {
@ -323,7 +338,8 @@ 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();
@ -368,13 +384,15 @@ impl Layout {
) )
}); });
let button_states let button_states = HashMap::<String, KeyState>::from_iter(
= HashMap::<String, KeyState>::from_iter( button_states
button_states );
);
// TODO: generate from symbols // 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( let button_states_cache = hash_map_map(
button_states, button_states,
@ -405,7 +423,8 @@ 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(),
}) })
@ -414,116 +433,148 @@ impl Layout {
)}) )})
); );
Ok(::layout::LayoutData { (
views: views, Ok(::layout::LayoutData {
keymap_str: { views: views,
CString::new(keymap_str) keymap_str: {
.expect("Invalid keymap string generated") CString::new(keymap_str)
}, .expect("Invalid keymap string generated")
}) },
}),
warning_handler,
)
} }
} }
fn create_action( fn create_action<H: WarningHandler>(
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 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 { fn keysym_valid(name: &str) -> bool {
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
} }
let keysyms = match &symbol_meta.action { enum SubmitData {
// Non-submit action Action(Action),
Some(_) => Vec::new(), Text(String),
// Submit action Keysym(String),
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 { let submission = match (
Some(Action::SetView(view_name)) => ::action::Action::SetLevel( &symbol_meta.action,
filter_view_name(name, view_name.clone(), &view_names) &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 lock_view, unlock_view
}) => ::action::Action::LockLevel { }) => ::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( unlock: filter_view_name(
name, name,
unlock_view.clone(), unlock_view.clone(),
&view_names &view_names,
warning_handler,
), ),
}, },
Some(Action::ShowPrefs) => ::action::Action::ShowPreferences, SubmitData::Action(
None => ::action::Action::Submit { Action::ShowPrefs
) => ::action::Action::ShowPreferences,
SubmitData::Keysym(keysym) => ::action::Action::Submit {
text: None, 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, /// 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( fn create_button<H: WarningHandler>(
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");
@ -539,6 +590,18 @@ fn create_button(
} 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())
}; };
@ -548,7 +611,7 @@ fn create_button(
if outlines.contains_key(outline) { if outlines.contains_key(outline) {
outline.clone() outline.clone()
} else { } 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() "default".into()
} }
} }
@ -558,7 +621,9 @@ fn create_button(
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(|| {
eprintln!("No default outline defied Using 1x1!"); warning_handler.handle(
&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 },
} }
@ -585,6 +650,14 @@ 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!(
@ -599,6 +672,7 @@ 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,
} }
@ -656,7 +730,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() .build(PanicWarn).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -671,7 +745,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() .build(PanicWarn).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -687,7 +761,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() .build(PanicWarn).0
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
out.views["base"] out.views["base"]
@ -702,7 +776,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)
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap)) .map(|layout| layout.build(PanicWarn).0.unwrap())
.is_ok() .is_ok()
); );
} }
@ -742,18 +816,20 @@ 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: None, text: Some(CString::new(".").unwrap()),
keys: vec!(::action::KeySym("U002E".into())), keys: vec!(::action::KeySym("U002E".into())),
} },
); );
} }
} }

View File

@ -51,14 +51,16 @@ pub struct KeyState {
pub action: Action, pub action: Action,
} }
/// Generates a mapping where each key gets a keycode, starting from 8 /// Generates a mapping where each key gets a keycode, starting from ~~8~~
/// HACK: starting from 9, because 8 results in keycode 0,
/// 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(
key_names.into_iter() key_names.into_iter()
.map(|name| String::from(name)) .map(|name| String::from(name))
.zip(8..) .zip(9..)
) )
} }

View File

@ -24,5 +24,7 @@ mod outputs;
mod popover; mod popover;
mod resources; mod resources;
mod submission; mod submission;
mod util; mod style;
pub mod tests;
pub mod util;
mod xdg; mod xdg;

View File

@ -58,7 +58,7 @@ rslibs = custom_target(
output: ['librs.a'], output: ['librs.a'],
install: false, install: false,
console: true, console: true,
command: [cargo_script, '@OUTPUT@', 'build'] command: [cargo_build] + cargo_build_flags + ['@OUTPUT@', '--lib']
) )
build_rstests = custom_target( build_rstests = custom_target(
@ -72,14 +72,16 @@ build_rstests = custom_target(
output: ['src'], output: ['src'],
install: false, install: false,
console: true, console: true,
command: [cargo_script, '', 'build', '--tests'], 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
timeout: 900,
depends: build_rstests, depends: build_rstests,
) )
@ -95,20 +97,7 @@ libsqueekboard = static_library('libsqueekboard',
'-DEEK_COMPILATION=1'], '-DEEK_COMPILATION=1'],
) )
# the straight binary needs to be demoted in favor of the wrapper script squeekboard = executable('squeekboard',
# 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,
@ -122,3 +111,17 @@ squeekboard = executable('squeekboard-real',
'-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

@ -18,8 +18,11 @@ 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;
@ -49,6 +52,44 @@ 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 {
@ -88,12 +129,15 @@ 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 inputs = variants::get_tuples(inputs).into_iter(); let current = (kind.clone(), name.clone());
for (index, (ikind, iname)) in inputs.enumerate() { let inputs = variants::get_tuples(inputs).into_iter()
if (&ikind, &iname) == (&kind, &name) { .filter(|t| t != &current);
settings.set_uint("current", index as u32); let inputs = vec![(kind, name)].into_iter()
} .chain(inputs).collect();
} settings.set_value(
"sources",
&variants::ArrayPairString(inputs).to_variant()
);
settings.apply(); 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 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()
@ -152,38 +195,44 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
height: position.width.floor() as i32, height: position.width.floor() as i32,
}); });
let initial_state = input_names[current].to_variant(); let action = input_names.get(0).map(|current_name| {
let current_name = current_name.to_variant();
let layout_action = gio::SimpleAction::new_stateful( let layout_action = gio::SimpleAction::new_stateful(
"layout", "layout",
Some(initial_state.type_()), Some(current_name.type_()),
&initial_state, &current_name,
); );
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));
layout_action
});
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| { menu.connect_closed(move |_menu| {
let state = match layout_action.get_state() { if let Some(layout_action) = &action {
Some(v) => { let state = match layout_action.get_state() {
let s = v.get::<String>().or_else(|| { Some(v) => {
eprintln!("Variant is not string: {:?}", 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 None
}); },
// FIXME: the `get_state` docs call for unrefing, };
// but the function is nowhere to be found set_layout("xkb".into(), state.unwrap_or("us".into()));
// 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

@ -17,7 +17,7 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
("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")),
("it", include_str!("../data/keyboards/it.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")), ("no", include_str!("../data/keyboards/no.yaml")),
("number", include_str!("../data/keyboards/number.yaml")), ("number", include_str!("../data/keyboards/number.yaml")),
("se", include_str!("../data/keyboards/se.yaml")), ("se", include_str!("../data/keyboards/se.yaml")),
@ -39,7 +39,10 @@ pub fn get_keyboard(needle: &str) -> Option<&'static str> {
/// Translations of the layout identifier strings /// Translations of the layout identifier strings
const LAYOUT_NAMES: &[(*const str, *const str)] = &[ 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")), ("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")), ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
]; ];

7
src/style.h Normal file
View 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
View 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: &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(),
}
)
}

78
src/tests.rs Normal file
View 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");
}
}

View File

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

View File

@ -54,7 +54,7 @@ foreach layout : [
'es', 'es',
'fi', 'fi',
'it', 'it',
'ja+kana', 'jp+kana',
'no', 'no',
'number', 'number',
'se', 'se',
@ -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 @bindir@/squeekboard-real exec $(which squeekboard)