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